1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package org.mozilla.thirdparty.com.google.android.exoplayer2.scheduler; 17 18 import android.content.Context; 19 import android.content.Intent; 20 import android.content.IntentFilter; 21 import android.net.ConnectivityManager; 22 import android.net.Network; 23 import android.net.NetworkCapabilities; 24 import android.net.NetworkInfo; 25 import android.os.BatteryManager; 26 import android.os.Parcel; 27 import android.os.Parcelable; 28 import android.os.PowerManager; 29 import androidx.annotation.IntDef; 30 import androidx.annotation.Nullable; 31 import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Assertions; 32 import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Util; 33 import java.lang.annotation.Documented; 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 37 /** Defines a set of device state requirements. */ 38 public final class Requirements implements Parcelable { 39 40 /** 41 * Requirement flags. Possible flag values are {@link #NETWORK}, {@link #NETWORK_UNMETERED}, 42 * {@link #DEVICE_IDLE} and {@link #DEVICE_CHARGING}. 43 */ 44 @Documented 45 @Retention(RetentionPolicy.SOURCE) 46 @IntDef( 47 flag = true, 48 value = {NETWORK, NETWORK_UNMETERED, DEVICE_IDLE, DEVICE_CHARGING}) 49 public @interface RequirementFlags {} 50 51 /** Requirement that the device has network connectivity. */ 52 public static final int NETWORK = 1; 53 /** Requirement that the device has a network connection that is unmetered. */ 54 public static final int NETWORK_UNMETERED = 1 << 1; 55 /** Requirement that the device is idle. */ 56 public static final int DEVICE_IDLE = 1 << 2; 57 /** Requirement that the device is charging. */ 58 public static final int DEVICE_CHARGING = 1 << 3; 59 60 @RequirementFlags private final int requirements; 61 62 /** @param requirements A combination of requirement flags. */ Requirements(@equirementFlags int requirements)63 public Requirements(@RequirementFlags int requirements) { 64 if ((requirements & NETWORK_UNMETERED) != 0) { 65 // Make sure network requirement flags are consistent. 66 requirements |= NETWORK; 67 } 68 this.requirements = requirements; 69 } 70 71 /** Returns the requirements. */ 72 @RequirementFlags getRequirements()73 public int getRequirements() { 74 return requirements; 75 } 76 77 /** Returns whether network connectivity is required. */ isNetworkRequired()78 public boolean isNetworkRequired() { 79 return (requirements & NETWORK) != 0; 80 } 81 82 /** Returns whether un-metered network connectivity is required. */ isUnmeteredNetworkRequired()83 public boolean isUnmeteredNetworkRequired() { 84 return (requirements & NETWORK_UNMETERED) != 0; 85 } 86 87 /** Returns whether the device is required to be charging. */ isChargingRequired()88 public boolean isChargingRequired() { 89 return (requirements & DEVICE_CHARGING) != 0; 90 } 91 92 /** Returns whether the device is required to be idle. */ isIdleRequired()93 public boolean isIdleRequired() { 94 return (requirements & DEVICE_IDLE) != 0; 95 } 96 97 /** 98 * Returns whether the requirements are met. 99 * 100 * @param context Any context. 101 * @return Whether the requirements are met. 102 */ checkRequirements(Context context)103 public boolean checkRequirements(Context context) { 104 return getNotMetRequirements(context) == 0; 105 } 106 107 /** 108 * Returns requirements that are not met, or 0. 109 * 110 * @param context Any context. 111 * @return The requirements that are not met, or 0. 112 */ 113 @RequirementFlags getNotMetRequirements(Context context)114 public int getNotMetRequirements(Context context) { 115 @RequirementFlags int notMetRequirements = getNotMetNetworkRequirements(context); 116 if (isChargingRequired() && !isDeviceCharging(context)) { 117 notMetRequirements |= DEVICE_CHARGING; 118 } 119 if (isIdleRequired() && !isDeviceIdle(context)) { 120 notMetRequirements |= DEVICE_IDLE; 121 } 122 return notMetRequirements; 123 } 124 125 @RequirementFlags getNotMetNetworkRequirements(Context context)126 private int getNotMetNetworkRequirements(Context context) { 127 if (!isNetworkRequired()) { 128 return 0; 129 } 130 131 ConnectivityManager connectivityManager = 132 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 133 NetworkInfo networkInfo = Assertions.checkNotNull(connectivityManager).getActiveNetworkInfo(); 134 if (networkInfo == null 135 || !networkInfo.isConnected() 136 || !isInternetConnectivityValidated(connectivityManager)) { 137 return requirements & (NETWORK | NETWORK_UNMETERED); 138 } 139 140 if (isUnmeteredNetworkRequired() && connectivityManager.isActiveNetworkMetered()) { 141 return NETWORK_UNMETERED; 142 } 143 144 return 0; 145 } 146 isDeviceCharging(Context context)147 private boolean isDeviceCharging(Context context) { 148 Intent batteryStatus = 149 context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 150 if (batteryStatus == null) { 151 return false; 152 } 153 int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1); 154 return status == BatteryManager.BATTERY_STATUS_CHARGING 155 || status == BatteryManager.BATTERY_STATUS_FULL; 156 } 157 isDeviceIdle(Context context)158 private boolean isDeviceIdle(Context context) { 159 PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 160 return Util.SDK_INT >= 23 161 ? powerManager.isDeviceIdleMode() 162 : Util.SDK_INT >= 20 ? !powerManager.isInteractive() : !powerManager.isScreenOn(); 163 } 164 isInternetConnectivityValidated(ConnectivityManager connectivityManager)165 private static boolean isInternetConnectivityValidated(ConnectivityManager connectivityManager) { 166 // It's possible to query NetworkCapabilities from API level 23, but RequirementsWatcher only 167 // fires an event to update its Requirements when NetworkCapabilities change from API level 24. 168 // Since Requirements won't be updated, we assume connectivity is validated on API level 23. 169 if (Util.SDK_INT < 24) { 170 return true; 171 } 172 Network activeNetwork = connectivityManager.getActiveNetwork(); 173 if (activeNetwork == null) { 174 return false; 175 } 176 NetworkCapabilities networkCapabilities = 177 connectivityManager.getNetworkCapabilities(activeNetwork); 178 return networkCapabilities != null 179 && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); 180 } 181 182 @Override equals(@ullable Object o)183 public boolean equals(@Nullable Object o) { 184 if (this == o) { 185 return true; 186 } 187 if (o == null || getClass() != o.getClass()) { 188 return false; 189 } 190 return requirements == ((Requirements) o).requirements; 191 } 192 193 @Override hashCode()194 public int hashCode() { 195 return requirements; 196 } 197 198 // Parcelable implementation. 199 200 @Override describeContents()201 public int describeContents() { 202 return 0; 203 } 204 205 @Override writeToParcel(Parcel dest, int flags)206 public void writeToParcel(Parcel dest, int flags) { 207 dest.writeInt(requirements); 208 } 209 210 public static final Parcelable.Creator<Requirements> CREATOR = 211 new Creator<Requirements>() { 212 213 @Override 214 public Requirements createFromParcel(Parcel in) { 215 return new Requirements(in.readInt()); 216 } 217 218 @Override 219 public Requirements[] newArray(int size) { 220 return new Requirements[size]; 221 } 222 }; 223 } 224