Add Sensors Off QS tile and display status bar icon when active
This CL will replace the SensorPrivacyTile with a new tile that can be
enabled from the developer settings. When this new tile is enabled only
the camera, mic, and sensors controlled by the SensorManager will be
disabled; the location and airplane mode tiles will not be modified.
The user will be notified when this tile is enabled with the sensors
off icon in the status bar.
Fixes: 126618519
Test: Manually verified airplane and location are not modified when sensors
off is enabled; also verified icon is displayed when mode is active.
Change-Id: Iabf099d0d76c015015ce9859edc0b225ec554020
This commit is contained in:
@@ -8561,38 +8561,6 @@ public final class Settings {
|
||||
public static final String PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE =
|
||||
"packages_to_clear_data_before_full_restore";
|
||||
|
||||
/**
|
||||
* Indicates the location state should be maintained after sensor privacy is disabled.
|
||||
* @hide
|
||||
*/
|
||||
public static final String MAINTAIN_LOCATION_AFTER_SP_DISABLED = "0";
|
||||
|
||||
/**
|
||||
* Indicates location should be reenabled after sensor privacy is disabled.
|
||||
* @hide
|
||||
*/
|
||||
public static final String REENABLE_LOCATION_AFTER_SP_DISABLED = "1";
|
||||
|
||||
/**
|
||||
* Indicates the state of airplane mode should be maintained after sensor privacy is
|
||||
* disabled.
|
||||
* @hide
|
||||
*/
|
||||
public static final String MAINTAIN_AIRPLANE_MODE_AFTER_SP_DISABLED = "0";
|
||||
|
||||
/**
|
||||
* Indicates airplane mode should be disabled after sensor privacy is disabled.
|
||||
* @hide
|
||||
*/
|
||||
public static final String DISABLE_AIRPLANE_MODE_AFTER_SP_DISABLED = "1";
|
||||
|
||||
/**
|
||||
* The state of all sensors managed by SensorPrivacyService when sensor privacy is enabled.
|
||||
* @hide
|
||||
*/
|
||||
public static final String SENSOR_PRIVACY_SENSOR_STATE =
|
||||
"sensor_privacy_sensor_state";
|
||||
|
||||
/**
|
||||
* Setting to determine whether to use the new notification priority handling features.
|
||||
* @hide
|
||||
|
||||
@@ -590,6 +590,13 @@ enum Action {
|
||||
// OS: P
|
||||
DIALOG_SWITCH_HFP_DEVICES = 1416;
|
||||
|
||||
// OPEN: QS Sensor Privacy Mode tile shown
|
||||
// ACTION: QS Sensor Privacy Mode tile tapped
|
||||
// SUBTYPE: 0 is off, 1 is on
|
||||
// CATEGORY: QUICK_SETTINGS
|
||||
// OS: Q
|
||||
QS_SENSOR_PRIVACY = 1598;
|
||||
|
||||
// ACTION: Tap & Pay -> Default Application Setting -> Use Forground
|
||||
ACTION_NFC_PAYMENT_FOREGROUND_SETTING = 1622;
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
<item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
|
||||
<item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
|
||||
<item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
|
||||
<item><xliff:g id="id">@string/status_bar_sensors_off</xliff:g></item>
|
||||
</string-array>
|
||||
|
||||
<string translatable="false" name="status_bar_rotate">rotate</string>
|
||||
@@ -92,6 +93,7 @@
|
||||
<string translatable="false" name="status_bar_microphone">microphone</string>
|
||||
<string translatable="false" name="status_bar_camera">camera</string>
|
||||
<string translatable="false" name="status_bar_airplane">airplane</string>
|
||||
<string translatable="false" name="status_bar_sensors_off">sensors_off</string>
|
||||
|
||||
<!-- Flag indicating whether the surface flinger has limited
|
||||
alpha compositing functionality in hardware. If set, the window
|
||||
|
||||
@@ -2923,6 +2923,7 @@
|
||||
<java-symbol type="string" name="status_bar_vpn" />
|
||||
<java-symbol type="string" name="status_bar_microphone" />
|
||||
<java-symbol type="string" name="status_bar_camera" />
|
||||
<java-symbol type="string" name="status_bar_sensors_off" />
|
||||
|
||||
<!-- Locale picker -->
|
||||
<java-symbol type="id" name="locale_search_menu" />
|
||||
|
||||
@@ -623,7 +623,6 @@ public class SettingsBackupTest {
|
||||
Settings.Secure.DEFAULT_INPUT_METHOD,
|
||||
Settings.Secure.DEVICE_PAIRED,
|
||||
Settings.Secure.DIALER_DEFAULT_APPLICATION,
|
||||
Settings.Secure.DISABLE_AIRPLANE_MODE_AFTER_SP_DISABLED,
|
||||
Settings.Secure.DISABLED_PRINT_SERVICES,
|
||||
Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
|
||||
Settings.Secure.DISPLAY_DENSITY_FORCED,
|
||||
@@ -647,8 +646,6 @@ public class SettingsBackupTest {
|
||||
Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, // Candidate?
|
||||
Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
|
||||
Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
|
||||
Settings.Secure.MAINTAIN_AIRPLANE_MODE_AFTER_SP_DISABLED,
|
||||
Settings.Secure.MAINTAIN_LOCATION_AFTER_SP_DISABLED,
|
||||
Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH,
|
||||
Settings.Secure.MULTI_PRESS_TIMEOUT,
|
||||
Settings.Secure.NFC_PAYMENT_FOREGROUND,
|
||||
@@ -661,7 +658,6 @@ public class SettingsBackupTest {
|
||||
Settings.Secure.PARENTAL_CONTROL_LAST_UPDATE,
|
||||
Settings.Secure.PAYMENT_SERVICE_SEARCH_URI,
|
||||
Settings.Secure.PRINT_SERVICE_SEARCH_URI,
|
||||
Settings.Secure.REENABLE_LOCATION_AFTER_SP_DISABLED,
|
||||
Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT, // Candidate?
|
||||
Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY,
|
||||
Settings.Secure.SEARCH_MAX_RESULTS_PER_SOURCE,
|
||||
@@ -685,7 +681,6 @@ public class SettingsBackupTest {
|
||||
Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
|
||||
Settings.Secure.SELECTED_SPELL_CHECKER, // Intentionally removed in Q
|
||||
Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, // Intentionally removed in Q
|
||||
Settings.Secure.SENSOR_PRIVACY_SENSOR_STATE,
|
||||
Settings.Secure.SETTINGS_CLASSNAME,
|
||||
Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, // candidate?
|
||||
Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<!--
|
||||
Copyright (C) 2018 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.
|
||||
-->
|
||||
|
||||
<vector android:height="48dp" android:viewportHeight="5"
|
||||
android:viewportWidth="5" android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="m4.762,0.661 l-4.233,4.233"
|
||||
android:strokeAlpha="1" android:strokeColor="#000000"
|
||||
android:strokeLineCap="round" android:strokeLineJoin="miter" android:strokeWidth=".5"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M0.265,2.778L1.058,2.778l0.529,-1.323 0.529,2.646 0.529,-3.175 0.529,2.646 0.529,-1.587 0.265,0.794h1.058"
|
||||
android:strokeAlpha="1" android:strokeColor="#000000"
|
||||
android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth=".33"/>
|
||||
</vector>
|
||||
|
||||
41
packages/SystemUI/res/drawable/stat_sys_sensors_off.xml
Normal file
41
packages/SystemUI/res/drawable/stat_sys_sensors_off.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<!--
|
||||
Copyright (C) 2019 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.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetLeft="3dp"
|
||||
android:insetRight="3dp"
|
||||
android:width="17dp"
|
||||
android:height="17dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:pathData="M21.966,2 L2,22"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeColor="#000000"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeWidth="1.6521436"
|
||||
android:strokeLineJoin="miter"
|
||||
android:strokeAlpha="1"/>
|
||||
<path
|
||||
android:pathData="M0.752,12L4.496,12l2.496,-6.25 2.496,12.5 2.496,-15 2.496,12.5 2.496,-7.5 1.248,3.75h4.992"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeColor="#000000"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeWidth="1.25090861"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeAlpha="1"/>
|
||||
</vector>
|
||||
|
||||
@@ -97,6 +97,7 @@ import com.android.systemui.statusbar.policy.NextAlarmController;
|
||||
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
|
||||
import com.android.systemui.statusbar.policy.RotationLockController;
|
||||
import com.android.systemui.statusbar.policy.SecurityController;
|
||||
import com.android.systemui.statusbar.policy.SensorPrivacyController;
|
||||
import com.android.systemui.statusbar.policy.SmartReplyConstants;
|
||||
import com.android.systemui.statusbar.policy.UserInfoController;
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||
@@ -291,6 +292,7 @@ public class Dependency extends SystemUI {
|
||||
@Inject Lazy<ActivityManagerWrapper> mActivityManagerWrapper;
|
||||
@Inject Lazy<DevicePolicyManagerWrapper> mDevicePolicyManagerWrapper;
|
||||
@Inject Lazy<PackageManagerWrapper> mPackageManagerWrapper;
|
||||
@Inject Lazy<SensorPrivacyController> mSensorPrivacyController;
|
||||
|
||||
@Inject
|
||||
public Dependency() {
|
||||
@@ -461,6 +463,7 @@ public class Dependency extends SystemUI {
|
||||
mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get);
|
||||
mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get);
|
||||
mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get);
|
||||
mProviders.put(SensorPrivacyController.class, mSensorPrivacyController::get);
|
||||
|
||||
|
||||
// TODO(b/118592525): to support multi-display , we start to add something which is
|
||||
|
||||
@@ -60,6 +60,8 @@ import com.android.systemui.statusbar.policy.RotationLockController;
|
||||
import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
|
||||
import com.android.systemui.statusbar.policy.SecurityController;
|
||||
import com.android.systemui.statusbar.policy.SecurityControllerImpl;
|
||||
import com.android.systemui.statusbar.policy.SensorPrivacyController;
|
||||
import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
|
||||
import com.android.systemui.statusbar.policy.UserInfoController;
|
||||
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
|
||||
import com.android.systemui.statusbar.policy.ZenModeController;
|
||||
@@ -222,6 +224,12 @@ public abstract class DependencyBinder {
|
||||
@Binds
|
||||
public abstract PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl);
|
||||
|
||||
/**
|
||||
*/
|
||||
@Binds
|
||||
public abstract SensorPrivacyController provideSensorPrivacyControllerImpl(
|
||||
SensorPrivacyControllerImpl controllerImpl);
|
||||
|
||||
/**
|
||||
*/
|
||||
@Binds
|
||||
|
||||
@@ -41,7 +41,6 @@ import com.android.systemui.qs.tiles.LocationTile;
|
||||
import com.android.systemui.qs.tiles.NfcTile;
|
||||
import com.android.systemui.qs.tiles.NightDisplayTile;
|
||||
import com.android.systemui.qs.tiles.RotationLockTile;
|
||||
import com.android.systemui.qs.tiles.SensorPrivacyTile;
|
||||
import com.android.systemui.qs.tiles.UserTile;
|
||||
import com.android.systemui.qs.tiles.WifiTile;
|
||||
import com.android.systemui.qs.tiles.WorkModeTile;
|
||||
@@ -73,7 +72,6 @@ public class QSFactoryImpl implements QSFactory {
|
||||
private final Provider<DataSaverTile> mDataSaverTileProvider;
|
||||
private final Provider<NightDisplayTile> mNightDisplayTileProvider;
|
||||
private final Provider<NfcTile> mNfcTileProvider;
|
||||
private final Provider<SensorPrivacyTile> mSensorPrivacyTileProvider;
|
||||
private final Provider<GarbageMonitor.MemoryTile> mMemoryTileProvider;
|
||||
|
||||
private QSTileHost mHost;
|
||||
@@ -96,7 +94,6 @@ public class QSFactoryImpl implements QSFactory {
|
||||
Provider<DataSaverTile> dataSaverTileProvider,
|
||||
Provider<NightDisplayTile> nightDisplayTileProvider,
|
||||
Provider<NfcTile> nfcTileProvider,
|
||||
Provider<SensorPrivacyTile> sensorPrivacyTileProvider,
|
||||
Provider<GarbageMonitor.MemoryTile> memoryTileProvider) {
|
||||
mWifiTileProvider = wifiTileProvider;
|
||||
mBluetoothTileProvider = bluetoothTileProvider;
|
||||
@@ -115,7 +112,6 @@ public class QSFactoryImpl implements QSFactory {
|
||||
mDataSaverTileProvider = dataSaverTileProvider;
|
||||
mNightDisplayTileProvider = nightDisplayTileProvider;
|
||||
mNfcTileProvider = nfcTileProvider;
|
||||
mSensorPrivacyTileProvider = sensorPrivacyTileProvider;
|
||||
mMemoryTileProvider = memoryTileProvider;
|
||||
}
|
||||
|
||||
@@ -168,8 +164,6 @@ public class QSFactoryImpl implements QSFactory {
|
||||
return mNightDisplayTileProvider.get();
|
||||
case "nfc":
|
||||
return mNfcTileProvider.get();
|
||||
case "sensorprivacy":
|
||||
return mSensorPrivacyTileProvider.get();
|
||||
}
|
||||
|
||||
// Intent tiles.
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.systemui.qs.tiles;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.hardware.SensorPrivacyManager;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.widget.Switch;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.plugins.qs.QSTile.BooleanState;
|
||||
import com.android.systemui.qs.QSHost;
|
||||
import com.android.systemui.qs.tileimpl.QSTileImpl;
|
||||
import com.android.systemui.statusbar.policy.KeyguardMonitor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Quick settings tile: SensorPrivacy mode **/
|
||||
public class SensorPrivacyTile extends QSTileImpl<BooleanState> implements
|
||||
SensorPrivacyManager.OnSensorPrivacyChangedListener {
|
||||
private static final String TAG = "SensorPrivacy";
|
||||
private final Icon mIcon =
|
||||
ResourceIcon.get(R.drawable.ic_signal_sensors);
|
||||
private final KeyguardMonitor mKeyguard;
|
||||
private final SensorPrivacyManager mSensorPrivacyManager;
|
||||
private final ActivityStarter mActivityStarter;
|
||||
|
||||
@Inject
|
||||
public SensorPrivacyTile(QSHost host, SensorPrivacyManager sensorPrivacyManager,
|
||||
KeyguardMonitor keyguardMonitor, ActivityStarter activityStarter) {
|
||||
super(host);
|
||||
|
||||
mSensorPrivacyManager = sensorPrivacyManager;
|
||||
mKeyguard = keyguardMonitor;
|
||||
mActivityStarter = activityStarter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanState newTileState() {
|
||||
return new BooleanState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleClick() {
|
||||
final boolean wasEnabled = mState.value;
|
||||
// Don't allow disabling from the lockscreen.
|
||||
if (wasEnabled && mKeyguard.isSecure() && mKeyguard.isShowing()) {
|
||||
mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
|
||||
MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
|
||||
setEnabled(!wasEnabled);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
|
||||
setEnabled(!wasEnabled);
|
||||
}
|
||||
|
||||
private void setEnabled(boolean enabled) {
|
||||
mSensorPrivacyManager.setSensorPrivacy(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTileLabel() {
|
||||
return mContext.getString(R.string.sensor_privacy_mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getLongClickIntent() {
|
||||
return new Intent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleUpdateState(BooleanState state, Object arg) {
|
||||
final boolean enabled = arg instanceof Boolean ? (Boolean) arg
|
||||
: mSensorPrivacyManager.isSensorPrivacyEnabled();
|
||||
state.value = enabled;
|
||||
state.label = mContext.getString(R.string.sensor_privacy_mode);
|
||||
state.icon = mIcon;
|
||||
state.state = enabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
|
||||
state.contentDescription = state.label;
|
||||
state.expandedAccessibilityClassName = Switch.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return MetricsEvent.QS_SENSOR_PRIVACY;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String composeChangeAnnouncement() {
|
||||
if (mState.value) {
|
||||
return mContext
|
||||
.getString(R.string.accessibility_quick_settings_sensor_privacy_changed_on);
|
||||
} else {
|
||||
return mContext
|
||||
.getString(R.string.accessibility_quick_settings_sensor_privacy_changed_off);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleSetListening(boolean listening) {
|
||||
if (listening) {
|
||||
mSensorPrivacyManager.addSensorPrivacyListener(this);
|
||||
} else {
|
||||
mSensorPrivacyManager.removeSensorPrivacyListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorPrivacyChanged(boolean enabled) {
|
||||
refreshState(enabled);
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,7 @@ import com.android.systemui.statusbar.policy.LocationController;
|
||||
import com.android.systemui.statusbar.policy.NextAlarmController;
|
||||
import com.android.systemui.statusbar.policy.RotationLockController;
|
||||
import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
|
||||
import com.android.systemui.statusbar.policy.SensorPrivacyController;
|
||||
import com.android.systemui.statusbar.policy.UserInfoController;
|
||||
import com.android.systemui.statusbar.policy.ZenModeController;
|
||||
|
||||
@@ -100,6 +101,7 @@ public class PhoneStatusBarPolicy
|
||||
private final String mSlotLocation;
|
||||
private final String mSlotMicrophone;
|
||||
private final String mSlotCamera;
|
||||
private final String mSlotSensorsOff;
|
||||
|
||||
private final Context mContext;
|
||||
private final Handler mHandler = new Handler();
|
||||
@@ -118,6 +120,7 @@ public class PhoneStatusBarPolicy
|
||||
private final LocationController mLocationController;
|
||||
private final PrivacyItemController mPrivacyItemController;
|
||||
private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
|
||||
private final SensorPrivacyController mSensorPrivacyController;
|
||||
|
||||
// Assume it's all good unless we hear otherwise. We don't always seem
|
||||
// to get broadcasts that it *is* there.
|
||||
@@ -149,6 +152,7 @@ public class PhoneStatusBarPolicy
|
||||
mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
|
||||
mLocationController = Dependency.get(LocationController.class);
|
||||
mPrivacyItemController = Dependency.get(PrivacyItemController.class);
|
||||
mSensorPrivacyController = Dependency.get(SensorPrivacyController.class);
|
||||
|
||||
mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
|
||||
mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
|
||||
@@ -165,6 +169,7 @@ public class PhoneStatusBarPolicy
|
||||
mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);
|
||||
mSlotMicrophone = context.getString(com.android.internal.R.string.status_bar_microphone);
|
||||
mSlotCamera = context.getString(com.android.internal.R.string.status_bar_camera);
|
||||
mSlotSensorsOff = context.getString(com.android.internal.R.string.status_bar_sensors_off);
|
||||
|
||||
// listen for broadcasts
|
||||
IntentFilter filter = new IntentFilter();
|
||||
@@ -232,6 +237,11 @@ public class PhoneStatusBarPolicy
|
||||
mContext.getString(R.string.accessibility_location_active));
|
||||
mIconController.setIconVisibility(mSlotLocation, false);
|
||||
|
||||
// sensors off
|
||||
mIconController.setIcon(mSlotSensorsOff, R.drawable.stat_sys_sensors_off, null);
|
||||
mIconController.setIconVisibility(mSlotSensorsOff,
|
||||
mSensorPrivacyController.isSensorPrivacyEnabled());
|
||||
|
||||
mRotationLockController.addCallback(this);
|
||||
mBluetooth.addCallback(this);
|
||||
mProvisionedController.addCallback(this);
|
||||
@@ -242,6 +252,7 @@ public class PhoneStatusBarPolicy
|
||||
mDataSaver.addCallback(this);
|
||||
mKeyguardMonitor.addCallback(this);
|
||||
mPrivacyItemController.addCallback(this);
|
||||
mSensorPrivacyController.addCallback(mSensorPrivacyListener);
|
||||
|
||||
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
|
||||
}
|
||||
@@ -514,6 +525,16 @@ public class PhoneStatusBarPolicy
|
||||
}
|
||||
};
|
||||
|
||||
private final SensorPrivacyController.OnSensorPrivacyChangedListener mSensorPrivacyListener =
|
||||
new SensorPrivacyController.OnSensorPrivacyChangedListener() {
|
||||
@Override
|
||||
public void onSensorPrivacyChanged(boolean enabled) {
|
||||
mHandler.post(() -> {
|
||||
mIconController.setIconVisibility(mSlotSensorsOff, enabled);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void appTransitionStarting(int displayId, long startTime, long duration,
|
||||
boolean forced) {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.systemui.statusbar.policy;
|
||||
|
||||
/**
|
||||
* Interface for classes to control sensor privacy state and notification.
|
||||
*/
|
||||
public interface SensorPrivacyController extends
|
||||
CallbackController<SensorPrivacyController.OnSensorPrivacyChangedListener> {
|
||||
|
||||
/**
|
||||
* Returns whether sensor privacy is enabled.
|
||||
*/
|
||||
boolean isSensorPrivacyEnabled();
|
||||
|
||||
/**
|
||||
* Interface for classes to receive callbacks when sensor privacy state changes.
|
||||
*/
|
||||
interface OnSensorPrivacyChangedListener {
|
||||
void onSensorPrivacyChanged(boolean enabled);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.systemui.statusbar.policy;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.SensorPrivacyManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Controls sensor privacy state and notification.
|
||||
*/
|
||||
@Singleton
|
||||
public class SensorPrivacyControllerImpl implements SensorPrivacyController,
|
||||
SensorPrivacyManager.OnSensorPrivacyChangedListener {
|
||||
private SensorPrivacyManager mSensorPrivacyManager;
|
||||
private final List<OnSensorPrivacyChangedListener> mListeners;
|
||||
private Object mLock = new Object();
|
||||
private boolean mSensorPrivacyEnabled;
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
*/
|
||||
@Inject
|
||||
public SensorPrivacyControllerImpl(Context context) {
|
||||
mSensorPrivacyManager = (SensorPrivacyManager) context.getSystemService(
|
||||
Context.SENSOR_PRIVACY_SERVICE);
|
||||
mSensorPrivacyEnabled = mSensorPrivacyManager.isSensorPrivacyEnabled();
|
||||
mSensorPrivacyManager.addSensorPrivacyListener(this);
|
||||
mListeners = new ArrayList<>(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether sensor privacy is enabled.
|
||||
*/
|
||||
public boolean isSensorPrivacyEnabled() {
|
||||
synchronized (mLock) {
|
||||
return mSensorPrivacyEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the provided listener for callbacks when sensor privacy state changes.
|
||||
*/
|
||||
public void addCallback(OnSensorPrivacyChangedListener listener) {
|
||||
synchronized (mLock) {
|
||||
mListeners.add(listener);
|
||||
notifyListenerLocked(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the provided listener from callbacks when sensor privacy state changes.
|
||||
*/
|
||||
public void removeCallback(OnSensorPrivacyChangedListener listener) {
|
||||
synchronized (mLock) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked by the SensorPrivacyService when sensor privacy state changes.
|
||||
*/
|
||||
public void onSensorPrivacyChanged(boolean enabled) {
|
||||
synchronized (mLock) {
|
||||
mSensorPrivacyEnabled = enabled;
|
||||
for (OnSensorPrivacyChangedListener listener : mListeners) {
|
||||
notifyListenerLocked(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyListenerLocked(OnSensorPrivacyChangedListener listener) {
|
||||
listener.onSensorPrivacyChanged(mSensorPrivacyEnabled);
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.systemui.qs.tiles;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.hardware.SensorPrivacyManager;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.qs.QSTileHost;
|
||||
import com.android.systemui.statusbar.policy.KeyguardMonitor;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
@SmallTest
|
||||
public class SensorPrivacyTileTest extends SysuiTestCase {
|
||||
|
||||
@Mock
|
||||
private KeyguardMonitor mKeyguard;
|
||||
@Mock
|
||||
private QSTileHost mHost;
|
||||
@Mock
|
||||
SensorPrivacyManager mSensorPrivacyManager;
|
||||
|
||||
private TestableLooper mTestableLooper;
|
||||
|
||||
private SensorPrivacyTile mSensorPrivacyTile;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mTestableLooper = TestableLooper.get(this);
|
||||
mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
|
||||
mKeyguard = mDependency.injectMockDependency(KeyguardMonitor.class);
|
||||
|
||||
mSensorPrivacyManager = mDependency.injectMockDependency(SensorPrivacyManager.class);
|
||||
|
||||
when(mHost.getContext()).thenReturn(mContext);
|
||||
|
||||
mSensorPrivacyTile = new SensorPrivacyTile(mHost, mSensorPrivacyManager, mKeyguard,
|
||||
mock(ActivityStarter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSensorPrivacyListenerAdded_handleListeningTrue() {
|
||||
// To prevent access to privacy related features from apps with WRITE_SECURE_SETTINGS the
|
||||
// sensor privacy state is not stored in Settings; to receive notification apps must add
|
||||
// themselves as a listener with the SensorPrivacyManager. This test verifies when
|
||||
// setListening is called with a value of true the tile adds itself as a listener.
|
||||
mSensorPrivacyTile.handleSetListening(true);
|
||||
mTestableLooper.processAllMessages();
|
||||
verify(mSensorPrivacyManager).addSensorPrivacyListener(mSensorPrivacyTile);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSensorPrivacyListenerRemoved_handleListeningFalse() {
|
||||
// Similar to the test above verifies that the tile removes itself as a listener when
|
||||
// setListening is called with a value of false.
|
||||
mSensorPrivacyTile.handleSetListening(false);
|
||||
mTestableLooper.processAllMessages();
|
||||
verify(mSensorPrivacyManager).removeSensorPrivacyListener((mSensorPrivacyTile));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSensorPrivacyEnabled_handleClick() {
|
||||
// Verifies when the SensorPrivacy tile is clicked it invokes the SensorPrivacyManager to
|
||||
// set sensor privacy.
|
||||
mSensorPrivacyTile.getState().value = false;
|
||||
mSensorPrivacyTile.handleClick();
|
||||
mTestableLooper.processAllMessages();
|
||||
verify(mSensorPrivacyManager).setSensorPrivacy(true);
|
||||
|
||||
mSensorPrivacyTile.getState().value = true;
|
||||
mSensorPrivacyTile.handleClick();
|
||||
mTestableLooper.processAllMessages();
|
||||
verify(mSensorPrivacyManager).setSensorPrivacy(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSensorPrivacyNotDisabled_keyguard() {
|
||||
// Verifies when the device is locked that sensor privacy cannot be disabled
|
||||
when(mKeyguard.isSecure()).thenReturn(true);
|
||||
when(mKeyguard.isShowing()).thenReturn(true);
|
||||
mSensorPrivacyTile.getState().value = true;
|
||||
mSensorPrivacyTile.handleClick();
|
||||
mTestableLooper.processAllMessages();
|
||||
verify(mSensorPrivacyManager, never()).setSensorPrivacy(false);
|
||||
}
|
||||
}
|
||||
@@ -18,20 +18,15 @@ package com.android.server;
|
||||
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.hardware.ISensorPrivacyListener;
|
||||
import android.hardware.ISensorPrivacyManager;
|
||||
import android.location.LocationManager;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.AtomicFile;
|
||||
import android.util.Log;
|
||||
@@ -277,8 +272,6 @@ public final class SensorPrivacyService extends SystemService {
|
||||
}
|
||||
}
|
||||
mListeners.finishBroadcast();
|
||||
// Handle the state of all sensors managed by this service.
|
||||
SensorState.handleSensorPrivacyToggled(mContext, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,121 +299,4 @@ public final class SensorPrivacyService extends SystemService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maintains the state of the sensors when sensor privacy is enabled to return them to their
|
||||
* original state when sensor privacy is disabled.
|
||||
*/
|
||||
private static final class SensorState {
|
||||
|
||||
private static Object sLock = new Object();
|
||||
@GuardedBy("sLock")
|
||||
private static SensorState sPreviousState;
|
||||
|
||||
private boolean mAirplaneEnabled;
|
||||
private boolean mLocationEnabled;
|
||||
|
||||
SensorState(boolean airplaneEnabled, boolean locationEnabled) {
|
||||
mAirplaneEnabled = airplaneEnabled;
|
||||
mLocationEnabled = locationEnabled;
|
||||
}
|
||||
|
||||
public static void handleSensorPrivacyToggled(Context context, boolean enabled) {
|
||||
synchronized (sLock) {
|
||||
SensorState state;
|
||||
if (enabled) {
|
||||
// if sensor privacy is being enabled then obtain the current state of the
|
||||
// sensors to be persisted and restored when sensor privacy is disabled.
|
||||
state = getCurrentSensorState(context);
|
||||
} else {
|
||||
// else obtain the previous sensor state to be restored, first from the saved
|
||||
// state if available, otherwise attempt to read it from Settings.
|
||||
if (sPreviousState != null) {
|
||||
state = sPreviousState;
|
||||
} else {
|
||||
state = getPersistedSensorState(context);
|
||||
}
|
||||
// if the previous state is not available then return without attempting to
|
||||
// modify the sensor state.
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// The SensorState represents the state of the sensor before sensor privacy was
|
||||
// enabled; if airplane mode was not enabled then the state of airplane mode should
|
||||
// be the same as the state of sensor privacy.
|
||||
if (!state.mAirplaneEnabled) {
|
||||
setAirplaneMode(context, enabled);
|
||||
}
|
||||
// Similar to airplane mode the state of location should be the opposite of sensor
|
||||
// privacy mode, if it was enabled when sensor privacy was enabled then it should be
|
||||
// disabled. If location is disabled when sensor privacy is enabled then it will be
|
||||
// left disabled when sensor privacy is disabled.
|
||||
if (state.mLocationEnabled) {
|
||||
setLocationEnabled(context, !enabled);
|
||||
}
|
||||
|
||||
// if sensor privacy is being enabled then persist the current state.
|
||||
if (enabled) {
|
||||
sPreviousState = state;
|
||||
persistState(context, sPreviousState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static SensorState getCurrentSensorState(Context context) {
|
||||
LocationManager locationManager = (LocationManager) context.getSystemService(
|
||||
Context.LOCATION_SERVICE);
|
||||
boolean airplaneEnabled = Settings.Global.getInt(context.getContentResolver(),
|
||||
Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
|
||||
boolean locationEnabled = locationManager.isLocationEnabled();
|
||||
return new SensorState(airplaneEnabled, locationEnabled);
|
||||
}
|
||||
|
||||
public static void persistState(Context context, SensorState state) {
|
||||
StringBuilder stateValue = new StringBuilder();
|
||||
stateValue.append(state.mAirplaneEnabled
|
||||
? Settings.Secure.MAINTAIN_AIRPLANE_MODE_AFTER_SP_DISABLED
|
||||
: Settings.Secure.DISABLE_AIRPLANE_MODE_AFTER_SP_DISABLED);
|
||||
stateValue.append(",");
|
||||
stateValue.append(
|
||||
state.mLocationEnabled ? Settings.Secure.REENABLE_LOCATION_AFTER_SP_DISABLED
|
||||
: Settings.Secure.MAINTAIN_LOCATION_AFTER_SP_DISABLED);
|
||||
Settings.Secure.putString(context.getContentResolver(),
|
||||
Settings.Secure.SENSOR_PRIVACY_SENSOR_STATE, stateValue.toString());
|
||||
}
|
||||
|
||||
public static SensorState getPersistedSensorState(Context context) {
|
||||
String persistedState = Settings.Secure.getString(context.getContentResolver(),
|
||||
Settings.Secure.SENSOR_PRIVACY_SENSOR_STATE);
|
||||
if (persistedState == null) {
|
||||
Log.e(TAG, "The persisted sensor state could not be obtained from Settings");
|
||||
return null;
|
||||
}
|
||||
String[] sensorStates = persistedState.split(",");
|
||||
if (sensorStates.length < 2) {
|
||||
Log.e(TAG, "The persisted sensor state does not contain the expected values: "
|
||||
+ persistedState);
|
||||
return null;
|
||||
}
|
||||
boolean airplaneEnabled = sensorStates[0].equals(
|
||||
Settings.Secure.MAINTAIN_AIRPLANE_MODE_AFTER_SP_DISABLED);
|
||||
boolean locationEnabled = sensorStates[1].equals(
|
||||
Settings.Secure.REENABLE_LOCATION_AFTER_SP_DISABLED);
|
||||
return new SensorState(airplaneEnabled, locationEnabled);
|
||||
}
|
||||
|
||||
private static void setAirplaneMode(Context context, boolean enable) {
|
||||
ConnectivityManager connectivityManager =
|
||||
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
connectivityManager.setAirplaneMode(enable);
|
||||
}
|
||||
|
||||
private static void setLocationEnabled(Context context, boolean enable) {
|
||||
LocationManager locationManager = (LocationManager) context.getSystemService(
|
||||
Context.LOCATION_SERVICE);
|
||||
locationManager.setLocationEnabledForUser(enable,
|
||||
UserHandle.of(ActivityManager.getCurrentUser()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user