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;
18 
19 import android.car.Car;
20 import android.car.drivingstate.CarUxRestrictions;
21 import android.car.watchdog.CarWatchdogManager;
22 import android.car.watchdog.PackageKillableState;
23 import android.content.Context;
24 import android.content.pm.PackageInfo;
25 import android.os.UserHandle;
26 
27 import androidx.annotation.VisibleForTesting;
28 import androidx.preference.TwoStatePreference;
29 
30 import com.android.car.settings.R;
31 import com.android.car.settings.common.ConfirmationDialogFragment;
32 import com.android.car.settings.common.FragmentController;
33 import com.android.car.settings.common.Logger;
34 import com.android.car.settings.common.PreferenceController;
35 import com.android.internal.annotations.GuardedBy;
36 
37 import java.util.Objects;
38 
39 /** Controller for preference which turns on / off prioritize app performance setting. */
40 public class PrioritizeAppPerformancePreferenceController
41         extends PreferenceController<TwoStatePreference> {
42     private static final Logger LOG =
43             new Logger(PrioritizeAppPerformancePreferenceController.class);
44 
45     @VisibleForTesting
46     static final String TURN_ON_PRIORITIZE_APP_PERFORMANCE_DIALOG_TAG =
47             "com.android.car.settings.applications.TurnOnPrioritizeAppPerformanceDialogTag";
48 
49     private final Object mLock = new Object();
50     @GuardedBy("mLock")
51     private CarWatchdogManager mCarWatchdogManager;
52 
53     private Car mCar;
54     private String mPackageName;
55     private UserHandle mUserHandle;
56 
57     private final ConfirmationDialogFragment.ConfirmListener mConfirmListener = arguments -> {
58         setKillableState(false);
59         getPreference().setChecked(true);
60     };
61 
PrioritizeAppPerformancePreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)62     public PrioritizeAppPerformancePreferenceController(Context context, String preferenceKey,
63             FragmentController fragmentController,
64             CarUxRestrictions uxRestrictions) {
65         super(context, preferenceKey, fragmentController, uxRestrictions);
66     }
67 
68     @Override
onCreateInternal()69     protected void onCreateInternal() {
70         if (mCar != null && mCar.isConnected()) {
71             mCar.disconnect();
72             mCar = null;
73         }
74         mCar = Car.createCar(getContext(), null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
75                 (car, isReady) -> {
76                     synchronized (mLock) {
77                         mCarWatchdogManager = isReady
78                                 ? (CarWatchdogManager) car.getCarManager(Car.CAR_WATCHDOG_SERVICE)
79                                 : null;
80                     }
81                 });
82 
83         ConfirmationDialogFragment dialogFragment =
84                 (ConfirmationDialogFragment) getFragmentController().findDialogByTag(
85                         TURN_ON_PRIORITIZE_APP_PERFORMANCE_DIALOG_TAG);
86         ConfirmationDialogFragment.resetListeners(
87                 dialogFragment, mConfirmListener, /* rejectListener= */ null,
88                 /* neutralListener= */ null);
89     }
90 
91     @Override
onDestroyInternal()92     protected void onDestroyInternal() {
93         if (mCar != null) {
94             mCar.disconnect();
95             mCar = null;
96         }
97     }
98 
99     /**
100      * Set the package info of the application.
101      */
setPackageInfo(PackageInfo packageInfo)102     public void setPackageInfo(PackageInfo packageInfo) {
103         mPackageName = packageInfo.packageName;
104         mUserHandle = UserHandle.getUserHandleForUid(packageInfo.applicationInfo.uid);
105     }
106 
107     @Override
getPreferenceType()108     protected Class<TwoStatePreference> getPreferenceType() {
109         return TwoStatePreference.class;
110     }
111 
112     @Override
updateState(TwoStatePreference preference)113     protected void updateState(TwoStatePreference preference) {
114         int killableState = getKillableState();
115         preference.setChecked(killableState == PackageKillableState.KILLABLE_STATE_NO);
116         preference.setEnabled(killableState != PackageKillableState.KILLABLE_STATE_NEVER);
117     }
118 
119     @Override
handlePreferenceChanged(TwoStatePreference preference, Object newValue)120     protected boolean handlePreferenceChanged(TwoStatePreference preference, Object newValue) {
121         boolean isToggledOn = (boolean) newValue;
122         if (isToggledOn) {
123             showConfirmationDialog();
124             return false;
125         }
126         setKillableState(true);
127         return true;
128     }
129 
getKillableState()130     private int getKillableState() {
131         synchronized (mLock) {
132             return Objects.requireNonNull(mCarWatchdogManager)
133                     .getPackageKillableStatesAsUser(mUserHandle).stream()
134                     .filter(pks -> pks.getPackageName().equals(mPackageName))
135                     .findFirst().map(PackageKillableState::getKillableState).orElse(-1);
136         }
137     }
138 
setKillableState(boolean isKillable)139     private void setKillableState(boolean isKillable) {
140         synchronized (mLock) {
141             mCarWatchdogManager.setKillablePackageAsUser(mPackageName, mUserHandle, isKillable);
142         }
143     }
144 
showConfirmationDialog()145     private void showConfirmationDialog() {
146         ConfirmationDialogFragment dialogFragment =
147                 new ConfirmationDialogFragment.Builder(getContext())
148                         .setTitle(R.string.prioritize_app_performance_dialog_title)
149                         .setMessage(R.string.prioritize_app_performance_dialog_text)
150                         .setPositiveButton(R.string.prioritize_app_performance_dialog_action_on,
151                                 mConfirmListener)
152                         .setNegativeButton(R.string.prioritize_app_performance_dialog_action_off,
153                                 /* rejectListener= */ null)
154                         .build();
155         getFragmentController().showDialog(
156                 dialogFragment, TURN_ON_PRIORITIZE_APP_PERFORMANCE_DIALOG_TAG);
157     }
158 }
159