• 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.car.settings.applications.appinfo;
18 
19 import static android.app.AppOpsManager.MODE_ALLOWED;
20 import static android.app.AppOpsManager.MODE_DEFAULT;
21 import static android.app.AppOpsManager.MODE_IGNORED;
22 import static android.app.AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED;
23 
24 import static com.android.car.settings.applications.ApplicationsUtils.isHibernationEnabled;
25 
26 import android.app.AppOpsManager;
27 import android.car.drivingstate.CarUxRestrictions;
28 import android.content.Context;
29 import android.content.pm.PackageManager;
30 import android.text.TextUtils;
31 import android.util.Slog;
32 
33 import androidx.preference.TwoStatePreference;
34 
35 import com.android.car.settings.common.FragmentController;
36 import com.android.car.settings.common.PreferenceController;
37 
38 /**
39  * A PreferenceController handling the logic for exempting hibernation of app
40  */
41 public final class HibernationSwitchPreferenceController
42         extends PreferenceController<TwoStatePreference>
43         implements AppOpsManager.OnOpChangedListener {
44     private static final String TAG = "HibernationSwitchPrefController";
45     private String mPackageName;
46     private final AppOpsManager mAppOpsManager;
47     private int mPackageUid;
48     private boolean mIsPackageSet;
49     private boolean mIsPackageExemptByDefault;
50 
HibernationSwitchPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)51     public HibernationSwitchPreferenceController(Context context, String preferenceKey,
52             FragmentController fragmentController,
53             CarUxRestrictions uxRestrictions) {
54         super(context, preferenceKey, fragmentController, uxRestrictions);
55         mAppOpsManager = context.getSystemService(AppOpsManager.class);
56     }
57 
58     @Override
getPreferenceType()59     protected Class<TwoStatePreference> getPreferenceType() {
60         return TwoStatePreference.class;
61     }
62 
63     @Override
getAvailabilityStatus()64     public int getAvailabilityStatus() {
65         return isHibernationEnabled() && mIsPackageSet ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
66     }
67 
68     @Override
onStartInternal()69     protected void onStartInternal() {
70         if (mIsPackageSet) {
71             mAppOpsManager.startWatchingMode(
72                     OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, mPackageName, this);
73         }
74     }
75 
76     @Override
onStopInternal()77     protected void onStopInternal() {
78         mAppOpsManager.stopWatchingMode(this);
79     }
80 
81     /**
82      * Set the package. And also retrieve details from package manager. Some packages may be
83      * exempted from hibernation by default. This method should only be called to initialize the
84      * controller.
85      * @param packageName The name of the package whose hibernation state to be managed.
86      */
setPackageName(String packageName)87     public void setPackageName(String packageName) {
88         mPackageName = packageName;
89         PackageManager packageManager = getContext().getPackageManager();
90 
91         // Q- packages exempt by default, except R- on Auto since Auto-Revoke was skipped in R
92         int maxTargetSdkVersionForExemptApps = android.os.Build.VERSION_CODES.R;
93         try {
94             mPackageUid = packageManager.getPackageUid(packageName, /* flags= */ 0);
95             mIsPackageExemptByDefault = packageManager.getTargetSdkVersion(packageName)
96                     <= maxTargetSdkVersionForExemptApps;
97             mIsPackageSet = true;
98         } catch (PackageManager.NameNotFoundException e) {
99             Slog.w(TAG, "Package [" + mPackageName + "] is not found!");
100             mIsPackageSet = false;
101         }
102     }
103 
104     @Override
updateState(TwoStatePreference preference)105     protected void updateState(TwoStatePreference preference) {
106         super.updateState(preference);
107         preference.setChecked(!isPackageHibernationExemptByUser());
108     }
109 
isPackageHibernationExemptByUser()110     private boolean isPackageHibernationExemptByUser() {
111         if (!mIsPackageSet) return true;
112         int mode = mAppOpsManager.unsafeCheckOpNoThrow(
113                 OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, mPackageUid, mPackageName);
114 
115         return mode == MODE_DEFAULT ? mIsPackageExemptByDefault : mode != MODE_ALLOWED;
116     }
117 
118     @Override
onOpChanged(String op, String packageName)119     public void onOpChanged(String op, String packageName) {
120         if (OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED.equals(op)
121                 && TextUtils.equals(mPackageName, packageName)) {
122             refreshUi();
123         }
124     }
125 
126     @Override
handlePreferenceChanged(TwoStatePreference preference, Object newValue)127     protected boolean handlePreferenceChanged(TwoStatePreference preference, Object newValue) {
128         try {
129             mAppOpsManager.setUidMode(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, mPackageUid,
130                     (boolean) newValue ? MODE_ALLOWED : MODE_IGNORED);
131         } catch (RuntimeException e) {
132             return false;
133         }
134         return true;
135     }
136 }
137