Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5119ee33 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Implements AppOpsManager.OnOpNotedListener"

parents d378aac6 510585d2
Loading
Loading
Loading
Loading
+93 −26
Original line number Diff line number Diff line
@@ -39,25 +39,39 @@ import java.util.Set;
 * NotificationPresenter to be displayed to the user.
 */
public class AppOpsControllerImpl implements AppOpsController,
        AppOpsManager.OnOpActiveChangedListener {
        AppOpsManager.OnOpActiveChangedListener,
        AppOpsManager.OnOpNotedListener {

    private static final long LOCATION_TIME_DELAY_MS = 5000;
    private static final long NOTED_OP_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 AppOpsManager mAppOps;
    private 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<>();
    @GuardedBy("mNotedItems")
    private final List<AppOpItem> mNotedItems = new ArrayList<>();

    protected static final int[] OPS;
    protected static final String[] OPS_STRING = new String[] {
            AppOpsManager.OPSTR_CAMERA,
            AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW,
            AppOpsManager.OPSTR_RECORD_AUDIO,
            AppOpsManager.OPSTR_COARSE_LOCATION,
            AppOpsManager.OPSTR_FINE_LOCATION};

    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};
    static {
        int numOps = OPS_STRING.length;
        OPS = new int[numOps];
        for (int i = 0; i < numOps; i++) {
            OPS[i] = AppOpsManager.strOpToOp(OPS_STRING[i]);
        }
    }

    public AppOpsControllerImpl(Context context, Looper bgLooper) {
        mContext = context;
@@ -69,12 +83,19 @@ public class AppOpsControllerImpl implements AppOpsController,
        }
    }

    @VisibleForTesting
    protected void setBGHandler(H handler) {
        mBGHandler = handler;
    }

    @VisibleForTesting
    protected void setListening(boolean listening) {
        if (listening) {
            mAppOps.startWatchingActive(OPS, this);
            mAppOps.startWatchingNoted(OPS_STRING, this);
        } else {
            mAppOps.stopWatchingActive(this);
            mAppOps.stopWatchingNoted(this);
        }
    }

