• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 
17 package com.android.settings.applications;
18 
19 import android.Manifest;
20 import android.app.AlarmManager;
21 import android.app.compat.CompatChanges;
22 import android.content.Context;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.os.PowerExemptionManager;
26 import android.os.UserHandle;
27 import android.util.Log;
28 
29 import com.android.internal.annotations.VisibleForTesting;
30 import com.android.internal.util.ArrayUtils;
31 import com.android.settingslib.applications.ApplicationsState;
32 import com.android.settingslib.applications.ApplicationsState.AppEntry;
33 import com.android.settingslib.applications.ApplicationsState.AppFilter;
34 
35 import java.util.List;
36 
37 /**
38  * Connects app op info to the ApplicationsState. Extends {@link AppStateAppOpsBridge} to tailor
39  * to the semantics of {@link Manifest.permission#SCHEDULE_EXACT_ALARM}.
40  * Also provides app filters that can use the info.
41  */
42 public class AppStateAlarmsAndRemindersBridge extends AppStateBaseBridge {
43     private static final String SEA_PERMISSION = Manifest.permission.SCHEDULE_EXACT_ALARM;
44     private static final String UEA_PERMISSION = Manifest.permission.USE_EXACT_ALARM;
45     private static final String TAG = "AlarmsAndRemindersBridge";
46 
47     @VisibleForTesting
48     AlarmManager mAlarmManager;
49     @VisibleForTesting
50     PowerExemptionManager mPowerExemptionManager;
51     @VisibleForTesting
52     PackageManager mPackageManager;
53 
AppStateAlarmsAndRemindersBridge(Context context, ApplicationsState appState, Callback callback)54     public AppStateAlarmsAndRemindersBridge(Context context, ApplicationsState appState,
55             Callback callback) {
56         super(appState, callback);
57 
58         mPowerExemptionManager = context.getSystemService(PowerExemptionManager.class);
59         mAlarmManager = context.getSystemService(AlarmManager.class);
60         mPackageManager = context.getPackageManager();
61     }
62 
isChangeEnabled(String packageName, int userId)63     private boolean isChangeEnabled(String packageName, int userId) {
64         return CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
65                 packageName, UserHandle.of(userId));
66     }
67 
isUeaChangeEnabled(String packageName, int userId)68     private boolean isUeaChangeEnabled(String packageName, int userId) {
69         return CompatChanges.isChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, packageName,
70                 UserHandle.of(userId));
71     }
72 
getRequestedPermissions(String packageName, int userId)73     private String[] getRequestedPermissions(String packageName, int userId) {
74         try {
75             final PackageInfo info = mPackageManager.getPackageInfoAsUser(packageName,
76                     PackageManager.GET_PERMISSIONS, userId);
77             return info.requestedPermissions;
78         } catch (PackageManager.NameNotFoundException e) {
79             Log.e(TAG, "Could not find package " + packageName, e);
80         }
81         return null;
82     }
83 
84     /**
85      * Returns information regarding {@link Manifest.permission#SCHEDULE_EXACT_ALARM} for the given
86      * package and uid.
87      */
createPermissionState(String packageName, int uid)88     public AlarmsAndRemindersState createPermissionState(String packageName, int uid) {
89         final int userId = UserHandle.getUserId(uid);
90 
91         final String[] requestedPermissions = getRequestedPermissions(packageName, userId);
92 
93         final boolean seaRequested = ArrayUtils.contains(requestedPermissions, SEA_PERMISSION)
94                 && isChangeEnabled(packageName, userId);
95         final boolean ueaRequested = ArrayUtils.contains(requestedPermissions, UEA_PERMISSION)
96                 && isUeaChangeEnabled(packageName, userId);
97 
98         final boolean seaGranted = mAlarmManager.hasScheduleExactAlarm(packageName, userId);
99         final boolean allowListed = mPowerExemptionManager.isAllowListed(packageName, true);
100 
101         return new AlarmsAndRemindersState(seaRequested, ueaRequested, seaGranted, allowListed);
102     }
103 
104     @Override
updateExtraInfo(AppEntry app, String pkg, int uid)105     protected void updateExtraInfo(AppEntry app, String pkg, int uid) {
106         app.extraInfo = createPermissionState(pkg, uid);
107     }
108 
109     @Override
loadAllExtraInfo()110     protected void loadAllExtraInfo() {
111         final List<AppEntry> allApps = mAppSession.getAllApps();
112         for (int i = 0; i < allApps.size(); i++) {
113             final AppEntry currentEntry = allApps.get(i);
114             updateExtraInfo(currentEntry, currentEntry.info.packageName, currentEntry.info.uid);
115         }
116     }
117 
118     public static final AppFilter FILTER_CLOCK_APPS = new AppFilter() {
119 
120         @Override
121         public void init() {
122         }
123 
124         @Override
125         public boolean filterApp(AppEntry info) {
126             if (info.extraInfo instanceof AlarmsAndRemindersState) {
127                 final AlarmsAndRemindersState state = (AlarmsAndRemindersState) info.extraInfo;
128                 return state.shouldBeVisible();
129             }
130             return false;
131         }
132     };
133 
134     /**
135      * Class to denote the state of an app regarding "Alarms and Reminders" permission.
136      * This permission state is a combination of {@link Manifest.permission#SCHEDULE_EXACT_ALARM},
137      * {@link Manifest.permission#USE_EXACT_ALARM} and the power allowlist state.
138      */
139     public static class AlarmsAndRemindersState {
140         private boolean mSeaPermissionRequested;
141         private boolean mUeaPermissionRequested;
142         private boolean mSeaPermissionGranted;
143         private boolean mAllowListed;
144 
AlarmsAndRemindersState(boolean seaPermissionRequested, boolean ueaPermissionRequested, boolean seaPermissionGranted, boolean allowListed)145         AlarmsAndRemindersState(boolean seaPermissionRequested, boolean ueaPermissionRequested,
146                 boolean seaPermissionGranted, boolean allowListed) {
147             mSeaPermissionRequested = seaPermissionRequested;
148             mUeaPermissionRequested = ueaPermissionRequested;
149             mSeaPermissionGranted = seaPermissionGranted;
150             mAllowListed = allowListed;
151         }
152 
153         /** Should the app associated with this state appear on the Settings screen */
shouldBeVisible()154         public boolean shouldBeVisible() {
155             return mSeaPermissionRequested && !mUeaPermissionRequested && !mAllowListed;
156         }
157 
158         /** Is the permission granted to the app associated with this state */
isAllowed()159         public boolean isAllowed() {
160             return mSeaPermissionGranted || mUeaPermissionRequested || mAllowListed;
161         }
162     }
163 }
164