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