Not showing apps that are targeting SDK < 31, because the change is not enabled for them. Now alarm manager service manages killing the process whenever the permission gets revoked, so we don't need to do it here. This also lets us kill the app on "Reset app preferences" if needed. Adding the preference under "Advanced" in the app info page so it appears for apps that have requested this permission. Test: atest SettingsUnitTests:AppStateAlarmsAndRemindersBridgeTest make -j64 RunSettingsRoboTests \ ROBOTEST_FILTER="AlarmsAndRemindersDetailsTest| AlarmsAndRemindersDetailPreferenceControllerTest" Manually: - There should be no observable difference in behavior when toggling the setting. ActivityManager logs should still indicate that the app is killed when the permission is revoked. - "Alarms & Reminders" should appear under "Advanced" when looking at the app info detail of an app that appears under "Alarms & reminders" special app access page. Bug: 179541791 Bug: 190070171 Change-Id: I2d437cec10ee10e4326fb25b2820de9ef9c31c67
139 lines
5.0 KiB
Java
139 lines
5.0 KiB
Java
/*
|
|
* Copyright (C) 2021 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.settings.applications;
|
|
|
|
import android.Manifest;
|
|
import android.app.AlarmManager;
|
|
import android.app.AppGlobals;
|
|
import android.app.compat.CompatChanges;
|
|
import android.content.Context;
|
|
import android.content.pm.IPackageManager;
|
|
import android.os.RemoteException;
|
|
import android.os.UserHandle;
|
|
import android.util.Log;
|
|
|
|
import com.android.internal.annotations.VisibleForTesting;
|
|
import com.android.internal.util.ArrayUtils;
|
|
import com.android.settingslib.applications.ApplicationsState;
|
|
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
|
import com.android.settingslib.applications.ApplicationsState.AppFilter;
|
|
|
|
import libcore.util.EmptyArray;
|
|
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Connects app op info to the ApplicationsState. Extends {@link AppStateAppOpsBridge} to tailor
|
|
* to the semantics of {@link Manifest.permission#SCHEDULE_EXACT_ALARM}.
|
|
* Also provides app filters that can use the info.
|
|
*/
|
|
public class AppStateAlarmsAndRemindersBridge extends AppStateBaseBridge {
|
|
private static final String PERMISSION = Manifest.permission.SCHEDULE_EXACT_ALARM;
|
|
private static final String TAG = "AlarmsAndRemindersBridge";
|
|
|
|
@VisibleForTesting
|
|
AlarmManager mAlarmManager;
|
|
@VisibleForTesting
|
|
String[] mRequesterPackages;
|
|
|
|
public AppStateAlarmsAndRemindersBridge(Context context, ApplicationsState appState,
|
|
Callback callback) {
|
|
super(appState, callback);
|
|
|
|
mAlarmManager = context.getSystemService(AlarmManager.class);
|
|
final IPackageManager iPm = AppGlobals.getPackageManager();
|
|
try {
|
|
mRequesterPackages = iPm.getAppOpPermissionPackages(PERMISSION);
|
|
} catch (RemoteException re) {
|
|
Log.e(TAG, "Cannot reach package manager", re);
|
|
mRequesterPackages = EmptyArray.STRING;
|
|
}
|
|
}
|
|
|
|
private boolean isChangeEnabled(String packageName, int userId) {
|
|
return CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
|
|
packageName, UserHandle.of(userId));
|
|
}
|
|
|
|
/**
|
|
* Returns information regarding {@link Manifest.permission#SCHEDULE_EXACT_ALARM} for the given
|
|
* package and uid.
|
|
*/
|
|
public AlarmsAndRemindersState createPermissionState(String packageName, int uid) {
|
|
final int userId = UserHandle.getUserId(uid);
|
|
|
|
final boolean permissionRequested = ArrayUtils.contains(mRequesterPackages, packageName)
|
|
&& isChangeEnabled(packageName, userId);
|
|
final boolean permissionGranted = mAlarmManager.hasScheduleExactAlarm(packageName, userId);
|
|
return new AlarmsAndRemindersState(permissionRequested, permissionGranted);
|
|
}
|
|
|
|
@Override
|
|
protected void updateExtraInfo(AppEntry app, String pkg, int uid) {
|
|
app.extraInfo = createPermissionState(pkg, uid);
|
|
}
|
|
|
|
@Override
|
|
protected void loadAllExtraInfo() {
|
|
final List<AppEntry> allApps = mAppSession.getAllApps();
|
|
for (int i = 0; i < allApps.size(); i++) {
|
|
final AppEntry currentEntry = allApps.get(i);
|
|
updateExtraInfo(currentEntry, currentEntry.info.packageName, currentEntry.info.uid);
|
|
}
|
|
}
|
|
|
|
public static final AppFilter FILTER_CLOCK_APPS = new AppFilter() {
|
|
|
|
@Override
|
|
public void init() {
|
|
}
|
|
|
|
@Override
|
|
public boolean filterApp(AppEntry info) {
|
|
if (info.extraInfo instanceof AlarmsAndRemindersState) {
|
|
final AlarmsAndRemindersState state = (AlarmsAndRemindersState) info.extraInfo;
|
|
return state.shouldBeVisible();
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Class to denote the state of an app regarding
|
|
* {@link Manifest.permission#SCHEDULE_EXACT_ALARM}.
|
|
*/
|
|
public static class AlarmsAndRemindersState {
|
|
private boolean mPermissionRequested;
|
|
private boolean mPermissionGranted;
|
|
|
|
AlarmsAndRemindersState(boolean permissionRequested, boolean permissionGranted) {
|
|
mPermissionRequested = permissionRequested;
|
|
mPermissionGranted = permissionGranted;
|
|
}
|
|
|
|
/** Should the app associated with this state appear on the Settings screen */
|
|
public boolean shouldBeVisible() {
|
|
return mPermissionRequested;
|
|
}
|
|
|
|
/** Is the permission granted to the app associated with this state */
|
|
public boolean isAllowed() {
|
|
return mPermissionGranted;
|
|
}
|
|
}
|
|
}
|