Merge "Cleanup and refactoring of test utilities"

This commit is contained in:
TreeHugger Robot
2016-12-08 18:14:49 +00:00
committed by Android (Google) Code Review
41 changed files with 2154 additions and 1144 deletions

View File

@@ -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();

View File

@@ -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();
}
}
};
}

View File

@@ -22,6 +22,7 @@ import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
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.Process;
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.KeyguardHostView.OnDismissAction;
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.BluetoothControllerImpl;
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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
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();

View File

@@ -143,7 +143,7 @@ public class QSTileHost implements QSTile.Host, Tunable {
mBattery = battery;
mIconController = iconController;
mNextAlarmController = nextAlarmController;
mProfileController = new ManagedProfileController(this);
mProfileController = new ManagedProfileControllerImpl(this);
mHandlerThread = new HandlerThread(QSTileHost.class.getSimpleName(),
Process.THREAD_PRIORITY_BACKGROUND);

View File

@@ -235,11 +235,18 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements
mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD);
}
@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 class QuickStatusBarHeader extends BaseStatusBarHeader implements
}
public void setUserInfoController(UserInfoController userInfoController) {
userInfoController.addCallback(this);
// Don't care
}
@Override

View File

@@ -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);
}
}

View File

@@ -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 {
}
};
}

View File

@@ -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 {

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -159,7 +159,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
mConfig = config;
mReceiverHandler = new Handler(bgLooper);
mCallbackHandler = callbackHandler;
mDataSaverController = new DataSaverController(context);
mDataSaverController = new DataSaverControllerImpl(context);
mSubscriptionManager = subManager;
mSubDefaults = defaultsHandler;

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -92,6 +92,10 @@ public class TunerService extends SystemUI {
mUserTracker.startTracking();
}
public void destroy() {
mUserTracker.stopTracking();
}
private void upgradeTuner(int oldVersion, int newVersion) {
if (oldVersion < 1) {
String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);

View File

@@ -26,6 +26,8 @@ import android.view.LayoutInflater;
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 @@ public abstract class FragmentTestCase extends LeakCheckedTest {
private class HostCallbacks extends FragmentHostCallback<FragmentTestCase> {
public HostCallbacks() {
super(getTrackedContext(), FragmentTestCase.this.mHandler, 0);
super(mContext, FragmentTestCase.this.mHandler, 0);
}
@Override

View File

@@ -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);
}
}
}

View File

@@ -22,6 +22,7 @@ import android.os.Looper;
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 @@ import org.junit.Before;
/**
* 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 @@ public class SysuiTestCase {
}
}
// 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() {
}

View File

@@ -38,6 +38,7 @@ import com.android.systemui.statusbar.policy.SecurityController;
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 @@ public class QSFragmentTest extends FragmentTestCase {
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 @@ public class QSFragmentTest extends FragmentTestCase {
waitForIdleSync(h);
host.destroy();
// Ensure the tuner cleans up its persistent listeners.
TunerService.get(mContext).destroy();
}
}

View File

@@ -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 @@ public class TestableContext extends ContextWrapper {
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 @@ public class TestableContext extends ContextWrapper {
// 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);
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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) {
}
}

View File

@@ -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) {
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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");
}
}

View File

@@ -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) {
}
}

View File

@@ -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) {
}
}

View File

@@ -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() {
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}