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 package com.android.settings.display;
17 
18 import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
19 import static android.provider.Settings.Secure.CAMERA_AUTOROTATE;
20 
21 import static androidx.lifecycle.Lifecycle.Event.ON_START;
22 import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
23 
24 import android.Manifest;
25 import android.app.settings.SettingsEnums;
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.content.pm.PackageManager;
31 import android.content.pm.ResolveInfo;
32 import android.hardware.SensorPrivacyManager;
33 import android.os.PowerManager;
34 import android.provider.Settings;
35 import android.service.rotationresolver.RotationResolverService;
36 import android.text.TextUtils;
37 
38 import androidx.lifecycle.Lifecycle;
39 import androidx.lifecycle.LifecycleObserver;
40 import androidx.lifecycle.OnLifecycleEvent;
41 import androidx.preference.Preference;
42 import androidx.preference.PreferenceScreen;
43 
44 import com.android.internal.annotations.VisibleForTesting;
45 import com.android.internal.view.RotationPolicy;
46 import com.android.settings.R;
47 import com.android.settings.core.TogglePreferenceController;
48 import com.android.settings.overlay.FeatureFactory;
49 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
50 
51 /**
52  * SmartAutoRotateController controls whether auto rotation is enabled
53  */
54 public class SmartAutoRotateController extends TogglePreferenceController implements
55         Preference.OnPreferenceChangeListener, LifecycleObserver {
56 
57     private final MetricsFeatureProvider mMetricsFeatureProvider;
58     private final SensorPrivacyManager mPrivacyManager;
59     private final PowerManager mPowerManager;
60     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
61         @Override
62         public void onReceive(Context context, Intent intent) {
63             updateState(mPreference);
64         }
65     };
66     private Preference mPreference;
67     private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
68 
SmartAutoRotateController(Context context, String preferenceKey)69     public SmartAutoRotateController(Context context, String preferenceKey) {
70         super(context, preferenceKey);
71         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
72         mPrivacyManager = SensorPrivacyManager.getInstance(context);
73         mPrivacyManager
74                 .addSensorPrivacyListener(CAMERA, (sensor, enabled) -> updateState(mPreference));
75         mPowerManager = context.getSystemService(PowerManager.class);
76     }
77 
init(Lifecycle lifecycle)78     public void init(Lifecycle lifecycle) {
79         lifecycle.addObserver(this);
80     }
81 
82     @Override
getAvailabilityStatus()83     public int getAvailabilityStatus() {
84         if (!isRotationResolverServiceAvailable(mContext)) {
85             return UNSUPPORTED_ON_DEVICE;
86         }
87         return !RotationPolicy.isRotationLocked(mContext) && hasSufficientPermission(mContext)
88                 && !isCameraLocked() && !isPowerSaveMode() ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
89     }
90 
91     @Override
updateState(Preference preference)92     public void updateState(Preference preference) {
93         super.updateState(preference);
94         if (preference != null) {
95             preference.setEnabled(getAvailabilityStatus() == AVAILABLE);
96         }
97     }
98 
99     /**
100      * Need this because all controller tests use RoboElectric. No easy way to mock this service,
101      * so we mock the call we need
102      */
103     @VisibleForTesting
isCameraLocked()104     boolean isCameraLocked() {
105         return mPrivacyManager.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA);
106     }
107 
108     @VisibleForTesting
isPowerSaveMode()109     boolean isPowerSaveMode() {
110         return mPowerManager.isPowerSaveMode();
111     }
112 
113     @OnLifecycleEvent(ON_START)
onStart()114     public void onStart() {
115         mContext.registerReceiver(mReceiver,
116                 new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
117         if (mRotationPolicyListener == null) {
118             mRotationPolicyListener = new RotationPolicy.RotationPolicyListener() {
119                 @Override
120                 public void onChange() {
121                     updateState(mPreference);
122                 }
123             };
124         }
125         RotationPolicy.registerRotationPolicyListener(mContext, mRotationPolicyListener);
126     }
127 
128     @OnLifecycleEvent(ON_STOP)
onStop()129     public void onStop() {
130         mContext.unregisterReceiver(mReceiver);
131         if (mRotationPolicyListener != null) {
132             RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener);
133             mRotationPolicyListener = null;
134         }
135     }
136 
137     @Override
isChecked()138     public boolean isChecked() {
139         return !RotationPolicy.isRotationLocked(mContext) && hasSufficientPermission(mContext)
140                 && !isCameraLocked() && !isPowerSaveMode() && Settings.Secure.getInt(
141                 mContext.getContentResolver(),
142                 CAMERA_AUTOROTATE, 0) == 1;
143     }
144 
145     @Override
displayPreference(PreferenceScreen screen)146     public void displayPreference(PreferenceScreen screen) {
147         super.displayPreference(screen);
148         mPreference = screen.findPreference(getPreferenceKey());
149     }
150 
151     @Override
setChecked(boolean isChecked)152     public boolean setChecked(boolean isChecked) {
153         mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_CAMERA_ROTATE_TOGGLE,
154                 isChecked);
155         Settings.Secure.putInt(mContext.getContentResolver(),
156                 CAMERA_AUTOROTATE,
157                 isChecked ? 1 : 0);
158         return true;
159     }
160 
161     @Override
getSliceHighlightMenuRes()162     public int getSliceHighlightMenuRes() {
163         return R.string.menu_key_display;
164     }
165 
isRotationResolverServiceAvailable(Context context)166     static boolean isRotationResolverServiceAvailable(Context context) {
167         final PackageManager packageManager = context.getPackageManager();
168         final String resolvePackage = packageManager.getRotationResolverPackageName();
169         if (TextUtils.isEmpty(resolvePackage)) {
170             return false;
171         }
172         final Intent intent = new Intent(RotationResolverService.SERVICE_INTERFACE).setPackage(
173                 resolvePackage);
174         final ResolveInfo resolveInfo = packageManager.resolveService(intent,
175                 PackageManager.MATCH_SYSTEM_ONLY);
176         return resolveInfo != null && resolveInfo.serviceInfo != null;
177     }
178 
hasSufficientPermission(Context context)179     static boolean hasSufficientPermission(Context context) {
180         final PackageManager packageManager = context.getPackageManager();
181         final String rotationPackage = packageManager.getRotationResolverPackageName();
182         return rotationPackage != null && packageManager.checkPermission(
183                 Manifest.permission.CAMERA, rotationPackage) == PackageManager.PERMISSION_GRANTED;
184     }
185 }
186