Update settings for app hibernation
Created an preference in Apps & Notifcations page as the entry point for apps that are hibernated. Also added a switch preference in AppInfo page for users to exempt an app from hibernation. Bug: 181172051 Test: HibernatedAppsPreferenceControllerTest; HibernationSwitchPreferenceControllerTest; AppInfoDashboardFragmentTest Change-Id: I72a90ab391cd521150fc155a6d9c67c846b7360d
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.appinfo;
|
||||
|
||||
import static android.app.AppOpsManager.MODE_ALLOWED;
|
||||
import static android.app.AppOpsManager.MODE_DEFAULT;
|
||||
import static android.app.AppOpsManager.MODE_IGNORED;
|
||||
import static android.app.AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED;
|
||||
import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
|
||||
|
||||
import static com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Slog;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.LifecycleObserver;
|
||||
import androidx.lifecycle.OnLifecycleEvent;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* A PreferenceController handling the logic for exempting hibernation of app
|
||||
*/
|
||||
public final class HibernationSwitchPreferenceController extends AppInfoPreferenceControllerBase
|
||||
implements LifecycleObserver, AppOpsManager.OnOpChangedListener,
|
||||
Preference.OnPreferenceChangeListener {
|
||||
private static final String TAG = "HibernationSwitchPrefController";
|
||||
private String mPackageName;
|
||||
private final AppOpsManager mAppOpsManager;
|
||||
private int mPackageUid;
|
||||
@VisibleForTesting
|
||||
boolean mIsPackageSet;
|
||||
private boolean mIsPackageExemptByDefault;
|
||||
|
||||
public HibernationSwitchPreferenceController(Context context,
|
||||
String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mAppOpsManager = context.getSystemService(AppOpsManager.class);
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||
public void onResume() {
|
||||
if (mIsPackageSet) {
|
||||
mAppOpsManager.startWatchingMode(
|
||||
OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, mPackageName, this);
|
||||
}
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
|
||||
public void onPause() {
|
||||
mAppOpsManager.stopWatchingMode(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return isHibernationEnabled() && mIsPackageSet ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the package. And also retrieve details from package manager. Some packages may be
|
||||
* exempted from hibernation by default.
|
||||
* @param packageName The name of the package whose hibernation state to be managed.
|
||||
*/
|
||||
void setPackage(@NonNull String packageName) {
|
||||
mPackageName = packageName;
|
||||
final PackageManager packageManager = mContext.getPackageManager();
|
||||
|
||||
// Q- packages exempt by default, except R- on Auto since Auto-Revoke was skipped in R
|
||||
final int maxTargetSdkVersionForExemptApps =
|
||||
packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
|
||||
? android.os.Build.VERSION_CODES.R
|
||||
: android.os.Build.VERSION_CODES.Q;
|
||||
try {
|
||||
mPackageUid = packageManager.getPackageUidAsUser(
|
||||
packageName, mContext.getUserId());
|
||||
mIsPackageExemptByDefault = packageManager.getTargetSdkVersion(packageName)
|
||||
<= maxTargetSdkVersionForExemptApps;
|
||||
mIsPackageSet = true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Slog.w(TAG, "Package [" + mPackageName + "] is not found!");
|
||||
mIsPackageSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
((SwitchPreference) preference).setChecked(!isPackageHibernationExemptByUser());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean isPackageHibernationExemptByUser() {
|
||||
if (!mIsPackageSet) return true;
|
||||
final int mode = mAppOpsManager.unsafeCheckOpNoThrow(
|
||||
OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, mPackageUid, mPackageName);
|
||||
|
||||
return mode == MODE_DEFAULT ? mIsPackageExemptByDefault : mode != MODE_ALLOWED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpChanged(String op, String packageName) {
|
||||
if (OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED.equals(op)
|
||||
&& TextUtils.equals(mPackageName, packageName)) {
|
||||
updateState(mPreference);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object isChecked) {
|
||||
try {
|
||||
mAppOpsManager.setUidMode(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, mPackageUid,
|
||||
(boolean) isChecked ? MODE_ALLOWED : MODE_IGNORED);
|
||||
} catch (RuntimeException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isHibernationEnabled() {
|
||||
return DeviceConfig.getBoolean(
|
||||
NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED, false);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user