@@ -124,10 +145,11 @@ public class AppOpsControllerImpl implements AppOpsController,
        if (mCallbacks.isEmpty()) setListening(false);
    }

    private AppOpItem getAppOpItem(int code, int uid, String packageName) {
        final int itemsQ = mActiveItems.size();
    private AppOpItem getAppOpItem(List<AppOpItem> appOpList, int code, int uid,
            String packageName) {
        final int itemsQ = appOpList.size();
        for (int i = 0; i < itemsQ; i++) {
            AppOpItem item = mActiveItems.get(i);
            AppOpItem item = appOpList.get(i);
            if (item.getCode() == code && item.getUid() == uid
                    && item.getPackageName().equals(packageName)) {
                return item;
@@ -138,39 +160,59 @@ public class AppOpsControllerImpl implements AppOpsController,

    private boolean updateActives(int code, int uid, String packageName, boolean active) {
        synchronized (mActiveItems) {
            AppOpItem item = getAppOpItem(code, uid, packageName);
            AppOpItem item = getAppOpItem(mActiveItems, 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;
        }
    }

    private void removeNoted(int code, int uid, String packageName) {
        AppOpItem item;
        synchronized (mNotedItems) {
            item = getAppOpItem(mNotedItems, code, uid, packageName);
            if (item == null) return;
            mNotedItems.remove(item);
            if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
        }
        notifySuscribers(code, uid, packageName, false);
    }

    private void addNoted(int code, int uid, String packageName) {
        AppOpItem item;
        synchronized (mNotedItems) {
            item = getAppOpItem(mNotedItems, code, uid, packageName);
            if (item == null) {
                item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
                mNotedItems.add(item);
                if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
            }
        }
        mBGHandler.scheduleRemoval(item, NOTED_OP_TIME_DELAY_MS);
    }

    /**
     * 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() {
        ArrayList<AppOpItem> active;
        synchronized (mActiveItems) {
            return new ArrayList<>(mActiveItems);
            active = new ArrayList<>(mActiveItems);
        }
        synchronized (mNotedItems) {
            active.addAll(mNotedItems);
        }
        return active;
    }

    /**
@@ -192,19 +234,45 @@ public class AppOpsControllerImpl implements AppOpsController,
                }
            }
        }
        synchronized (mNotedItems) {
            final int numNotedItems = mNotedItems.size();
            for (int i = 0; i < numNotedItems; i++) {
                AppOpItem item = mNotedItems.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)) {
            notifySuscribers(code, uid, packageName, active);
        }
    }

    @Override
    public void onOpNoted(String code, int uid, String packageName, int result) {
        if (DEBUG) {
            Log.w(TAG, "Op: " + code + " with result " + AppOpsManager.MODE_NAMES[result]);
        }
        if (result != AppOpsManager.MODE_ALLOWED) return;
        int op_code = AppOpsManager.strOpToOp(code);
        addNoted(op_code, uid, packageName);
        notifySuscribers(op_code, uid, packageName, true);
    }

    private void notifySuscribers(int code, int uid, String packageName, boolean active) {
        if (mCallbacksByCode.containsKey(code)) {
            for (Callback cb: mCallbacksByCode.get(code)) {
                cb.onActiveStateChanged(code, uid, packageName, active);
            }
        }
    }

    private final class H extends Handler {
    protected final class H extends Handler {
        H(Looper looper) {
            super(looper);
        }
@@ -214,8 +282,7 @@ public class AppOpsControllerImpl implements AppOpsController,
            postDelayed(new Runnable() {
                @Override
                public void run() {
                    onOpActiveChanged(item.getCode(), item.getUid(),
                            item.getPackageName(), false);
                    removeNoted(item.getCode(), item.getUid(), item.getPackageName());
                }
            }, item, timeToRemoval);
        }
+1 −1
Original line number Diff line number Diff line
@@ -115,7 +115,7 @@ class PrivacyItemController(val context: Context, val callback: Callback) {

    private fun updatePrivacyList() {
        privacyList = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
                .mapNotNull { toPrivacyItem(it) }
                .mapNotNull { toPrivacyItem(it) }.distinct()
    }

    private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
+31 −10
Original line number Diff line number Diff line
@@ -17,8 +17,10 @@
package com.android.systemui.appops;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -32,7 +34,6 @@ 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;
@@ -48,9 +49,12 @@ public class AppOpsControllerTest extends SysuiTestCase {
    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;
    @Mock
    private AppOpsManager mAppOpsManager;
    @Mock
    private AppOpsController.Callback mCallback;
    @Mock
    private AppOpsControllerImpl.H mMockHandler;

    private AppOpsControllerImpl mController;

@@ -77,9 +81,13 @@ public class AppOpsControllerTest extends SysuiTestCase {

    @Test
    public void addCallback_includedCode() {
        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
        mController.addCallback(
                new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
                mCallback);
        mController.onOpActiveChanged(
                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
        mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
                AppOpsManager.MODE_ALLOWED);
        verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
                TEST_UID, TEST_PACKAGE_NAME, true);
    }
@@ -106,7 +114,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
    @Test
    public void addCallback_notSameCode() {
        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
        mController.removeCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
        mController.removeCallback(new int[]{AppOpsManager.OP_CAMERA}, mCallback);
        mController.onOpActiveChanged(
                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
        verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
@@ -128,17 +136,30 @@ public class AppOpsControllerTest extends SysuiTestCase {
                TEST_UID, TEST_PACKAGE_NAME, true);
        mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
                TEST_UID, TEST_PACKAGE_NAME, true);
        assertEquals(2, mController.getActiveAppOps().size());
        mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION,
                TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
        assertEquals(3, mController.getActiveAppOps().size());
    }

    @Test public void getActiveItemsForUser() {
    @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.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION,
                TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
        assertEquals(2,
                mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size());
        assertEquals(1,
                mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size());
                mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID_OTHER)).size());
    }

    @Test
    public void opNotedScheduledForRemoval() {
        mController.setBGHandler(mMockHandler);
        mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
                AppOpsManager.MODE_ALLOWED);
        verify(mMockHandler).scheduleRemoval(any(AppOpItem.class), anyLong());
    }
}
+24 −3
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.privacy
import android.app.ActivityManager
import android.app.AppOpsManager
import android.content.Intent
import android.content.pm.UserInfo
import android.os.Handler
import android.os.UserHandle
import android.os.UserManager
@@ -30,13 +31,16 @@ import com.android.systemui.Dependency
import com.android.systemui.SysuiTestCase
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyList
import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.doReturn
@@ -52,8 +56,8 @@ class PrivacyItemControllerTest : SysuiTestCase() {

    companion object {
        val CURRENT_USER_ID = ActivityManager.getCurrentUser()
        val OTHER_USER = UserHandle(CURRENT_USER_ID + 1)
        const val TAG = "PrivacyItemControllerTest"
        fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
    }

    @Mock
@@ -62,6 +66,8 @@ class PrivacyItemControllerTest : SysuiTestCase() {
    private lateinit var callback: PrivacyItemController.Callback
    @Mock
    private lateinit var userManager: UserManager
    @Captor
    private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>

    private lateinit var testableLooper: TestableLooper
    private lateinit var privacyItemController: PrivacyItemController
@@ -76,8 +82,11 @@ class PrivacyItemControllerTest : SysuiTestCase() {
        mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper))
        mContext.addMockSystemService(UserManager::class.java, userManager)

        doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, 0, "", 0)))
                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
        doReturn(listOf(object : UserInfo() {
            init {
                id = CURRENT_USER_ID
            }
        })).`when`(userManager).getProfiles(anyInt())

        privacyItemController = PrivacyItemController(mContext, callback)
    }
@@ -99,6 +108,18 @@ class PrivacyItemControllerTest : SysuiTestCase() {
                any(AppOpsController.Callback::class.java))
    }

    @Test
    fun testDistinctItems() {
        doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, CURRENT_USER_ID, "", 0),
                AppOpItem(AppOpsManager.OP_CAMERA, CURRENT_USER_ID, "", 1)))
                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())

        privacyItemController.setListening(true)
        testableLooper.processAllMessages()
        verify(callback).privacyChanged(capture(argCaptor))
        assertEquals(1, argCaptor.value.size)
    }

    @Test
    fun testRegisterReceiver_allUsers() {
        val spiedContext = spy(mContext)