Multi-user MTP.

The current MTP kernel driver at /dev/mtp_usb is exclusive, meaning
only one process can have it open. In addition, each MTP session
with a desktop requires unique object IDs, which doesn't hold true
across users on the device.

To solve these two issues, when switching users we cycle the USB host
stack to disconnect both local and remote MTP connections, giving the
new user's media process a chance to claim /dev/mtp_usb, and causing
the desktop to initiate a new MTP session.

This change also allows BroadcastReceivers to registerReceiver()
allow retrieval of a current sticky broadcast. Adds a system property
to override maximum users. Removes MOUNTED broadcasts for secondary
users. Allows INTERACT_ACROSS_USERS to getCurrentUser().

Bug: 6925114
Change-Id: I02b4a1b535af95fb2142655887b6d15a8068d18a
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 32ab154..ba758e5 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -549,34 +549,6 @@
         }
     }
 
-    private final BroadcastReceiver mBootReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-            if (userId == -1) return;
-            final UserHandle user = new UserHandle(userId);
-
-            Slog.d(TAG, "BOOT_COMPLETED for " + user);
-
-            // Broadcast mounted volumes to newly booted user. This kicks off
-            // media scanner when a user becomes active.
-            synchronized (mVolumesLock) {
-                for (StorageVolume volume : mVolumes) {
-                    final UserHandle owner = volume.getOwner();
-                    final boolean ownerMatch = owner == null
-                            || owner.getIdentifier() == user.getIdentifier();
-
-                    final String state = mVolumeStates.get(volume.getPath());
-
-                    if (ownerMatch && (Environment.MEDIA_MOUNTED.equals(state)
-                            || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))) {
-                        sendStorageIntent(Intent.ACTION_MEDIA_MOUNTED, volume, user);
-                    }
-                }
-            }
-        }
-    };
-
     private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -1309,10 +1281,6 @@
         mHandlerThread.start();
         mHandler = new MountServiceHandler(mHandlerThread.getLooper());
 
-        // Watch for user boot completion
-        mContext.registerReceiverAsUser(mBootReceiver, UserHandle.ALL,
-                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, mHandler);
-
         // Watch for user changes
         final IntentFilter userFilter = new IntentFilter();
         userFilter.addAction(Intent.ACTION_USER_ADDED);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index ce5424b..55dcc65 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -14224,12 +14224,14 @@
 
     @Override
     public UserInfo getCurrentUser() {
-        if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
-                != PackageManager.PERMISSION_GRANTED) {
+        if ((checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+                != PackageManager.PERMISSION_GRANTED) && (
+                checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                != PackageManager.PERMISSION_GRANTED)) {
             String msg = "Permission Denial: getCurrentUser() from pid="
                     + Binder.getCallingPid()
                     + ", uid=" + Binder.getCallingUid()
-                    + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+                    + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS;
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index be86628..2dc9a6a 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -39,6 +39,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -85,8 +86,6 @@
 
     private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
 
-    private final int mUserLimit;
-
     private int[] mUserIds;
     private boolean mGuestEnabled;
     private int mNextSerialNumber;
@@ -129,8 +128,6 @@
             mPm = pm;
             mInstallLock = installLock;
             mPackagesLock = packagesLock;
-            mUserLimit = mContext.getResources().getInteger(
-                    com.android.internal.R.integer.config_multiuserMaximumUsers);
             mUsersDir = new File(dataDir, USER_INFO_DIR);
             mUsersDir.mkdirs();
             // Make zeroth user directory, for services to migrate their files to that location
@@ -275,7 +272,7 @@
      */
     private boolean isUserLimitReachedLocked() {
         int nUsers = mUsers.size();
-        return nUsers >= mUserLimit;
+        return nUsers >= UserManager.getMaxSupportedUsers();
     }
 
     /**
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index 3ef6d4c..392d5e7 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -91,6 +91,7 @@
     private static final int MSG_SET_CURRENT_FUNCTIONS = 2;
     private static final int MSG_SYSTEM_READY = 3;
     private static final int MSG_BOOT_COMPLETED = 4;
+    private static final int MSG_USER_SWITCHED = 5;
 
     private static final int AUDIO_MODE_NONE = 0;
     private static final int AUDIO_MODE_SOURCE = 1;
@@ -295,14 +296,24 @@
         private UsbAccessory mCurrentAccessory;
         private int mUsbNotificationId;
         private boolean mAdbNotificationShown;
+        private int mCurrentUser = UserHandle.USER_NULL;
 
         private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
+            @Override
             public void onReceive(Context context, Intent intent) {
                 if (DEBUG) Slog.d(TAG, "boot completed");
                 mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
             }
         };
 
+        private final BroadcastReceiver mUserSwitchedReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+                mHandler.obtainMessage(MSG_USER_SWITCHED, userId, 0).sendToTarget();
+            }
+        };
+
         public UsbHandler(Looper looper) {
             super(looper);
             try {
@@ -347,8 +358,10 @@
                 mUEventObserver.startObserving(USB_STATE_MATCH);
                 mUEventObserver.startObserving(ACCESSORY_START_MATCH);
 
-                mContext.registerReceiver(mBootCompletedReceiver,
-                        new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
+                mContext.registerReceiver(
+                        mBootCompletedReceiver, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
+                mContext.registerReceiver(
+                        mUserSwitchedReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED));
             } catch (Exception e) {
                 Slog.e(TAG, "Error initializing UsbHandler", e);
             }
@@ -611,6 +624,18 @@
                         mDebuggingManager.setAdbEnabled(mAdbEnabled);
                     }
                     break;
+                case MSG_USER_SWITCHED: {
+                    final boolean mtpActive =
+                            containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)
+                            || containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP);
+                    if (mtpActive && mCurrentUser != UserHandle.USER_NULL) {
+                        Slog.v(TAG, "Current user switched; resetting USB host stack for MTP");
+                        setUsbConfig("none");
+                        setUsbConfig(mCurrentFunctions);
+                    }
+                    mCurrentUser = msg.arg1;
+                    break;
+                }
             }
         }