Files
app_Settings/src/com/android/settings/applications/appinfo/HibernationSwitchPreferenceController.java
Zhen Zhang 1f99d6a4b5 Update summary based on hibernated apps number
Update the summary of "unused apps" based on how many apps are
hibernated or auto revoked.

Test: HibernatedAppsPreferenceControllerTest
Bug: 181172051

Change-Id: I274eb3c0ada3d063937368232f31e52c20287552
2021-03-23 11:10:43 -07:00

146 lines
5.5 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.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. This method should only be called to initialize the
* controller.
* @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.getPackageUid(packageName, /* flags */ 0);
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);
}
}