Merge "Merge "Import translations. DO NOT MERGE" into cw-f-dev am: e1add91fa1 -s ours am: f0ed05f4b7 -s ours am: d62f14af4c -s ours"
diff --git a/packages/SystemUI/res/color/qs_tile_text.xml b/packages/SystemUI/res/color/qs_tile_text.xml
deleted file mode 100644
index 90e0bce..0000000
--- a/packages/SystemUI/res/color/qs_tile_text.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2015 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
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
- <item android:color="#B3FFFFFF" /> <!-- 70% white -->
-</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_dual_tile_caret.xml b/packages/SystemUI/res/drawable/qs_dual_tile_caret.xml
deleted file mode 100644
index 71400db..0000000
--- a/packages/SystemUI/res/drawable/qs_dual_tile_caret.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 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:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
-
- <path
- android:fillColor="@color/qs_tile_text"
- android:pathData="M14.0,20.0l10.0,10.0 10.0,-10.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index cce9c0f..5b0f0df 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -22,7 +22,7 @@
<TextView android:id="@+id/tile_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="@color/qs_tile_text"
+ android:textColor="?android:attr/textColorSecondary"
android:gravity="center_horizontal"
android:minLines="2"
android:padding="0dp"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
index 1a46815..4969a1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
@@ -14,134 +14,16 @@
package com.android.systemui.statusbar.phone;
-import android.app.ActivityManager;
-import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.UserInfo;
-import android.os.UserHandle;
-import android.os.UserManager;
-
import com.android.systemui.statusbar.phone.ManagedProfileController.Callback;
import com.android.systemui.statusbar.policy.CallbackController;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
+public interface ManagedProfileController extends CallbackController<Callback> {
-public class ManagedProfileController implements CallbackController<Callback> {
+ void setWorkModeEnabled(boolean enabled);
- private final List<Callback> mCallbacks = new ArrayList<>();
+ boolean hasActiveProfile();
- private final Context mContext;
- private final UserManager mUserManager;
- private final LinkedList<UserInfo> mProfiles;
- private boolean mListening;
- private int mCurrentUser;
-
- public ManagedProfileController(QSTileHost host) {
- mContext = host.getContext();
- mUserManager = UserManager.get(mContext);
- mProfiles = new LinkedList<UserInfo>();
- }
-
- public void addCallback(Callback callback) {
- mCallbacks.add(callback);
- if (mCallbacks.size() == 1) {
- setListening(true);
- }
- callback.onManagedProfileChanged();
- }
-
- public void removeCallback(Callback callback) {
- if (mCallbacks.remove(callback) && mCallbacks.size() == 0) {
- setListening(false);
- }
- }
-
- public void setWorkModeEnabled(boolean enableWorkMode) {
- synchronized (mProfiles) {
- for (UserInfo ui : mProfiles) {
- if (enableWorkMode) {
- if (!mUserManager.trySetQuietModeDisabled(ui.id, null)) {
- StatusBarManager statusBarManager = (StatusBarManager) mContext
- .getSystemService(android.app.Service.STATUS_BAR_SERVICE);
- statusBarManager.collapsePanels();
- }
- } else {
- mUserManager.setQuietModeEnabled(ui.id, true);
- }
- }
- }
- }
-
- private void reloadManagedProfiles() {
- synchronized (mProfiles) {
- boolean hadProfile = mProfiles.size() > 0;
- int user = ActivityManager.getCurrentUser();
- mProfiles.clear();
-
- for (UserInfo ui : mUserManager.getEnabledProfiles(user)) {
- if (ui.isManagedProfile()) {
- mProfiles.add(ui);
- }
- }
- if (mProfiles.size() == 0 && hadProfile && (user == mCurrentUser)) {
- for (Callback callback : mCallbacks) {
- callback.onManagedProfileRemoved();
- }
- }
- mCurrentUser = user;
- }
- }
-
- public boolean hasActiveProfile() {
- if (!mListening) reloadManagedProfiles();
- synchronized (mProfiles) {
- return mProfiles.size() > 0;
- }
- }
-
- public boolean isWorkModeEnabled() {
- if (!mListening) reloadManagedProfiles();
- synchronized (mProfiles) {
- for (UserInfo ui : mProfiles) {
- if (ui.isQuietModeEnabled()) {
- return false;
- }
- }
- return true;
- }
- }
-
- private void setListening(boolean listening) {
- mListening = listening;
- if (listening) {
- reloadManagedProfiles();
-
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, null);
- } else {
- mContext.unregisterReceiver(mReceiver);
- }
- }
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- reloadManagedProfiles();
- for (Callback callback : mCallbacks) {
- callback.onManagedProfileChanged();
- }
- }
- };
+ boolean isWorkModeEnabled();
public interface Callback {
void onManagedProfileChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
new file mode 100644
index 0000000..fc33ace
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 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.phone;
+
+import android.app.ActivityManager;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class ManagedProfileControllerImpl implements ManagedProfileController {
+
+ private final List<Callback> mCallbacks = new ArrayList<>();
+
+ private final Context mContext;
+ private final UserManager mUserManager;
+ private final LinkedList<UserInfo> mProfiles;
+ private boolean mListening;
+ private int mCurrentUser;
+
+ public ManagedProfileControllerImpl(QSTileHost host) {
+ mContext = host.getContext();
+ mUserManager = UserManager.get(mContext);
+ mProfiles = new LinkedList<UserInfo>();
+ }
+
+ public void addCallback(Callback callback) {
+ mCallbacks.add(callback);
+ if (mCallbacks.size() == 1) {
+ setListening(true);
+ }
+ callback.onManagedProfileChanged();
+ }
+
+ public void removeCallback(Callback callback) {
+ if (mCallbacks.remove(callback) && mCallbacks.size() == 0) {
+ setListening(false);
+ }
+ }
+
+ public void setWorkModeEnabled(boolean enableWorkMode) {
+ synchronized (mProfiles) {
+ for (UserInfo ui : mProfiles) {
+ if (enableWorkMode) {
+ if (!mUserManager.trySetQuietModeDisabled(ui.id, null)) {
+ StatusBarManager statusBarManager = (StatusBarManager) mContext
+ .getSystemService(android.app.Service.STATUS_BAR_SERVICE);
+ statusBarManager.collapsePanels();
+ }
+ } else {
+ mUserManager.setQuietModeEnabled(ui.id, true);
+ }
+ }
+ }
+ }
+
+ private void reloadManagedProfiles() {
+ synchronized (mProfiles) {
+ boolean hadProfile = mProfiles.size() > 0;
+ int user = ActivityManager.getCurrentUser();
+ mProfiles.clear();
+
+ for (UserInfo ui : mUserManager.getEnabledProfiles(user)) {
+ if (ui.isManagedProfile()) {
+ mProfiles.add(ui);
+ }
+ }
+ if (mProfiles.size() == 0 && hadProfile && (user == mCurrentUser)) {
+ for (Callback callback : mCallbacks) {
+ callback.onManagedProfileRemoved();
+ }
+ }
+ mCurrentUser = user;
+ }
+ }
+
+ public boolean hasActiveProfile() {
+ if (!mListening) reloadManagedProfiles();
+ synchronized (mProfiles) {
+ return mProfiles.size() > 0;
+ }
+ }
+
+ public boolean isWorkModeEnabled() {
+ if (!mListening) reloadManagedProfiles();
+ synchronized (mProfiles) {
+ for (UserInfo ui : mProfiles) {
+ if (ui.isQuietModeEnabled()) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private void setListening(boolean listening) {
+ mListening = listening;
+ if (listening) {
+ reloadManagedProfiles();
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, null);
+ } else {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ reloadManagedProfiles();
+ for (Callback callback : mCallbacks) {
+ callback.onManagedProfileChanged();
+ }
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 2c61358..4579ce6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -22,6 +22,7 @@
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.windowStateToString;
+
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
@@ -83,8 +84,8 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.Trace;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
@@ -123,13 +124,13 @@
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.LatencyTracker;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.DemoMode;
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
import com.android.systemui.Interpolators;
-import com.android.keyguard.LatencyTracker;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
@@ -178,19 +179,20 @@
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.CastControllerImpl;
import com.android.systemui.statusbar.policy.EncryptionHelper;
-import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HotspotControllerImpl;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.LocationControllerImpl;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
import com.android.systemui.statusbar.policy.PreviewInflater;
import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
import com.android.systemui.statusbar.policy.SecurityControllerImpl;
-import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -319,15 +321,15 @@
NetworkControllerImpl mNetworkController;
HotspotControllerImpl mHotspotController;
RotationLockControllerImpl mRotationLockController;
- UserInfoController mUserInfoController;
+ UserInfoControllerImpl mUserInfoController;
protected ZenModeController mZenModeController;
CastControllerImpl mCastController;
VolumeComponent mVolumeComponent;
KeyguardUserSwitcher mKeyguardUserSwitcher;
- FlashlightController mFlashlightController;
+ FlashlightControllerImpl mFlashlightController;
protected UserSwitcherController mUserSwitcherController;
- NextAlarmController mNextAlarmController;
- protected KeyguardMonitor mKeyguardMonitor;
+ NextAlarmControllerImpl mNextAlarmController;
+ protected KeyguardMonitorImpl mKeyguardMonitor;
BrightnessMirrorController mBrightnessMirrorController;
AccessibilityController mAccessibilityController;
protected FingerprintUnlockController mFingerprintUnlockController;
@@ -895,7 +897,7 @@
if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {
mRotationLockController = new RotationLockControllerImpl(mContext);
}
- mUserInfoController = new UserInfoController(mContext);
+ mUserInfoController = new UserInfoControllerImpl(mContext);
mVolumeComponent = getComponent(VolumeComponent.class);
if (mVolumeComponent != null) {
mZenModeController = mVolumeComponent.getZenController();
@@ -906,16 +908,16 @@
initSignalCluster(mKeyguardStatusBar);
initEmergencyCryptkeeperText();
- mFlashlightController = new FlashlightController(mContext);
+ mFlashlightController = new FlashlightControllerImpl(mContext);
mKeyguardBottomArea.setFlashlightController(mFlashlightController);
mKeyguardBottomArea.setPhoneStatusBar(this);
mKeyguardBottomArea.setUserSetupComplete(mUserSetup);
mAccessibilityController = new AccessibilityController(mContext);
mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
- mNextAlarmController = new NextAlarmController(mContext);
+ mNextAlarmController = new NextAlarmControllerImpl(mContext);
mLightStatusBarController = new LightStatusBarController(mIconController,
mBatteryController);
- mKeyguardMonitor = new KeyguardMonitor(mContext);
+ mKeyguardMonitor = new KeyguardMonitorImpl(mContext);
mUserSwitcherController = createUserSwitcherController();
if (UserManager.get(mContext).isUserSwitcherEnabled()) {
createUserSwitcher();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 8fd6bbf..567ab3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -143,7 +143,7 @@
mBattery = battery;
mIconController = iconController;
mNextAlarmController = nextAlarmController;
- mProfileController = new ManagedProfileController(this);
+ mProfileController = new ManagedProfileControllerImpl(this);
mHandlerThread = new HandlerThread(QSTileHost.class.getSimpleName(),
Process.THREAD_PRIORITY_BACKGROUND);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 28aed87..9ab4d77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -236,10 +236,17 @@
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mHost.getUserInfoController().addCallback(this);
+ }
+
+ @Override
protected void onDetachedFromWindow() {
setListening(false);
mHost.getUserInfoController().removeCallback(this);
mHost.getNetworkController().removeEmergencyListener(this);
+ mHost.getUserInfoController().removeCallback(this);
super.onDetachedFromWindow();
}
@@ -368,7 +375,7 @@
}
public void setUserInfoController(UserInfoController userInfoController) {
- userInfoController.addCallback(this);
+ // Don't care
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
index e5f1e68..0df7859 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
@@ -14,92 +14,14 @@
package com.android.systemui.statusbar.policy;
-import android.content.Context;
-import android.net.INetworkPolicyListener;
-import android.net.NetworkPolicyManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
-import java.util.ArrayList;
+public interface DataSaverController extends CallbackController<Listener> {
-public class DataSaverController implements CallbackController<Listener> {
-
- private final Handler mHandler = new Handler(Looper.getMainLooper());
- private final ArrayList<Listener> mListeners = new ArrayList<>();
- private final NetworkPolicyManager mPolicyManager;
-
- public DataSaverController(Context context) {
- mPolicyManager = NetworkPolicyManager.from(context);
- }
-
- private void handleRestrictBackgroundChanged(boolean isDataSaving) {
- synchronized (mListeners) {
- for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).onDataSaverChanged(isDataSaving);
- }
- }
- }
-
- public void addCallback(Listener listener) {
- synchronized (mListeners) {
- mListeners.add(listener);
- if (mListeners.size() == 1) {
- mPolicyManager.registerListener(mPolicyListener);
- }
- }
- listener.onDataSaverChanged(isDataSaverEnabled());
- }
-
- public void removeCallback(Listener listener) {
- synchronized (mListeners) {
- mListeners.remove(listener);
- if (mListeners.size() == 0) {
- mPolicyManager.unregisterListener(mPolicyListener);
- }
- }
- }
-
- public boolean isDataSaverEnabled() {
- return mPolicyManager.getRestrictBackground();
- }
-
- public void setDataSaverEnabled(boolean enabled) {
- mPolicyManager.setRestrictBackground(enabled);
- try {
- mPolicyListener.onRestrictBackgroundChanged(enabled);
- } catch (RemoteException e) {
- }
- }
-
- private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
- @Override
- public void onUidRulesChanged(int uid, int uidRules) throws RemoteException {
- }
-
- @Override
- public void onMeteredIfacesChanged(String[] strings) throws RemoteException {
- }
-
- @Override
- public void onRestrictBackgroundChanged(final boolean isDataSaving) throws RemoteException {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- handleRestrictBackgroundChanged(isDataSaving);
- }
- });
- }
-
- @Override
- public void onUidPoliciesChanged(int uid, int uidPolicies) throws RemoteException {
- }
- };
+ boolean isDataSaverEnabled();
+ void setDataSaverEnabled(boolean enabled);
public interface Listener {
void onDataSaverChanged(boolean isDataSaving);
}
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java
new file mode 100644
index 0000000..2951943
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 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.net.INetworkPolicyListener;
+import android.net.NetworkPolicyManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+
+import com.android.systemui.statusbar.policy.DataSaverController.Listener;
+
+import java.util.ArrayList;
+
+public class DataSaverControllerImpl implements DataSaverController {
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final ArrayList<Listener> mListeners = new ArrayList<>();
+ private final NetworkPolicyManager mPolicyManager;
+
+ public DataSaverControllerImpl(Context context) {
+ mPolicyManager = NetworkPolicyManager.from(context);
+ }
+
+ private void handleRestrictBackgroundChanged(boolean isDataSaving) {
+ synchronized (mListeners) {
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onDataSaverChanged(isDataSaving);
+ }
+ }
+ }
+
+ public void addCallback(Listener listener) {
+ synchronized (mListeners) {
+ mListeners.add(listener);
+ if (mListeners.size() == 1) {
+ mPolicyManager.registerListener(mPolicyListener);
+ }
+ }
+ listener.onDataSaverChanged(isDataSaverEnabled());
+ }
+
+ public void removeCallback(Listener listener) {
+ synchronized (mListeners) {
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ mPolicyManager.unregisterListener(mPolicyListener);
+ }
+ }
+ }
+
+ public boolean isDataSaverEnabled() {
+ return mPolicyManager.getRestrictBackground();
+ }
+
+ public void setDataSaverEnabled(boolean enabled) {
+ mPolicyManager.setRestrictBackground(enabled);
+ try {
+ mPolicyListener.onRestrictBackgroundChanged(enabled);
+ } catch (RemoteException e) {
+ }
+ }
+
+ private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
+ @Override
+ public void onUidRulesChanged(int uid, int uidRules) throws RemoteException {
+ }
+
+ @Override
+ public void onMeteredIfacesChanged(String[] strings) throws RemoteException {
+ }
+
+ @Override
+ public void onRestrictBackgroundChanged(final boolean isDataSaving) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ handleRestrictBackgroundChanged(isDataSaving);
+ }
+ });
+ }
+
+ @Override
+ public void onUidPoliciesChanged(int uid, int uidPolicies) throws RemoteException {
+ }
+ };
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index 0f77b03..6023f3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -1,255 +1,27 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 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
+ * 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
+ * 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.content.pm.PackageManager;
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraManager;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Process;
-import android.text.TextUtils;
-import android.util.Log;
-
import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
+public interface FlashlightController extends CallbackController<FlashlightListener> {
-/**
- * Manages the flashlight.
- */
-public class FlashlightController implements CallbackController<FlashlightListener> {
-
- private static final String TAG = "FlashlightController";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- private static final int DISPATCH_ERROR = 0;
- private static final int DISPATCH_CHANGED = 1;
- private static final int DISPATCH_AVAILABILITY_CHANGED = 2;
-
- private final CameraManager mCameraManager;
- private final Context mContext;
- /** Call {@link #ensureHandler()} before using */
- private Handler mHandler;
-
- /** Lock on mListeners when accessing */
- private final ArrayList<WeakReference<FlashlightListener>> mListeners = new ArrayList<>(1);
-
- /** Lock on {@code this} when accessing */
- private boolean mFlashlightEnabled;
-
- private String mCameraId;
- private boolean mTorchAvailable;
-
- public FlashlightController(Context context) {
- mContext = context;
- mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
-
- tryInitCamera();
- }
-
- private void tryInitCamera() {
- try {
- mCameraId = getCameraId();
- } catch (Throwable e) {
- Log.e(TAG, "Couldn't initialize.", e);
- return;
- }
-
- if (mCameraId != null) {
- ensureHandler();
- mCameraManager.registerTorchCallback(mTorchCallback, mHandler);
- }
- }
-
- public void setFlashlight(boolean enabled) {
- boolean pendingError = false;
- synchronized (this) {
- if (mCameraId == null) return;
- if (mFlashlightEnabled != enabled) {
- mFlashlightEnabled = enabled;
- try {
- mCameraManager.setTorchMode(mCameraId, enabled);
- } catch (CameraAccessException e) {
- Log.e(TAG, "Couldn't set torch mode", e);
- mFlashlightEnabled = false;
- pendingError = true;
- }
- }
- }
- dispatchModeChanged(mFlashlightEnabled);
- if (pendingError) {
- dispatchError();
- }
- }
-
- public boolean hasFlashlight() {
- return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
- }
-
- public synchronized boolean isEnabled() {
- return mFlashlightEnabled;
- }
-
- public synchronized boolean isAvailable() {
- return mTorchAvailable;
- }
-
- public void addCallback(FlashlightListener l) {
- synchronized (mListeners) {
- if (mCameraId == null) {
- tryInitCamera();
- }
- cleanUpListenersLocked(l);
- mListeners.add(new WeakReference<>(l));
- }
- }
-
- public void removeCallback(FlashlightListener l) {
- synchronized (mListeners) {
- cleanUpListenersLocked(l);
- }
- }
-
- private synchronized void ensureHandler() {
- if (mHandler == null) {
- HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
- thread.start();
- mHandler = new Handler(thread.getLooper());
- }
- }
-
- private String getCameraId() throws CameraAccessException {
- String[] ids = mCameraManager.getCameraIdList();
- for (String id : ids) {
- CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
- Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
- Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
- if (flashAvailable != null && flashAvailable
- && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
- return id;
- }
- }
- return null;
- }
-
- private void dispatchModeChanged(boolean enabled) {
- dispatchListeners(DISPATCH_CHANGED, enabled);
- }
-
- private void dispatchError() {
- dispatchListeners(DISPATCH_CHANGED, false /* argument (ignored) */);
- }
-
- private void dispatchAvailabilityChanged(boolean available) {
- dispatchListeners(DISPATCH_AVAILABILITY_CHANGED, available);
- }
-
- private void dispatchListeners(int message, boolean argument) {
- synchronized (mListeners) {
- final int N = mListeners.size();
- boolean cleanup = false;
- for (int i = 0; i < N; i++) {
- FlashlightListener l = mListeners.get(i).get();
- if (l != null) {
- if (message == DISPATCH_ERROR) {
- l.onFlashlightError();
- } else if (message == DISPATCH_CHANGED) {
- l.onFlashlightChanged(argument);
- } else if (message == DISPATCH_AVAILABILITY_CHANGED) {
- l.onFlashlightAvailabilityChanged(argument);
- }
- } else {
- cleanup = true;
- }
- }
- if (cleanup) {
- cleanUpListenersLocked(null);
- }
- }
- }
-
- private void cleanUpListenersLocked(FlashlightListener listener) {
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- FlashlightListener found = mListeners.get(i).get();
- if (found == null || found == listener) {
- mListeners.remove(i);
- }
- }
- }
-
- private final CameraManager.TorchCallback mTorchCallback =
- new CameraManager.TorchCallback() {
-
- @Override
- public void onTorchModeUnavailable(String cameraId) {
- if (TextUtils.equals(cameraId, mCameraId)) {
- setCameraAvailable(false);
- }
- }
-
- @Override
- public void onTorchModeChanged(String cameraId, boolean enabled) {
- if (TextUtils.equals(cameraId, mCameraId)) {
- setCameraAvailable(true);
- setTorchMode(enabled);
- }
- }
-
- private void setCameraAvailable(boolean available) {
- boolean changed;
- synchronized (FlashlightController.this) {
- changed = mTorchAvailable != available;
- mTorchAvailable = available;
- }
- if (changed) {
- if (DEBUG) Log.d(TAG, "dispatchAvailabilityChanged(" + available + ")");
- dispatchAvailabilityChanged(available);
- }
- }
-
- private void setTorchMode(boolean enabled) {
- boolean changed;
- synchronized (FlashlightController.this) {
- changed = mFlashlightEnabled != enabled;
- mFlashlightEnabled = enabled;
- }
- if (changed) {
- if (DEBUG) Log.d(TAG, "dispatchModeChanged(" + enabled + ")");
- dispatchModeChanged(enabled);
- }
- }
- };
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("FlashlightController state:");
-
- pw.print(" mCameraId=");
- pw.println(mCameraId);
- pw.print(" mFlashlightEnabled=");
- pw.println(mFlashlightEnabled);
- pw.print(" mTorchAvailable=");
- pw.println(mTorchAvailable);
- }
+ boolean hasFlashlight();
+ void setFlashlight(boolean newState);
+ boolean isAvailable();
+ boolean isEnabled();
public interface FlashlightListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
new file mode 100644
index 0000000..008d837
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2014 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.content.pm.PackageManager;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * Manages the flashlight.
+ */
+public class FlashlightControllerImpl implements FlashlightController {
+
+ private static final String TAG = "FlashlightController";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final int DISPATCH_ERROR = 0;
+ private static final int DISPATCH_CHANGED = 1;
+ private static final int DISPATCH_AVAILABILITY_CHANGED = 2;
+
+ private final CameraManager mCameraManager;
+ private final Context mContext;
+ /** Call {@link #ensureHandler()} before using */
+ private Handler mHandler;
+
+ /** Lock on mListeners when accessing */
+ private final ArrayList<WeakReference<FlashlightListener>> mListeners = new ArrayList<>(1);
+
+ /** Lock on {@code this} when accessing */
+ private boolean mFlashlightEnabled;
+
+ private String mCameraId;
+ private boolean mTorchAvailable;
+
+ public FlashlightControllerImpl(Context context) {
+ mContext = context;
+ mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+
+ tryInitCamera();
+ }
+
+ private void tryInitCamera() {
+ try {
+ mCameraId = getCameraId();
+ } catch (Throwable e) {
+ Log.e(TAG, "Couldn't initialize.", e);
+ return;
+ }
+
+ if (mCameraId != null) {
+ ensureHandler();
+ mCameraManager.registerTorchCallback(mTorchCallback, mHandler);
+ }
+ }
+
+ public void setFlashlight(boolean enabled) {
+ boolean pendingError = false;
+ synchronized (this) {
+ if (mCameraId == null) return;
+ if (mFlashlightEnabled != enabled) {
+ mFlashlightEnabled = enabled;
+ try {
+ mCameraManager.setTorchMode(mCameraId, enabled);
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Couldn't set torch mode", e);
+ mFlashlightEnabled = false;
+ pendingError = true;
+ }
+ }
+ }
+ dispatchModeChanged(mFlashlightEnabled);
+ if (pendingError) {
+ dispatchError();
+ }
+ }
+
+ public boolean hasFlashlight() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
+ }
+
+ public synchronized boolean isEnabled() {
+ return mFlashlightEnabled;
+ }
+
+ public synchronized boolean isAvailable() {
+ return mTorchAvailable;
+ }
+
+ public void addCallback(FlashlightListener l) {
+ synchronized (mListeners) {
+ if (mCameraId == null) {
+ tryInitCamera();
+ }
+ cleanUpListenersLocked(l);
+ mListeners.add(new WeakReference<>(l));
+ }
+ }
+
+ public void removeCallback(FlashlightListener l) {
+ synchronized (mListeners) {
+ cleanUpListenersLocked(l);
+ }
+ }
+
+ private synchronized void ensureHandler() {
+ if (mHandler == null) {
+ HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ mHandler = new Handler(thread.getLooper());
+ }
+ }
+
+ private String getCameraId() throws CameraAccessException {
+ String[] ids = mCameraManager.getCameraIdList();
+ for (String id : ids) {
+ CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
+ Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
+ Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
+ if (flashAvailable != null && flashAvailable
+ && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
+ return id;
+ }
+ }
+ return null;
+ }
+
+ private void dispatchModeChanged(boolean enabled) {
+ dispatchListeners(DISPATCH_CHANGED, enabled);
+ }
+
+ private void dispatchError() {
+ dispatchListeners(DISPATCH_CHANGED, false /* argument (ignored) */);
+ }
+
+ private void dispatchAvailabilityChanged(boolean available) {
+ dispatchListeners(DISPATCH_AVAILABILITY_CHANGED, available);
+ }
+
+ private void dispatchListeners(int message, boolean argument) {
+ synchronized (mListeners) {
+ final int N = mListeners.size();
+ boolean cleanup = false;
+ for (int i = 0; i < N; i++) {
+ FlashlightListener l = mListeners.get(i).get();
+ if (l != null) {
+ if (message == DISPATCH_ERROR) {
+ l.onFlashlightError();
+ } else if (message == DISPATCH_CHANGED) {
+ l.onFlashlightChanged(argument);
+ } else if (message == DISPATCH_AVAILABILITY_CHANGED) {
+ l.onFlashlightAvailabilityChanged(argument);
+ }
+ } else {
+ cleanup = true;
+ }
+ }
+ if (cleanup) {
+ cleanUpListenersLocked(null);
+ }
+ }
+ }
+
+ private void cleanUpListenersLocked(FlashlightListener listener) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ FlashlightListener found = mListeners.get(i).get();
+ if (found == null || found == listener) {
+ mListeners.remove(i);
+ }
+ }
+ }
+
+ private final CameraManager.TorchCallback mTorchCallback =
+ new CameraManager.TorchCallback() {
+
+ @Override
+ public void onTorchModeUnavailable(String cameraId) {
+ if (TextUtils.equals(cameraId, mCameraId)) {
+ setCameraAvailable(false);
+ }
+ }
+
+ @Override
+ public void onTorchModeChanged(String cameraId, boolean enabled) {
+ if (TextUtils.equals(cameraId, mCameraId)) {
+ setCameraAvailable(true);
+ setTorchMode(enabled);
+ }
+ }
+
+ private void setCameraAvailable(boolean available) {
+ boolean changed;
+ synchronized (FlashlightControllerImpl.this) {
+ changed = mTorchAvailable != available;
+ mTorchAvailable = available;
+ }
+ if (changed) {
+ if (DEBUG) Log.d(TAG, "dispatchAvailabilityChanged(" + available + ")");
+ dispatchAvailabilityChanged(available);
+ }
+ }
+
+ private void setTorchMode(boolean enabled) {
+ boolean changed;
+ synchronized (FlashlightControllerImpl.this) {
+ changed = mFlashlightEnabled != enabled;
+ mFlashlightEnabled = enabled;
+ }
+ if (changed) {
+ if (DEBUG) Log.d(TAG, "dispatchModeChanged(" + enabled + ")");
+ dispatchModeChanged(enabled);
+ }
+ }
+ };
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("FlashlightController state:");
+
+ pw.print(" mCameraId=");
+ pw.println(mCameraId);
+ pw.print(" mFlashlightEnabled=");
+ pw.println(mFlashlightEnabled);
+ pw.print(" mTorchAvailable=");
+ pw.println(mTorchAvailable);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
index 0396613..de47267 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
@@ -1,126 +1,28 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 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
+ * 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.
+ * 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.app.ActivityManager;
-import android.content.Context;
-import android.os.RemoteException;
-import android.view.WindowManagerGlobal;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback;
-import java.util.ArrayList;
+public interface KeyguardMonitor extends CallbackController<Callback> {
-public class KeyguardMonitor extends KeyguardUpdateMonitorCallback
- implements CallbackController<Callback> {
-
- private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
-
- private final Context mContext;
- private final CurrentUserTracker mUserTracker;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-
- private int mCurrentUser;
- private boolean mShowing;
- private boolean mSecure;
- private boolean mOccluded;
- private boolean mCanSkipBouncer;
-
- private boolean mListening;
-
- public KeyguardMonitor(Context context) {
- mContext = context;
- mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
- mUserTracker = new CurrentUserTracker(mContext) {
- @Override
- public void onUserSwitched(int newUserId) {
- mCurrentUser = newUserId;
- updateCanSkipBouncerState();
- }
- };
- }
-
- public void addCallback(Callback callback) {
- mCallbacks.add(callback);
- if (mCallbacks.size() != 0 && !mListening) {
- mListening = true;
- mCurrentUser = ActivityManager.getCurrentUser();
- updateCanSkipBouncerState();
- mKeyguardUpdateMonitor.registerCallback(this);
- mUserTracker.startTracking();
- }
- }
-
- public void removeCallback(Callback callback) {
- if (mCallbacks.remove(callback) && mCallbacks.size() == 0 && mListening) {
- mListening = false;
- mKeyguardUpdateMonitor.removeCallback(this);
- mUserTracker.stopTracking();
- }
- }
-
- public boolean isShowing() {
- return mShowing;
- }
-
- public boolean isSecure() {
- return mSecure;
- }
-
- public boolean isOccluded() {
- return mOccluded;
- }
-
- public boolean canSkipBouncer() {
- return mCanSkipBouncer;
- }
-
- public void notifyKeyguardState(boolean showing, boolean secure, boolean occluded) {
- if (mShowing == showing && mSecure == secure && mOccluded == occluded) return;
- mShowing = showing;
- mSecure = secure;
- mOccluded = occluded;
- notifyKeyguardChanged();
- }
-
- @Override
- public void onTrustChanged(int userId) {
- updateCanSkipBouncerState();
- notifyKeyguardChanged();
- }
-
- public boolean isDeviceInteractive() {
- return mKeyguardUpdateMonitor.isDeviceInteractive();
- }
-
- private void updateCanSkipBouncerState() {
- mCanSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(mCurrentUser);
- }
-
- private void notifyKeyguardChanged() {
- for (Callback callback : mCallbacks) {
- callback.onKeyguardChanged();
- }
- }
+ boolean isSecure();
+ boolean canSkipBouncer();
+ boolean isShowing();
public interface Callback {
void onKeyguardChanged();
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
new file mode 100644
index 0000000..769f93f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 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.app.ActivityManager;
+import android.content.Context;
+import android.os.RemoteException;
+import android.view.WindowManagerGlobal;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback;
+
+import java.util.ArrayList;
+
+public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback
+ implements KeyguardMonitor {
+
+ private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+
+ private final Context mContext;
+ private final CurrentUserTracker mUserTracker;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+ private int mCurrentUser;
+ private boolean mShowing;
+ private boolean mSecure;
+ private boolean mOccluded;
+ private boolean mCanSkipBouncer;
+
+ private boolean mListening;
+
+ public KeyguardMonitorImpl(Context context) {
+ mContext = context;
+ mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+ mUserTracker = new CurrentUserTracker(mContext) {
+ @Override
+ public void onUserSwitched(int newUserId) {
+ mCurrentUser = newUserId;
+ updateCanSkipBouncerState();
+ }
+ };
+ }
+
+ public void addCallback(Callback callback) {
+ mCallbacks.add(callback);
+ if (mCallbacks.size() != 0 && !mListening) {
+ mListening = true;
+ mCurrentUser = ActivityManager.getCurrentUser();
+ updateCanSkipBouncerState();
+ mKeyguardUpdateMonitor.registerCallback(this);
+ mUserTracker.startTracking();
+ }
+ }
+
+ public void removeCallback(Callback callback) {
+ if (mCallbacks.remove(callback) && mCallbacks.size() == 0 && mListening) {
+ mListening = false;
+ mKeyguardUpdateMonitor.removeCallback(this);
+ mUserTracker.stopTracking();
+ }
+ }
+
+ public boolean isShowing() {
+ return mShowing;
+ }
+
+ public boolean isSecure() {
+ return mSecure;
+ }
+
+ public boolean isOccluded() {
+ return mOccluded;
+ }
+
+ public boolean canSkipBouncer() {
+ return mCanSkipBouncer;
+ }
+
+ public void notifyKeyguardState(boolean showing, boolean secure, boolean occluded) {
+ if (mShowing == showing && mSecure == secure && mOccluded == occluded) return;
+ mShowing = showing;
+ mSecure = secure;
+ mOccluded = occluded;
+ notifyKeyguardChanged();
+ }
+
+ @Override
+ public void onTrustChanged(int userId) {
+ updateCanSkipBouncerState();
+ notifyKeyguardChanged();
+ }
+
+ public boolean isDeviceInteractive() {
+ return mKeyguardUpdateMonitor.isDeviceInteractive();
+ }
+
+ private void updateCanSkipBouncerState() {
+ mCanSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(mCurrentUser);
+ }
+
+ private void notifyKeyguardChanged() {
+ for (Callback callback : mCallbacks) {
+ callback.onKeyguardChanged();
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 1a9756f..a7fab41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -159,7 +159,7 @@
mConfig = config;
mReceiverHandler = new Handler(bgLooper);
mCallbackHandler = callbackHandler;
- mDataSaverController = new DataSaverController(context);
+ mDataSaverController = new DataSaverControllerImpl(context);
mSubscriptionManager = subManager;
mSubDefaults = defaultsHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java
index 28935bf..e5b0c03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java
@@ -1,84 +1,24 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 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
+ * 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
+ * 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.app.AlarmManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.UserHandle;
import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-public class NextAlarmController extends BroadcastReceiver
- implements CallbackController<NextAlarmChangeCallback> {
-
- private final ArrayList<NextAlarmChangeCallback> mChangeCallbacks = new ArrayList<>();
-
- private AlarmManager mAlarmManager;
- private AlarmManager.AlarmClockInfo mNextAlarm;
-
- public NextAlarmController(Context context) {
- mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
- context.registerReceiverAsUser(this, UserHandle.ALL, filter, null, null);
- updateNextAlarm();
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("NextAlarmController state:");
- pw.print(" mNextAlarm="); pw.println(mNextAlarm);
- }
-
- public void addCallback(NextAlarmChangeCallback cb) {
- mChangeCallbacks.add(cb);
- cb.onNextAlarmChanged(mNextAlarm);
- }
-
- public void removeCallback(NextAlarmChangeCallback cb) {
- mChangeCallbacks.remove(cb);
- }
-
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (action.equals(Intent.ACTION_USER_SWITCHED)
- || action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
- updateNextAlarm();
- }
- }
-
- private void updateNextAlarm() {
- mNextAlarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
- fireNextAlarmChanged();
- }
-
- private void fireNextAlarmChanged() {
- int n = mChangeCallbacks.size();
- for (int i = 0; i < n; i++) {
- mChangeCallbacks.get(i).onNextAlarmChanged(mNextAlarm);
- }
- }
+public interface NextAlarmController extends CallbackController<NextAlarmChangeCallback> {
public interface NextAlarmChangeCallback {
void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
new file mode 100644
index 0000000..dfdeae1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 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.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public class NextAlarmControllerImpl extends BroadcastReceiver
+ implements NextAlarmController {
+
+ private final ArrayList<NextAlarmChangeCallback> mChangeCallbacks = new ArrayList<>();
+
+ private AlarmManager mAlarmManager;
+ private AlarmManager.AlarmClockInfo mNextAlarm;
+
+ public NextAlarmControllerImpl(Context context) {
+ mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
+ context.registerReceiverAsUser(this, UserHandle.ALL, filter, null, null);
+ updateNextAlarm();
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("NextAlarmController state:");
+ pw.print(" mNextAlarm="); pw.println(mNextAlarm);
+ }
+
+ public void addCallback(NextAlarmChangeCallback cb) {
+ mChangeCallbacks.add(cb);
+ cb.onNextAlarmChanged(mNextAlarm);
+ }
+
+ public void removeCallback(NextAlarmChangeCallback cb) {
+ mChangeCallbacks.remove(cb);
+ }
+
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(Intent.ACTION_USER_SWITCHED)
+ || action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
+ updateNextAlarm();
+ }
+ }
+
+ private void updateNextAlarm() {
+ mNextAlarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
+ fireNextAlarmChanged();
+ }
+
+ private void fireNextAlarmChanged() {
+ int n = mChangeCallbacks.size();
+ for (int i = 0; i < n; i++) {
+ mChangeCallbacks.get(i).onNextAlarmChanged(mNextAlarm);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
index c09747b..1e23a20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -1,233 +1,28 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 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
+ * 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
+ * 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.app.ActivityManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.ContactsContract;
-import android.util.Log;
-import com.android.internal.util.UserIcons;
-import com.android.settingslib.drawable.UserIconDrawable;
-import com.android.systemui.R;
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
-import java.util.ArrayList;
+public interface UserInfoController extends CallbackController<OnUserInfoChangedListener> {
-public class UserInfoController implements CallbackController<OnUserInfoChangedListener> {
-
- private static final String TAG = "UserInfoController";
-
- private final Context mContext;
- private final ArrayList<OnUserInfoChangedListener> mCallbacks =
- new ArrayList<OnUserInfoChangedListener>();
- private AsyncTask<Void, Void, UserInfoQueryResult> mUserInfoTask;
-
- private String mUserName;
- private Drawable mUserDrawable;
- private String mUserAccount;
-
- public UserInfoController(Context context) {
- mContext = context;
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(mReceiver, filter);
-
- IntentFilter profileFilter = new IntentFilter();
- profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED);
- profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
- mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter,
- null, null);
- }
-
- public void addCallback(OnUserInfoChangedListener callback) {
- mCallbacks.add(callback);
- callback.onUserInfoChanged(mUserName, mUserDrawable, mUserAccount);
- }
-
- public void removeCallback(OnUserInfoChangedListener callback) {
- mCallbacks.remove(callback);
- }
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- reloadUserInfo();
- }
- }
- };
-
- private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) ||
- Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
- try {
- final int currentUser = ActivityManager.getService().getCurrentUser().id;
- final int changedUser =
- intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
- if (changedUser == currentUser) {
- reloadUserInfo();
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't get current user id for profile change", e);
- }
- }
- }
- };
-
- public void reloadUserInfo() {
- if (mUserInfoTask != null) {
- mUserInfoTask.cancel(false);
- mUserInfoTask = null;
- }
- queryForUserInformation();
- }
-
- private void queryForUserInformation() {
- Context currentUserContext;
- UserInfo userInfo;
- try {
- userInfo = ActivityManager.getService().getCurrentUser();
- currentUserContext = mContext.createPackageContextAsUser("android", 0,
- new UserHandle(userInfo.id));
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Couldn't create user context", e);
- throw new RuntimeException(e);
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't get user info", e);
- throw new RuntimeException(e);
- }
- final int userId = userInfo.id;
- final boolean isGuest = userInfo.isGuest();
- final String userName = userInfo.name;
-
- final Resources res = mContext.getResources();
- final int avatarSize = Math.max(
- res.getDimensionPixelSize(R.dimen.multi_user_avatar_expanded_size),
- res.getDimensionPixelSize(R.dimen.multi_user_avatar_keyguard_size));
-
- final Context context = currentUserContext;
- mUserInfoTask = new AsyncTask<Void, Void, UserInfoQueryResult>() {
-
- @Override
- protected UserInfoQueryResult doInBackground(Void... params) {
- final UserManager um = UserManager.get(mContext);
-
- // Fall back to the UserManager nickname if we can't read the name from the local
- // profile below.
- String name = userName;
- Drawable avatar = null;
- Bitmap rawAvatar = um.getUserIcon(userId);
- if (rawAvatar != null) {
- avatar = new UserIconDrawable(avatarSize)
- .setIcon(rawAvatar).setBadgeIfManagedUser(mContext, userId).bake();
- } else {
- avatar = UserIcons.getDefaultUserIcon(isGuest? UserHandle.USER_NULL : userId,
- /* light= */ true);
- }
-
- // If it's a single-user device, get the profile name, since the nickname is not
- // usually valid
- if (um.getUsers().size() <= 1) {
- // Try and read the display name from the local profile
- final Cursor cursor = context.getContentResolver().query(
- ContactsContract.Profile.CONTENT_URI, new String[] {
- ContactsContract.CommonDataKinds.Phone._ID,
- ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
- }, null, null, null);
- if (cursor != null) {
- try {
- if (cursor.moveToFirst()) {
- name = cursor.getString(cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
- }
- } finally {
- cursor.close();
- }
- }
- }
- String userAccount = um.getUserAccount(userId);
- return new UserInfoQueryResult(name, avatar, userAccount);
- }
-
- @Override
- protected void onPostExecute(UserInfoQueryResult result) {
- mUserName = result.getName();
- mUserDrawable = result.getAvatar();
- mUserAccount = result.getUserAccount();
- mUserInfoTask = null;
- notifyChanged();
- }
- };
- mUserInfoTask.execute();
- }
-
- private void notifyChanged() {
- for (OnUserInfoChangedListener listener : mCallbacks) {
- listener.onUserInfoChanged(mUserName, mUserDrawable, mUserAccount);
- }
- }
-
- public void onDensityOrFontScaleChanged() {
- reloadUserInfo();
- }
+ void reloadUserInfo();
public interface OnUserInfoChangedListener {
public void onUserInfoChanged(String name, Drawable picture, String userAccount);
}
-
- private static class UserInfoQueryResult {
- private String mName;
- private Drawable mAvatar;
- private String mUserAccount;
-
- public UserInfoQueryResult(String name, Drawable avatar, String userAccount) {
- mName = name;
- mAvatar = avatar;
- mUserAccount = userAccount;
- }
-
- public String getName() {
- return mName;
- }
-
- public Drawable getAvatar() {
- return mAvatar;
- }
-
- public String getUserAccount() {
- return mUserAccount;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
new file mode 100644
index 0000000..b1e4b03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2014 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.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.ContactsContract;
+import android.util.Log;
+
+import com.android.internal.util.UserIcons;
+import com.android.settingslib.drawable.UserIconDrawable;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
+
+import java.util.ArrayList;
+
+public class UserInfoControllerImpl implements UserInfoController {
+
+ private static final String TAG = "UserInfoController";
+
+ private final Context mContext;
+ private final ArrayList<OnUserInfoChangedListener> mCallbacks =
+ new ArrayList<OnUserInfoChangedListener>();
+ private AsyncTask<Void, Void, UserInfoQueryResult> mUserInfoTask;
+
+ private String mUserName;
+ private Drawable mUserDrawable;
+ private String mUserAccount;
+
+ public UserInfoControllerImpl(Context context) {
+ mContext = context;
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ mContext.registerReceiver(mReceiver, filter);
+
+ IntentFilter profileFilter = new IntentFilter();
+ profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED);
+ profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
+ mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter,
+ null, null);
+ }
+
+ public void addCallback(OnUserInfoChangedListener callback) {
+ mCallbacks.add(callback);
+ callback.onUserInfoChanged(mUserName, mUserDrawable, mUserAccount);
+ }
+
+ public void removeCallback(OnUserInfoChangedListener callback) {
+ mCallbacks.remove(callback);
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ reloadUserInfo();
+ }
+ }
+ };
+
+ private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) ||
+ Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
+ try {
+ final int currentUser = ActivityManager.getService().getCurrentUser().id;
+ final int changedUser =
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
+ if (changedUser == currentUser) {
+ reloadUserInfo();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't get current user id for profile change", e);
+ }
+ }
+ }
+ };
+
+ public void reloadUserInfo() {
+ if (mUserInfoTask != null) {
+ mUserInfoTask.cancel(false);
+ mUserInfoTask = null;
+ }
+ queryForUserInformation();
+ }
+
+ private void queryForUserInformation() {
+ Context currentUserContext;
+ UserInfo userInfo;
+ try {
+ userInfo = ActivityManager.getService().getCurrentUser();
+ currentUserContext = mContext.createPackageContextAsUser("android", 0,
+ new UserHandle(userInfo.id));
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Couldn't create user context", e);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't get user info", e);
+ throw new RuntimeException(e);
+ }
+ final int userId = userInfo.id;
+ final boolean isGuest = userInfo.isGuest();
+ final String userName = userInfo.name;
+
+ final Resources res = mContext.getResources();
+ final int avatarSize = Math.max(
+ res.getDimensionPixelSize(R.dimen.multi_user_avatar_expanded_size),
+ res.getDimensionPixelSize(R.dimen.multi_user_avatar_keyguard_size));
+
+ final Context context = currentUserContext;
+ mUserInfoTask = new AsyncTask<Void, Void, UserInfoQueryResult>() {
+
+ @Override
+ protected UserInfoQueryResult doInBackground(Void... params) {
+ final UserManager um = UserManager.get(mContext);
+
+ // Fall back to the UserManager nickname if we can't read the name from the local
+ // profile below.
+ String name = userName;
+ Drawable avatar = null;
+ Bitmap rawAvatar = um.getUserIcon(userId);
+ if (rawAvatar != null) {
+ avatar = new UserIconDrawable(avatarSize)
+ .setIcon(rawAvatar).setBadgeIfManagedUser(mContext, userId).bake();
+ } else {
+ avatar = UserIcons.getDefaultUserIcon(isGuest? UserHandle.USER_NULL : userId,
+ /* light= */ true);
+ }
+
+ // If it's a single-user device, get the profile name, since the nickname is not
+ // usually valid
+ if (um.getUsers().size() <= 1) {
+ // Try and read the display name from the local profile
+ final Cursor cursor = context.getContentResolver().query(
+ ContactsContract.Profile.CONTENT_URI, new String[] {
+ ContactsContract.CommonDataKinds.Phone._ID,
+ ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
+ }, null, null, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ name = cursor.getString(cursor.getColumnIndex(
+ ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+ String userAccount = um.getUserAccount(userId);
+ return new UserInfoQueryResult(name, avatar, userAccount);
+ }
+
+ @Override
+ protected void onPostExecute(UserInfoQueryResult result) {
+ mUserName = result.getName();
+ mUserDrawable = result.getAvatar();
+ mUserAccount = result.getUserAccount();
+ mUserInfoTask = null;
+ notifyChanged();
+ }
+ };
+ mUserInfoTask.execute();
+ }
+
+ private void notifyChanged() {
+ for (OnUserInfoChangedListener listener : mCallbacks) {
+ listener.onUserInfoChanged(mUserName, mUserDrawable, mUserAccount);
+ }
+ }
+
+ public void onDensityOrFontScaleChanged() {
+ reloadUserInfo();
+ }
+
+ private static class UserInfoQueryResult {
+ private String mName;
+ private Drawable mAvatar;
+ private String mUserAccount;
+
+ public UserInfoQueryResult(String name, Drawable avatar, String userAccount) {
+ mName = name;
+ mAvatar = avatar;
+ mUserAccount = userAccount;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public Drawable getAvatar() {
+ return mAvatar;
+ }
+
+ public String getUserAccount() {
+ return mUserAccount;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index ebc962d..565ac08 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -92,6 +92,10 @@
mUserTracker.startTracking();
}
+ public void destroy() {
+ mUserTracker.stopTracking();
+ }
+
private void upgradeTuner(int oldVersion, int newVersion) {
if (oldVersion < 1) {
String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java
index f87336c..447edac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java
@@ -26,6 +26,8 @@
import android.view.View;
import android.widget.FrameLayout;
+import com.android.systemui.utils.leaks.LeakCheckedTest;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -139,7 +141,7 @@
private class HostCallbacks extends FragmentHostCallback<FragmentTestCase> {
public HostCallbacks() {
- super(getTrackedContext(), FragmentTestCase.this.mHandler, 0);
+ super(mContext, FragmentTestCase.this.mHandler, 0);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/LeakCheckedTest.java
deleted file mode 100644
index d64669d..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/LeakCheckedTest.java
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (C) 2016 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;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.statusbar.policy.CallbackController;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Base class for tests to check if receivers are left registered, services bound, or other
- * listeners listening.
- */
-public class LeakCheckedTest extends SysuiTestCase {
- private static final String TAG = "LeakCheckedTest";
-
- private final Map<String, Tracker> mTrackers = new HashMap<>();
- private final Map<Class, Object> mLeakCheckers = new ArrayMap<>();
- private TrackingContext mTrackedContext;
-
- @Rule
- public TestWatcher successWatcher = new TestWatcher() {
- @Override
- protected void succeeded(Description description) {
- verify();
- }
- };
-
- @Before
- public void setup() {
- mTrackedContext = new TrackingContext(mContext);
- addSupportedLeakCheckers();
- }
-
- public <T> T getLeakChecker(Class<T> cls) {
- T obj = (T) mLeakCheckers.get(cls);
- if (obj == null) {
- Assert.fail(cls.getName() + " is not supported by LeakCheckedTest yet");
- }
- return obj;
- }
-
- public Context getTrackedContext() {
- return mTrackedContext;
- }
-
- private Tracker getTracker(String tag) {
- Tracker t = mTrackers.get(tag);
- if (t == null) {
- t = new Tracker();
- mTrackers.put(tag, t);
- }
- return t;
- }
-
- public void verify() {
- mTrackers.values().forEach(Tracker::verify);
- }
-
- public static class Tracker {
- private Map<Object, LeakInfo> mObjects = new ArrayMap<>();
-
- LeakInfo getLeakInfo(Object object) {
- LeakInfo leakInfo = mObjects.get(object);
- if (leakInfo == null) {
- leakInfo = new LeakInfo();
- mObjects.put(object, leakInfo);
- }
- return leakInfo;
- }
-
- private void verify() {
- mObjects.values().forEach(LeakInfo::verify);
- }
- }
-
- public static class LeakInfo {
- private List<Throwable> mThrowables = new ArrayList<>();
-
- private LeakInfo() {
- }
-
- private void addAllocation(Throwable t) {
- // TODO: Drop off the first element in the stack trace here to have a cleaner stack.
- mThrowables.add(t);
- }
-
- private void clearAllocations() {
- mThrowables.clear();
- }
-
- public void verify() {
- if (mThrowables.size() == 0) return;
- Log.e(TAG, "Listener or binding not properly released");
- for (Throwable t : mThrowables) {
- Log.e(TAG, "Allocation found", t);
- }
- StringWriter writer = new StringWriter();
- mThrowables.get(0).printStackTrace(new PrintWriter(writer));
- Assert.fail("Listener or binding not properly released\n"
- + writer.toString());
- }
- }
-
- private void addSupportedLeakCheckers() {
- addListening("bluetooth", BluetoothController.class);
- addListening("location", LocationController.class);
- addListening("rotation", RotationLockController.class);
- addListening("zen", ZenModeController.class);
- addListening("cast", CastController.class);
- addListening("hotspot", HotspotController.class);
- addListening("flashlight", FlashlightController.class);
- addListening("user", UserInfoController.class);
- addListening("keyguard", KeyguardMonitor.class);
- addListening("battery", BatteryController.class);
- addListening("security", SecurityController.class);
- addListening("profile", ManagedProfileController.class);
- addListening("alarm", NextAlarmController.class);
- NetworkController network = addListening("network", NetworkController.class);
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- getTracker("emergency").getLeakInfo(invocation.getArguments()[0])
- .addAllocation(new Throwable());
- return null;
- }
- }).when(network).addEmergencyListener(any());
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- getTracker("emergency").getLeakInfo(invocation.getArguments()[0]).clearAllocations();
- return null;
- }
- }).when(network).removeEmergencyListener(any());
- DataSaverController datasaver = addListening("datasaver", DataSaverController.class);
- when(network.getDataSaverController()).thenReturn(datasaver);
- }
-
- private <T extends CallbackController> T addListening(final String tag, Class<T> cls) {
- T mock = mock(cls);
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- getTracker(tag).getLeakInfo(invocation.getArguments()[0])
- .addAllocation(new Throwable());
- return null;
- }
- }).when(mock).addCallback(any());
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- getTracker(tag).getLeakInfo(invocation.getArguments()[0]).clearAllocations();
- return null;
- }
- }).when(mock).removeCallback(any());
- mLeakCheckers.put(cls, mock);
- return mock;
- }
-
- class TrackingContext extends ContextWrapper {
- public TrackingContext(Context base) {
- super(base);
- }
-
- @Override
- public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
- getTracker("receiver").getLeakInfo(receiver).addAllocation(new Throwable());
- return super.registerReceiver(receiver, filter);
- }
-
- @Override
- public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
- String broadcastPermission, Handler scheduler) {
- getTracker("receiver").getLeakInfo(receiver).addAllocation(new Throwable());
- return super.registerReceiver(receiver, filter, broadcastPermission, scheduler);
- }
-
- @Override
- public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
- IntentFilter filter, String broadcastPermission, Handler scheduler) {
- getTracker("receiver").getLeakInfo(receiver).addAllocation(new Throwable());
- return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
- scheduler);
- }
-
- @Override
- public void unregisterReceiver(BroadcastReceiver receiver) {
- getTracker("receiver").getLeakInfo(receiver).clearAllocations();
- super.unregisterReceiver(receiver);
- }
-
- @Override
- public boolean bindService(Intent service, ServiceConnection conn, int flags) {
- getTracker("service").getLeakInfo(conn).addAllocation(new Throwable());
- return super.bindService(service, conn, flags);
- }
-
- @Override
- public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
- Handler handler, UserHandle user) {
- getTracker("service").getLeakInfo(conn).addAllocation(new Throwable());
- return super.bindServiceAsUser(service, conn, flags, handler, user);
- }
-
- @Override
- public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
- UserHandle user) {
- getTracker("service").getLeakInfo(conn).addAllocation(new Throwable());
- return super.bindServiceAsUser(service, conn, flags, user);
- }
-
- @Override
- public void unbindService(ServiceConnection conn) {
- getTracker("service").getLeakInfo(conn).clearAllocations();
- super.unbindService(conn);
- }
-
- @Override
- public void registerComponentCallbacks(ComponentCallbacks callback) {
- getTracker("component").getLeakInfo(callback).addAllocation(new Throwable());
- super.registerComponentCallbacks(callback);
- }
-
- @Override
- public void unregisterComponentCallbacks(ComponentCallbacks callback) {
- getTracker("component").getLeakInfo(callback).clearAllocations();
- super.unregisterComponentCallbacks(callback);
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 5dac8e5..008580a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -22,6 +22,7 @@
import android.os.MessageQueue;
import com.android.systemui.utils.TestableContext;
+import com.android.systemui.utils.leaks.Tracker;
import org.junit.After;
import org.junit.Before;
@@ -29,14 +30,14 @@
/**
* Base class that does System UI specific setup.
*/
-public class SysuiTestCase {
+public abstract class SysuiTestCase {
private Handler mHandler;
protected TestableContext mContext;
@Before
public void SysuiSetup() throws Exception {
- mContext = new TestableContext(InstrumentationRegistry.getTargetContext());
+ mContext = new TestableContext(InstrumentationRegistry.getTargetContext(), this);
}
@After
@@ -71,6 +72,11 @@
}
}
+ // Used for leak tracking, returns null to indicate no leak tracking by default.
+ public Tracker getTracker(String tag) {
+ return null;
+ }
+
public static final class EmptyRunnable implements Runnable {
public void run() {
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 6ceaead..1973b54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -38,6 +38,7 @@
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.tuner.TunerService;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -59,7 +60,7 @@
KeyguardMonitor keyguardMonitor = getLeakChecker(KeyguardMonitor.class);
when(userSwitcher.getKeyguardMonitor()).thenReturn(keyguardMonitor);
when(userSwitcher.getUsers()).thenReturn(new ArrayList<>());
- QSTileHost host = new QSTileHost(getTrackedContext(),
+ QSTileHost host = new QSTileHost(mContext,
mock(PhoneStatusBar.class),
getLeakChecker(BluetoothController.class),
getLeakChecker(LocationController.class),
@@ -86,5 +87,7 @@
waitForIdleSync(h);
host.destroy();
+ // Ensure the tuner cleans up its persistent listeners.
+ TunerService.get(mContext).destroy();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
index 5179823..bf73416 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
@@ -14,18 +14,31 @@
package com.android.systemui.utils;
+import android.content.BroadcastReceiver;
+import android.content.ComponentCallbacks;
import android.content.ContentProviderClient;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.UserHandle;
import android.provider.Settings;
+import com.android.systemui.utils.leaks.Tracker;
+import com.android.systemui.SysuiTestCase;
+
public class TestableContext extends ContextWrapper {
private final FakeContentResolver mFakeContentResolver;
private final FakeSettingsProvider mSettingsProvider;
- public TestableContext(Context base) {
+ private Tracker mReceiver;
+ private Tracker mService;
+ private Tracker mComponent;
+
+ public TestableContext(Context base, SysuiTestCase test) {
super(base);
mFakeContentResolver = new FakeContentResolver(base);
ContentProviderClient settings = base.getContentResolver()
@@ -33,6 +46,9 @@
mSettingsProvider = FakeSettingsProvider.getFakeSettingsProvider(settings,
mFakeContentResolver);
mFakeContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider);
+ mReceiver = test.getTracker("receiver");
+ mService = test.getTracker("service");
+ mComponent = test.getTracker("component");
}
public FakeSettingsProvider getSettingsProvider() {
@@ -49,4 +65,69 @@
// Return this so its always a TestableContext.
return this;
}
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
+ return super.registerReceiver(receiver, filter);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler) {
+ if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
+ return super.registerReceiver(receiver, filter, broadcastPermission, scheduler);
+ }
+
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
+ return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
+ scheduler);
+ }
+
+ @Override
+ public void unregisterReceiver(BroadcastReceiver receiver) {
+ if (mReceiver != null) mReceiver.getLeakInfo(receiver).clearAllocations();
+ super.unregisterReceiver(receiver);
+ }
+
+ @Override
+ public boolean bindService(Intent service, ServiceConnection conn, int flags) {
+ if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
+ return super.bindService(service, conn, flags);
+ }
+
+ @Override
+ public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+ Handler handler, UserHandle user) {
+ if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
+ return super.bindServiceAsUser(service, conn, flags, handler, user);
+ }
+
+ @Override
+ public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+ UserHandle user) {
+ if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
+ return super.bindServiceAsUser(service, conn, flags, user);
+ }
+
+ @Override
+ public void unbindService(ServiceConnection conn) {
+ if (mService != null) mService.getLeakInfo(conn).clearAllocations();
+ super.unbindService(conn);
+ }
+
+ @Override
+ public void registerComponentCallbacks(ComponentCallbacks callback) {
+ if (mComponent != null) mComponent.getLeakInfo(callback).addAllocation(new Throwable());
+ super.registerComponentCallbacks(callback);
+ }
+
+ @Override
+ public void unregisterComponentCallbacks(ComponentCallbacks callback) {
+ if (mComponent != null) mComponent.getLeakInfo(callback).clearAllocations();
+ super.unregisterComponentCallbacks(callback);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
new file mode 100644
index 0000000..0238bf7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import com.android.systemui.statusbar.policy.CallbackController;
+
+public class BaseLeakChecker<T> implements CallbackController<T> {
+
+ private final Tracker mTracker;
+
+ public BaseLeakChecker(LeakCheckedTest test, String tag) {
+ mTracker = test.getTracker(tag);
+ }
+
+ protected final Tracker getTracker() {
+ return mTracker;
+ }
+
+ @Override
+ public void addCallback(T listener) {
+ mTracker.getLeakInfo(listener).addAllocation(new Throwable());
+ }
+
+ @Override
+ public void removeCallback(T listener) {
+ mTracker.getLeakInfo(listener).clearAllocations();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
new file mode 100644
index 0000000..fa07d33
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import android.os.Bundle;
+
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public class FakeBatteryController extends BaseLeakChecker<BatteryStateChangeCallback>
+ implements BatteryController {
+ public FakeBatteryController(LeakCheckedTest test) {
+ super(test, "battery");
+ }
+
+ @Override
+ public void dispatchDemoCommand(String command, Bundle args) {
+
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+
+ }
+
+ @Override
+ public void setPowerSaveMode(boolean powerSave) {
+
+ }
+
+ @Override
+ public boolean isPowerSave() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
new file mode 100644
index 0000000..6074a01
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.BluetoothController.Callback;
+
+import java.util.Collection;
+
+public class FakeBluetoothController extends BaseLeakChecker<Callback> implements
+ BluetoothController {
+
+ public FakeBluetoothController(LeakCheckedTest test) {
+ super(test, "bluetooth");
+ }
+
+ @Override
+ public boolean isBluetoothSupported() {
+ return false;
+ }
+
+ @Override
+ public boolean isBluetoothEnabled() {
+ return false;
+ }
+
+ @Override
+ public int getBluetoothState() {
+ return 0;
+ }
+
+ @Override
+ public boolean isBluetoothConnected() {
+ return false;
+ }
+
+ @Override
+ public boolean isBluetoothConnecting() {
+ return false;
+ }
+
+ @Override
+ public String getLastDeviceName() {
+ return null;
+ }
+
+ @Override
+ public void setBluetoothEnabled(boolean enabled) {
+
+ }
+
+ @Override
+ public Collection<CachedBluetoothDevice> getDevices() {
+ return null;
+ }
+
+ @Override
+ public void connect(CachedBluetoothDevice device) {
+
+ }
+
+ @Override
+ public void disconnect(CachedBluetoothDevice device) {
+
+ }
+
+ @Override
+ public boolean canConfigBluetooth() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
new file mode 100644
index 0000000..08211f8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.CastController.Callback;
+
+import java.util.Set;
+
+public class FakeCastController extends BaseLeakChecker<Callback> implements CastController {
+ public FakeCastController(LeakCheckedTest test) {
+ super(test, "cast");
+ }
+
+ @Override
+ public void setDiscovering(boolean request) {
+
+ }
+
+ @Override
+ public void setCurrentUserId(int currentUserId) {
+
+ }
+
+ @Override
+ public Set<CastDevice> getCastDevices() {
+ return null;
+ }
+
+ @Override
+ public void startCasting(CastDevice device) {
+
+ }
+
+ @Override
+ public void stopCasting(CastDevice device) {
+
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
new file mode 100644
index 0000000..857a785
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.DataSaverController.Listener;
+
+public class FakeDataSaverController extends BaseLeakChecker<Listener> implements DataSaverController {
+
+ public FakeDataSaverController(LeakCheckedTest test) {
+ super(test, "datasaver");
+ }
+
+ @Override
+ public boolean isDataSaverEnabled() {
+ return false;
+ }
+
+ @Override
+ public void setDataSaverEnabled(boolean enabled) {
+
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
new file mode 100644
index 0000000..630abd7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
+
+public class FakeFlashlightController extends BaseLeakChecker<FlashlightListener>
+ implements FlashlightController {
+ public FakeFlashlightController(LeakCheckedTest test) {
+ super(test, "flashlight");
+ }
+
+ @Override
+ public boolean hasFlashlight() {
+ return false;
+ }
+
+ @Override
+ public void setFlashlight(boolean newState) {
+
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
new file mode 100644
index 0000000..781960d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.HotspotController.Callback;
+
+public class FakeHotspotController extends BaseLeakChecker<Callback> implements HotspotController {
+
+ public FakeHotspotController(LeakCheckedTest test) {
+ super(test, "hotspot");
+ }
+
+ @Override
+ public boolean isHotspotEnabled() {
+ return false;
+ }
+
+ @Override
+ public void setHotspotEnabled(boolean enabled) {
+
+ }
+
+ @Override
+ public boolean isHotspotSupported() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
new file mode 100644
index 0000000..39bbf2d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+
+public class FakeKeyguardMonitor implements KeyguardMonitor {
+
+ private final BaseLeakChecker<Callback> mCallbackController;
+
+ public FakeKeyguardMonitor(LeakCheckedTest test) {
+ mCallbackController = new BaseLeakChecker<Callback>(test, "keyguard");
+ }
+
+ @Override
+ public void addCallback(Callback callback) {
+ mCallbackController.addCallback(callback);
+ }
+
+ @Override
+ public void removeCallback(Callback callback) {
+ mCallbackController.removeCallback(callback);
+ }
+
+ @Override
+ public boolean isSecure() {
+ return false;
+ }
+
+ @Override
+ public boolean isShowing() {
+ return false;
+ }
+
+ @Override
+ public boolean canSkipBouncer() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
new file mode 100644
index 0000000..eab436c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback;
+
+public class FakeLocationController extends BaseLeakChecker<LocationSettingsChangeCallback>
+ implements LocationController {
+ public FakeLocationController(LeakCheckedTest test) {
+ super(test, "location");
+ }
+
+ @Override
+ public boolean isLocationEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean setLocationEnabled(boolean enabled) {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java
new file mode 100644
index 0000000..0ec0d77
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.phone.ManagedProfileController.Callback;
+
+public class FakeManagedProfileController extends BaseLeakChecker<Callback> implements
+ ManagedProfileController {
+ public FakeManagedProfileController(LeakCheckedTest test) {
+ super(test, "profile");
+ }
+
+ @Override
+ public void setWorkModeEnabled(boolean enabled) {
+
+ }
+
+ @Override
+ public boolean hasActiveProfile() {
+ return false;
+ }
+
+ @Override
+ public boolean isWorkModeEnabled() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
new file mode 100644
index 0000000..fcfe9aa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+
+public class FakeNetworkController extends BaseLeakChecker<SignalCallback>
+ implements NetworkController {
+
+ private final FakeDataSaverController mDataSaverController;
+ private final BaseLeakChecker<EmergencyListener> mEmergencyChecker;
+
+ public FakeNetworkController(LeakCheckedTest test) {
+ super(test, "network");
+ mDataSaverController = new FakeDataSaverController(test);
+ mEmergencyChecker = new BaseLeakChecker<EmergencyListener>(test, "emergency");
+ }
+
+ @Override
+ public void addEmergencyListener(EmergencyListener listener) {
+ mEmergencyChecker.addCallback(listener);
+ }
+
+ @Override
+ public void removeEmergencyListener(EmergencyListener listener) {
+ mEmergencyChecker.removeCallback(listener);
+ }
+
+ @Override
+ public DataSaverController getDataSaverController() {
+ return mDataSaverController;
+ }
+
+ @Override
+ public boolean hasMobileDataFeature() {
+ return false;
+ }
+
+ @Override
+ public void setWifiEnabled(boolean enabled) {
+
+ }
+
+ @Override
+ public void onUserSwitched(int newUserId) {
+
+ }
+
+ @Override
+ public AccessPointController getAccessPointController() {
+ return null;
+ }
+
+ @Override
+ public DataUsageController getMobileDataController() {
+ return null;
+ }
+
+ @Override
+ public boolean hasVoiceCallingFeature() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java
new file mode 100644
index 0000000..707fc4b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
+
+public class FakeNextAlarmController extends BaseLeakChecker<NextAlarmChangeCallback>
+ implements NextAlarmController {
+
+ public FakeNextAlarmController(LeakCheckedTest test) {
+ super(test, "alarm");
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
new file mode 100644
index 0000000..00e2404
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
+
+public class FakeRotationLockController extends BaseLeakChecker<RotationLockControllerCallback>
+ implements RotationLockController {
+ public FakeRotationLockController(LeakCheckedTest test) {
+ super(test, "rotation");
+ }
+
+ @Override
+ public void setListening(boolean listening) {
+
+ }
+
+ @Override
+ public int getRotationLockOrientation() {
+ return 0;
+ }
+
+ @Override
+ public boolean isRotationLockAffordanceVisible() {
+ return false;
+ }
+
+ @Override
+ public boolean isRotationLocked() {
+ return false;
+ }
+
+ @Override
+ public void setRotationLocked(boolean locked) {
+
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
new file mode 100644
index 0000000..331df58
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback;
+
+public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCallback>
+ implements SecurityController {
+ public FakeSecurityController(LeakCheckedTest test) {
+ super(test, "security");
+ }
+
+ @Override
+ public boolean isDeviceManaged() {
+ return false;
+ }
+
+ @Override
+ public boolean hasProfileOwner() {
+ return false;
+ }
+
+ @Override
+ public String getDeviceOwnerName() {
+ return null;
+ }
+
+ @Override
+ public String getProfileOwnerName() {
+ return null;
+ }
+
+ @Override
+ public CharSequence getDeviceOwnerOrganizationName() {
+ return null;
+ }
+
+ @Override
+ public boolean isVpnEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isVpnRestricted() {
+ return false;
+ }
+
+ @Override
+ public boolean isVpnBranded() {
+ return false;
+ }
+
+ @Override
+ public String getPrimaryVpnName() {
+ return null;
+ }
+
+ @Override
+ public String getProfileVpnName() {
+ return null;
+ }
+
+ @Override
+ public void onUserSwitched(int newUserId) {
+
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java
new file mode 100644
index 0000000..578b310
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
+
+public class FakeUserInfoController extends BaseLeakChecker<OnUserInfoChangedListener>
+ implements UserInfoController {
+ public FakeUserInfoController(LeakCheckedTest test) {
+ super(test, "user_info");
+ }
+
+ @Override
+ public void reloadUserInfo() {
+
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
new file mode 100644
index 0000000..13ea385
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import android.content.ComponentName;
+import android.net.Uri;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.ZenRule;
+
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeController.Callback;
+
+public class FakeZenModeController extends BaseLeakChecker<Callback> implements ZenModeController {
+ public FakeZenModeController(LeakCheckedTest test) {
+ super(test, "zen");
+ }
+
+ @Override
+ public void setZen(int zen, Uri conditionId, String reason) {
+
+ }
+
+ @Override
+ public int getZen() {
+ return 0;
+ }
+
+ @Override
+ public ZenRule getManualRule() {
+ return null;
+ }
+
+ @Override
+ public ZenModeConfig getConfig() {
+ return null;
+ }
+
+ @Override
+ public long getNextAlarm() {
+ return 0;
+ }
+
+ @Override
+ public void setUserId(int userId) {
+
+ }
+
+ @Override
+ public boolean isZenAvailable() {
+ return false;
+ }
+
+ @Override
+ public ComponentName getEffectsSuppressor() {
+ return null;
+ }
+
+ @Override
+ public boolean isCountdownConditionSupported() {
+ return false;
+ }
+
+ @Override
+ public int getCurrentUser() {
+ return 0;
+ }
+
+ @Override
+ public boolean isVolumeRestricted() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
new file mode 100644
index 0000000..728ed60
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+
+import android.util.ArrayMap;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Base class for tests to check if receivers are left registered, services bound, or other
+ * listeners listening.
+ */
+public abstract class LeakCheckedTest extends SysuiTestCase {
+ private static final String TAG = "LeakCheckedTest";
+
+ private final Map<String, Tracker> mTrackers = new HashMap<>();
+ private final Map<Class, Object> mLeakCheckers = new ArrayMap<>();
+
+ @Rule
+ public TestWatcher successWatcher = new TestWatcher() {
+ @Override
+ protected void succeeded(Description description) {
+ verify();
+ }
+ };
+
+ public <T> T getLeakChecker(Class<T> cls) {
+ Object obj = mLeakCheckers.get(cls);
+ if (obj == null) {
+ // Lazy create checkers so we only have the ones we need.
+ if (cls == BluetoothController.class) {
+ obj = new FakeBluetoothController(this);
+ } else if (cls == LocationController.class) {
+ obj = new FakeLocationController(this);
+ } else if (cls == RotationLockController.class) {
+ obj = new FakeRotationLockController(this);
+ } else if (cls == ZenModeController.class) {
+ obj = new FakeZenModeController(this);
+ } else if (cls == CastController.class) {
+ obj = new FakeCastController(this);
+ } else if (cls == HotspotController.class) {
+ obj = new FakeHotspotController(this);
+ } else if (cls == FlashlightController.class) {
+ obj = new FakeFlashlightController(this);
+ } else if (cls == UserInfoController.class) {
+ obj = new FakeUserInfoController(this);
+ } else if (cls == KeyguardMonitor.class) {
+ obj = new FakeKeyguardMonitor(this);
+ } else if (cls == BatteryController.class) {
+ obj = new FakeBatteryController(this);
+ } else if (cls == SecurityController.class) {
+ obj = new FakeSecurityController(this);
+ } else if (cls == ManagedProfileController.class) {
+ obj = new FakeManagedProfileController(this);
+ } else if (cls == NextAlarmController.class) {
+ obj = new FakeNextAlarmController(this);
+ } else if (cls == NetworkController.class) {
+ obj = new FakeNetworkController(this);
+ } else {
+ Assert.fail(cls.getName() + " is not supported by LeakCheckedTest yet");
+ }
+ mLeakCheckers.put(cls, obj);
+ }
+ return (T) obj;
+ }
+
+ @Override
+ public Tracker getTracker(String tag) {
+ Tracker t = mTrackers.get(tag);
+ if (t == null) {
+ t = new Tracker();
+ mTrackers.put(tag, t);
+ }
+ return t;
+ }
+
+ public void verify() {
+ mTrackers.values().forEach(Tracker::verify);
+ }
+
+ public <T extends CallbackController> T addListening(T mock, Class<T> cls, String tag) {
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ getTracker(tag).getLeakInfo(invocation.getArguments()[0])
+ .addAllocation(new Throwable());
+ return null;
+ }
+ }).when(mock).addCallback(any());
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ getTracker(tag).getLeakInfo(invocation.getArguments()[0]).clearAllocations();
+ return null;
+ }
+ }).when(mock).removeCallback(any());
+ mLeakCheckers.put(cls, mock);
+ return mock;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java
new file mode 100644
index 0000000..1d016fb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import android.util.Log;
+
+import org.junit.Assert;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class LeakInfo {
+ private static final String TAG = "LeakInfo";
+ private List<Throwable> mThrowables = new ArrayList<>();
+
+ LeakInfo() {
+ }
+
+ public void addAllocation(Throwable t) {
+ // TODO: Drop off the first element in the stack trace here to have a cleaner stack.
+ mThrowables.add(t);
+ }
+
+ public void clearAllocations() {
+ mThrowables.clear();
+ }
+
+ void verify() {
+ if (mThrowables.size() == 0) return;
+ Log.e(TAG, "Listener or binding not properly released");
+ for (Throwable t : mThrowables) {
+ Log.e(TAG, "Allocation found", t);
+ }
+ StringWriter writer = new StringWriter();
+ mThrowables.get(0).printStackTrace(new PrintWriter(writer));
+ Assert.fail("Listener or binding not properly released\n"
+ + writer.toString());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java
new file mode 100644
index 0000000..26ffd10
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 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.utils.leaks;
+
+import android.util.ArrayMap;
+
+import com.android.systemui.utils.leaks.LeakInfo;
+
+import java.util.Map;
+
+public class Tracker {
+ private Map<Object, LeakInfo> mObjects = new ArrayMap<>();
+
+ public LeakInfo getLeakInfo(Object object) {
+ LeakInfo leakInfo = mObjects.get(object);
+ if (leakInfo == null) {
+ leakInfo = new LeakInfo();
+ mObjects.put(object, leakInfo);
+ }
+ return leakInfo;
+ }
+
+ void verify() {
+ mObjects.values().forEach(LeakInfo::verify);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5eab795..e2027fd 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2883,7 +2883,7 @@
mShowToOwnerOnly = showToOwnerOnly;
}
- boolean isHiddenFromUserLocked() {
+ private boolean isHiddenFromUserLocked() {
// Child windows are evaluated based on their parent window.
final WindowState win = getTopParentWindow();
if (win.mAttrs.type < FIRST_SYSTEM_WINDOW
@@ -3552,16 +3552,23 @@
/** Returns the topmost parent window if this is a child of another window, else this. */
WindowState getTopParentWindow() {
- WindowState w = this;
- while (w != null && w.mIsChildWindow) {
- w = w.getParentWindow();
+ WindowState current = this;
+ WindowState topParent = current;
+ while (current != null && current.mIsChildWindow) {
+ current = current.getParentWindow();
+ // Parent window can be null if the child is detached from it's parent already, but
+ // someone still has a reference to access it. So, we return the top parent value we
+ // already have instead of null.
+ if (current != null) {
+ topParent = current;
+ }
}
- return w;
+ return topParent;
}
boolean isParentWindowHidden() {
final WindowState parent = getParentWindow();
- return (parent == null) ? false : parent.mHidden;
+ return parent != null && parent.mHidden;
}
void setWillReplaceWindow(boolean animate) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 69bfc8f..df35b7ee 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -116,6 +116,11 @@
assertEquals(root, child1.getTopParentWindow());
assertEquals(child1, child2.getParentWindow());
assertEquals(root, child2.getTopParentWindow());
+
+ // Test case were child is detached from parent.
+ root.removeChild(child1);
+ assertEquals(child1, child1.getTopParentWindow());
+ assertEquals(child1, child2.getParentWindow());
}
@Test