diff options
| author | 2018-10-26 14:46:20 +0000 | |
|---|---|---|
| committer | 2018-10-26 14:46:20 +0000 | |
| commit | ffe3226069984e6c8e3f7bf08941d74c2afd3fdc (patch) | |
| tree | e99f15c86073d99a9a5e6f5cdcdff97f546a837d | |
| parent | 91559181e41e1008536f6be1785b95cb8638186c (diff) | |
| parent | 1f32cf0c09a295937a6d4bc97136e42f2629047d (diff) | |
Merge "Adds a AppOpsController that can be suscribed to."
9 files changed, 529 insertions, 184 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 494880e96ff7..26fb486a6598 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -33,34 +33,35 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.Preconditions; import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.systemui.appops.AppOpsController; +import com.android.systemui.appops.AppOpsControllerImpl; import com.android.systemui.assist.AssistManager; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.plugins.PluginInitializerImpl; import com.android.systemui.plugins.PluginDependencyProvider; -import com.android.systemui.shared.plugins.PluginManager; -import com.android.systemui.shared.plugins.PluginManagerImpl; +import com.android.systemui.plugins.PluginInitializerImpl; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.power.EnhancedEstimates; import com.android.systemui.power.EnhancedEstimatesImpl; import com.android.systemui.power.PowerNotificationWarnings; import com.android.systemui.power.PowerUI; +import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManagerImpl; import com.android.systemui.statusbar.NotificationRemoteInputManager; -import com.android.systemui.statusbar.notification.AppOpsListener; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl; +import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; @@ -336,8 +337,6 @@ public class Dependency extends SystemUI { mProviders.put(EnhancedEstimates.class, () -> new EnhancedEstimatesImpl()); - mProviders.put(AppOpsListener.class, () -> new AppOpsListener(mContext)); - mProviders.put(VibratorHelper.class, () -> new VibratorHelper(mContext)); mProviders.put(IStatusBarService.class, () -> IStatusBarService.Stub.asInterface( @@ -357,6 +356,9 @@ public class Dependency extends SystemUI { mProviders.put(InitController.class, InitController::new); + mProviders.put(AppOpsController.class, () -> + new AppOpsControllerImpl(mContext, getDependency(BG_LOOPER))); + // Put all dependencies above here so the factory can override them if it wants. SystemUIFactory.getInstance().injectDependencies(mProviders, mContext); diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java new file mode 100644 index 000000000000..9f363f681b04 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.appops; + +/** + * Item to store information of active applications using different APP OPS + */ +public class AppOpItem { + + private int mCode; + private int mUid; + private String mPackageName; + private long mTimeStarted; + + public AppOpItem(int code, int uid, String packageName, long timeStarted) { + this.mCode = code; + this.mUid = uid; + this.mPackageName = packageName; + this.mTimeStarted = timeStarted; + } + + public int getCode() { + return mCode; + } + + public int getUid() { + return mUid; + } + + public String getPackageName() { + return mPackageName; + } + + public long getTimeStarted() { + return mTimeStarted; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsController.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsController.java new file mode 100644 index 000000000000..4966fc62c542 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsController.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.appops; + +import java.util.List; + +/** + * Controller to keep track of applications that have requested access to given App Ops. + * + * It can be subscribed to with callbacks. Additionally, it passes on the information to + * NotificationPresenter to be displayed to the user. + */ +public interface AppOpsController { + + /** + * Callback to notify when the state of active AppOps tracked by the controller has changed + */ + interface Callback { + void onActiveStateChanged(int code, int uid, String packageName, boolean active); + } + + /** + * Adds a callback that will get notified when an AppOp of the type the controller tracks + * changes + * + * @param opsCodes App Ops the callback was interested in checking + * @param cb Callback to report changes + * + * @see #removeCallback(int[], Callback) + */ + void addCallback(int[] opsCodes, Callback cb); + + /** + * Removes a callback from those notifified when an AppOp of the type the controller tracks + * changes + * + * @param opsCodes App Ops the callback is interested in checking + * @param cb Callback to stop reporting changes + * + * @see #addCallback(int[], Callback) + */ + void removeCallback(int[] opsCodes, Callback cb); + + /** + * Returns a copy of the list containing all the active AppOps that the controller tracks. + * + * @return List of active AppOps information + */ + List<AppOpItem> getActiveAppOps(); + + /** + * Returns a copy of the list containing all the active AppOps that the controller tracks, for + * a given user id. + * + * @param userId User id to track + * + * @return List of active AppOps information for that user id + */ + List<AppOpItem> getActiveAppOpsForUser(int userId); +} diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java new file mode 100644 index 000000000000..906a210b6b31 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.appops; + +import android.app.AppOpsManager; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Controller to keep track of applications that have requested access to given App Ops + * + * It can be subscribed to with callbacks. Additionally, it passes on the information to + * NotificationPresenter to be displayed to the user. + */ +public class AppOpsControllerImpl implements AppOpsController, + AppOpsManager.OnOpActiveChangedListener { + + private static final long LOCATION_TIME_DELAY_MS = 5000; + private static final String TAG = "AppOpsControllerImpl"; + private static final boolean DEBUG = false; + private final Context mContext; + + protected final AppOpsManager mAppOps; + private final H mBGHandler; + private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>(); + private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>(); + @GuardedBy("mActiveItems") + private final List<AppOpItem> mActiveItems = new ArrayList<>(); + + protected static final int[] OPS = new int[] {AppOpsManager.OP_CAMERA, + AppOpsManager.OP_SYSTEM_ALERT_WINDOW, + AppOpsManager.OP_RECORD_AUDIO, + AppOpsManager.OP_COARSE_LOCATION, + AppOpsManager.OP_FINE_LOCATION}; + + public AppOpsControllerImpl(Context context, Looper bgLooper) { + mContext = context; + mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + mBGHandler = new H(bgLooper); + final int numOps = OPS.length; + for (int i = 0; i < numOps; i++) { + mCallbacksByCode.put(OPS[i], new ArraySet<>()); + } + } + + @VisibleForTesting + protected void setListening(boolean listening) { + if (listening) { + mAppOps.startWatchingActive(OPS, this); + } else { + mAppOps.stopWatchingActive(this); + } + } + + /** + * Adds a callback that will get notifified when an AppOp of the type the controller tracks + * changes + * + * @param callback Callback to report changes + * @param opsCodes App Ops the callback is interested in checking + * + * @see #removeCallback(int[], Callback) + */ + @Override + public void addCallback(int[] opsCodes, AppOpsController.Callback callback) { + boolean added = false; + final int numCodes = opsCodes.length; + for (int i = 0; i < numCodes; i++) { + if (mCallbacksByCode.containsKey(opsCodes[i])) { + mCallbacksByCode.get(opsCodes[i]).add(callback); + added = true; + } else { + if (DEBUG) Log.wtf(TAG, "APP_OP " + opsCodes[i] + " not supported"); + } + } + if (added) mCallbacks.add(callback); + if (!mCallbacks.isEmpty()) setListening(true); + } + + /** + * Removes a callback from those notified when an AppOp of the type the controller tracks + * changes + * + * @param callback Callback to stop reporting changes + * @param opsCodes App Ops the callback was interested in checking + * + * @see #addCallback(int[], Callback) + */ + @Override + public void removeCallback(int[] opsCodes, AppOpsController.Callback callback) { + final int numCodes = opsCodes.length; + for (int i = 0; i < numCodes; i++) { + if (mCallbacksByCode.containsKey(opsCodes[i])) { + mCallbacksByCode.get(opsCodes[i]).remove(callback); + } + } + mCallbacks.remove(callback); + if (mCallbacks.isEmpty()) setListening(false); + } + + private AppOpItem getAppOpItem(int code, int uid, String packageName) { + final int itemsQ = mActiveItems.size(); + for (int i = 0; i < itemsQ; i++) { + AppOpItem item = mActiveItems.get(i); + if (item.getCode() == code && item.getUid() == uid + && item.getPackageName().equals(packageName)) { + return item; + } + } + return null; + } + + private boolean updateActives(int code, int uid, String packageName, boolean active) { + synchronized (mActiveItems) { + AppOpItem item = getAppOpItem(code, uid, packageName); + if (item == null && active) { + item = new AppOpItem(code, uid, packageName, System.currentTimeMillis()); + mActiveItems.add(item); + if (code == AppOpsManager.OP_COARSE_LOCATION + || code == AppOpsManager.OP_FINE_LOCATION) { + mBGHandler.scheduleRemoval(item, LOCATION_TIME_DELAY_MS); + } + if (DEBUG) Log.w(TAG, "Added item: " + item.toString()); + return true; + } else if (item != null && !active) { + mActiveItems.remove(item); + if (DEBUG) Log.w(TAG, "Removed item: " + item.toString()); + return true; + } else if (item != null && active + && (code == AppOpsManager.OP_COARSE_LOCATION + || code == AppOpsManager.OP_FINE_LOCATION)) { + mBGHandler.scheduleRemoval(item, LOCATION_TIME_DELAY_MS); + return true; + } + return false; + } + } + + /** + * Returns a copy of the list containing all the active AppOps that the controller tracks. + * + * @return List of active AppOps information + */ + public List<AppOpItem> getActiveAppOps() { + synchronized (mActiveItems) { + return new ArrayList<>(mActiveItems); + } + } + + /** + * Returns a copy of the list containing all the active AppOps that the controller tracks, for + * a given user id. + * + * @param userId User id to track + * + * @return List of active AppOps information for that user id + */ + public List<AppOpItem> getActiveAppOpsForUser(int userId) { + List<AppOpItem> list = new ArrayList<>(); + synchronized (mActiveItems) { + final int numActiveItems = mActiveItems.size(); + for (int i = 0; i < numActiveItems; i++) { + AppOpItem item = mActiveItems.get(i); + if (UserHandle.getUserId(item.getUid()) == userId) { + list.add(item); + } + } + } + return list; + } + + @Override + public void onOpActiveChanged(int code, int uid, String packageName, boolean active) { + if (updateActives(code, uid, packageName, active)) { + for (Callback cb: mCallbacksByCode.get(code)) { + cb.onActiveStateChanged(code, uid, packageName, active); + } + } + } + + private final class H extends Handler { + H(Looper looper) { + super(looper); + } + + public void scheduleRemoval(AppOpItem item, long timeToRemoval) { + removeCallbacksAndMessages(item); + postDelayed(new Runnable() { + @Override + public void run() { + onOpActiveChanged(item.getCode(), item.getUid(), + item.getPackageName(), false); + } + }, item, timeToRemoval); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java deleted file mode 100644 index 9e99fbb3afc0..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2017 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.notification; - -import android.app.AppOpsManager; -import android.content.Context; - -import com.android.systemui.Dependency; -import com.android.systemui.ForegroundServiceController; -import com.android.systemui.statusbar.NotificationPresenter; - -/** - * This class handles listening to notification updates and passing them along to - * NotificationPresenter to be displayed to the user. - */ -public class AppOpsListener implements AppOpsManager.OnOpActiveChangedListener { - private static final String TAG = "NotificationListener"; - - // Dependencies: - private final ForegroundServiceController mFsc = - Dependency.get(ForegroundServiceController.class); - private final NotificationEntryManager mEntryManager = - Dependency.get(NotificationEntryManager.class); - - private final Context mContext; - protected NotificationPresenter mPresenter; - protected final AppOpsManager mAppOps; - - protected static final int[] OPS = new int[] {AppOpsManager.OP_CAMERA, - AppOpsManager.OP_SYSTEM_ALERT_WINDOW, - AppOpsManager.OP_RECORD_AUDIO}; - - public AppOpsListener(Context context) { - mContext = context; - mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - } - - public void setUpWithPresenter(NotificationPresenter presenter) { - mPresenter = presenter; - mAppOps.startWatchingActive(OPS, this); - } - - public void destroy() { - mAppOps.stopWatchingActive(this); - } - - @Override - public void onOpActiveChanged(int code, int uid, String packageName, boolean active) { - mFsc.onAppOpChanged(code, uid, packageName, active); - Dependency.get(Dependency.MAIN_HANDLER).post(() -> { - mEntryManager.updateNotificationsForAppOp(code, uid, packageName, active); - }); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index b9ca94956dc9..37eccb59ebdd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -29,7 +29,6 @@ import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAK import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_INVALID; import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_LEFT; import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF; -import static com.android.systemui.statusbar.NotificationMediaManager.DEBUG_MEDIA; 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; @@ -46,6 +45,7 @@ import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.AlarmManager; +import android.app.AppOpsManager; import android.app.IWallpaperManager; import android.app.KeyguardManager; import android.app.Notification; @@ -130,6 +130,7 @@ import com.android.systemui.DemoMode; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.EventLogTags; +import com.android.systemui.ForegroundServiceController; import com.android.systemui.InitController; import com.android.systemui.Interpolators; import com.android.systemui.Prefs; @@ -138,6 +139,7 @@ import com.android.systemui.RecentsComponent; import com.android.systemui.SystemUI; import com.android.systemui.SystemUIFactory; import com.android.systemui.UiOffloadThread; +import com.android.systemui.appops.AppOpsController; import com.android.systemui.assist.AssistManager; import com.android.systemui.charging.WirelessChargingAnimation; import com.android.systemui.classifier.FalsingLog; @@ -183,7 +185,6 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; -import com.android.systemui.statusbar.notification.AppOpsListener; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationData.Entry; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -228,7 +229,8 @@ public class StatusBar extends SystemUI implements DemoMode, OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback, ColorExtractor.OnColorsChangedListener, ConfigurationListener, StatusBarStateController.StateListener, ShadeController, - ActivityLaunchAnimator.Callback, AmbientPulseManager.OnAmbientChangedListener { + ActivityLaunchAnimator.Callback, AmbientPulseManager.OnAmbientChangedListener, + AppOpsController.Callback { public static final boolean MULTIUSER_DEBUG = false; public static final boolean ENABLE_CHILD_NOTIFICATIONS @@ -372,7 +374,8 @@ public class StatusBar extends SystemUI implements DemoMode, protected NotificationLogger mNotificationLogger; protected NotificationEntryManager mEntryManager; protected NotificationViewHierarchyManager mViewHierarchyManager; - protected AppOpsListener mAppOpsListener; + protected ForegroundServiceController mForegroundServiceController; + protected AppOpsController mAppOpsController; protected KeyguardViewMediator mKeyguardViewMediator; private ZenModeController mZenController; @@ -564,6 +567,20 @@ public class StatusBar extends SystemUI implements DemoMode, protected NotificationPresenter mPresenter; @Override + public void onActiveStateChanged(int code, int uid, String packageName, boolean active) { + mForegroundServiceController.onAppOpChanged(code, uid, packageName, active); + Dependency.get(Dependency.MAIN_HANDLER).post(() -> { + mEntryManager.updateNotificationsForAppOp(code, uid, packageName, active); + }); + } + + protected static final int[] APP_OPS = new int[] {AppOpsManager.OP_CAMERA, + AppOpsManager.OP_SYSTEM_ALERT_WINDOW, + AppOpsManager.OP_RECORD_AUDIO, + AppOpsManager.OP_COARSE_LOCATION, + AppOpsManager.OP_FINE_LOCATION}; + + @Override public void start() { mGroupManager = Dependency.get(NotificationGroupManager.class); mVisualStabilityManager = Dependency.get(VisualStabilityManager.class); @@ -585,7 +602,8 @@ public class StatusBar extends SystemUI implements DemoMode, mMediaManager = Dependency.get(NotificationMediaManager.class); mEntryManager = Dependency.get(NotificationEntryManager.class); mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class); - mAppOpsListener = Dependency.get(AppOpsListener.class); + mForegroundServiceController = Dependency.get(ForegroundServiceController.class); + mAppOpsController = Dependency.get(AppOpsController.class); mZenController = Dependency.get(ZenModeController.class); mKeyguardViewMediator = getComponent(KeyguardViewMediator.class); mColorExtractor = Dependency.get(SysuiColorExtractor.class); @@ -984,7 +1002,7 @@ public class StatusBar extends SystemUI implements DemoMode, mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel, mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController, mScrimController, this); - mAppOpsListener.setUpWithPresenter(mPresenter); + mAppOpsController.addCallback(APP_OPS, this); mNotificationListener.setUpWithPresenter(mPresenter); mNotificationShelf.setOnActivatedListener(mPresenter); mRemoteInputManager.getController().addCallback(mStatusBarWindowController); @@ -2853,7 +2871,7 @@ public class StatusBar extends SystemUI implements DemoMode, mDeviceProvisionedController.removeCallback(mUserSetupObserver); Dependency.get(ConfigurationController.class).removeCallback(this); mZenController.removeCallback(this); - mAppOpsListener.destroy(); + mAppOpsController.removeCallback(APP_OPS, this); } private boolean mDemoModeAllowed; diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java new file mode 100644 index 000000000000..b699163c40e8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.appops; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.AppOpsManager; +import android.os.UserHandle; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import com.android.systemui.Dependency; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.NotificationPresenter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class AppOpsControllerTest extends SysuiTestCase { + private static final String TEST_PACKAGE_NAME = "test"; + private static final int TEST_UID = 0; + private static final int TEST_UID_OTHER = 500000; + + @Mock private NotificationPresenter mPresenter; + @Mock private AppOpsManager mAppOpsManager; + @Mock private AppOpsController.Callback mCallback; + + private AppOpsControllerImpl mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager); + + mController = new AppOpsControllerImpl(mContext, Dependency.get(Dependency.BG_LOOPER)); + } + + @Test + public void testOnlyListenForFewOps() { + mController.setListening(true); + verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController); + } + + @Test + public void testStopListening() { + mController.setListening(false); + verify(mAppOpsManager, times(1)).stopWatchingActive(mController); + } + + @Test + public void addCallback_includedCode() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); + verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO, + TEST_UID, TEST_PACKAGE_NAME, true); + } + + @Test + public void addCallback_notIncludedCode() { + mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); + verify(mCallback, never()).onActiveStateChanged( + anyInt(), anyInt(), anyString(), anyBoolean()); + } + + @Test + public void removeCallback_sameCode() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); + mController.removeCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); + verify(mCallback, never()).onActiveStateChanged( + anyInt(), anyInt(), anyString(), anyBoolean()); + } + + @Test + public void addCallback_notSameCode() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); + mController.removeCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); + verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO, + TEST_UID, TEST_PACKAGE_NAME, true); + } + + @Test + public void getActiveItems_sameDetails() { + mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO, + TEST_UID, TEST_PACKAGE_NAME, true); + mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO, + TEST_UID, TEST_PACKAGE_NAME, true); + assertEquals(1, mController.getActiveAppOps().size()); + } + + @Test + public void getActiveItems_differentDetails() { + mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO, + TEST_UID, TEST_PACKAGE_NAME, true); + mController.onOpActiveChanged(AppOpsManager.OP_CAMERA, + TEST_UID, TEST_PACKAGE_NAME, true); + assertEquals(2, mController.getActiveAppOps().size()); + } + + @Test public void getActiveItemsForUser() { + mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO, + TEST_UID, TEST_PACKAGE_NAME, true); + mController.onOpActiveChanged(AppOpsManager.OP_CAMERA, + TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + assertEquals(1, + mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size()); + assertEquals(1, + mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java deleted file mode 100644 index b405a5cd91bf..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.statusbar.notification; - -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.app.AppOpsManager; -import android.os.Handler; -import android.os.Looper; -import android.support.test.filters.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; - -import com.android.systemui.Dependency; -import com.android.systemui.ForegroundServiceController; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.NotificationPresenter; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper -public class AppOpsListenerTest extends SysuiTestCase { - private static final String TEST_PACKAGE_NAME = "test"; - private static final int TEST_UID = 0; - - @Mock private NotificationPresenter mPresenter; - @Mock private AppOpsManager mAppOpsManager; - - // Dependency mocks: - @Mock private NotificationEntryManager mEntryManager; - @Mock private ForegroundServiceController mFsc; - - private AppOpsListener mListener; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); - mDependency.injectTestDependency(ForegroundServiceController.class, mFsc); - getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager); - mDependency.injectTestDependency(Dependency.MAIN_HANDLER, - Handler.createAsync(Looper.myLooper())); - - mListener = new AppOpsListener(mContext); - } - - @Test - public void testOnlyListenForFewOps() { - mListener.setUpWithPresenter(mPresenter); - - verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsListener.OPS, mListener); - } - - @Test - public void testStopListening() { - mListener.destroy(); - verify(mAppOpsManager, times(1)).stopWatchingActive(mListener); - } - - @Test - public void testInformEntryMgrOnAppOpsChange() { - mListener.setUpWithPresenter(mPresenter); - mListener.onOpActiveChanged( - AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); - TestableLooper.get(this).processAllMessages(); - verify(mEntryManager, times(1)).updateNotificationsForAppOp( - AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); - } - - @Test - public void testInformFscOnAppOpsChange() { - mListener.setUpWithPresenter(mPresenter); - mListener.onOpActiveChanged( - AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); - TestableLooper.get(this).processAllMessages(); - verify(mFsc, times(1)).onAppOpChanged( - AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 939245f7cc7e..882f2615f443 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -67,6 +67,8 @@ import com.android.systemui.ForegroundServiceController; import com.android.systemui.InitController; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.appops.AppOpsController; +import com.android.systemui.appops.AppOpsControllerImpl; import com.android.systemui.assist.AssistManager; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -84,7 +86,6 @@ import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.notification.AppOpsListener; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationData.Entry; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -157,7 +158,7 @@ public class StatusBarTest extends SysuiTestCase { mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager); mDependency.injectTestDependency(NotificationListener.class, mNotificationListener); mDependency.injectTestDependency(KeyguardMonitor.class, mock(KeyguardMonitorImpl.class)); - mDependency.injectTestDependency(AppOpsListener.class, mock(AppOpsListener.class)); + mDependency.injectTestDependency(AppOpsController.class, mock(AppOpsControllerImpl.class)); mDependency.injectTestDependency(StatusBarStateController.class, mStatusBarStateController); mDependency.injectTestDependency(DeviceProvisionedController.class, mDeviceProvisionedController); |