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

Commit 66d451b9 authored by Jaewan Kim's avatar Jaewan Kim
Browse files

MediaSessionService: Detects changes media session service apps

Bug: 72613128
Test: Following tests
  - Manually uninstall and install MediaComponents test apk and verified
    that 'dumpsys media_session' is updated accordingly.
  - Manually disabled/enabled a service in the MediaComponents test apk
    and verified that 'dumpsys media_session' is updated accordingly.
  - Manually reinstalled the MediaComponents test apk and verified that
    the session service is updated only once.
  - Run all MediaComponents tests once
Change-Id: I869853a40fb81e71f0188e84711f9d4a763defe2
parent 02a72280
Loading
Loading
Loading
Loading
+98 −11
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
@@ -76,6 +77,7 @@ import android.util.SparseIntArray;
import android.view.KeyEvent;
import android.view.ViewConfiguration;

import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
import com.android.server.Watchdog;
@@ -180,9 +182,8 @@ public class MediaSessionService extends SystemService implements Monitor {

        updateUser();

        registerPackageBroadcastReceivers();
        // TODO(jaewan): Query per users
        // TODO(jaewan): Add listener to know changes in list of services.
        //               Refer TvInputManagerService.registerBroadcastReceivers()
        buildMediaSessionService2List();
    }

@@ -437,12 +438,74 @@ public class MediaSessionService extends SystemService implements Monitor {
        mHandler.postSessionsChanged(session.getUserId());
    }

    private void registerPackageBroadcastReceivers() {
        // TODO(jaewan): Only consider changed packages when building session service list
        //               when we make this multi-user aware. At that time,
        //               use PackageMonitor.getChangingUserId() to know which user has changed.
        IntentFilter filter = new IntentFilter();
        filter.addDataScheme("package");
        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        filter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
        filter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);

        getContext().registerReceiverAsUser(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                final int changeUserId = intent.getIntExtra(
                        Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
                if (changeUserId == UserHandle.USER_NULL) {
                    Log.w(TAG, "Intent broadcast does not contain user handle: "+ intent);
                    return;
                }
                // Check if the package is replacing (i.e. reinstalling)
                final boolean isReplacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
                // TODO(jaewan): Add multi-user support with this.
                // final int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);

                if (DEBUG) {
                    Log.d(TAG, "Received change in packages, intent=" + intent);
                }
                switch (intent.getAction()) {
                    case Intent.ACTION_PACKAGE_ADDED:
                    case Intent.ACTION_PACKAGE_REMOVED:
                    case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
                    case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
                        if (isReplacing) {
                            // Ignore if the package(s) are replacing. In that case, followings will
                            // happen in order.
                            //    1. ACTION_PACKAGE_REMOVED with isReplacing=true
                            //    2. ACTION_PACKAGE_ADDED with isReplacing=true
                            //    3. ACTION_PACKAGE_REPLACED
                            //    (Note that ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE and
                            //     ACTION_EXTERNAL_APPLICATIONS_AVAILABLE will be also called with
                            //     isReplacing=true for both ASEC hosted packages and packages in
                            //     external storage)
                            // Since we only want to update session service list once, ignore
                            // actions above when replacing.
                            // Replacing will be handled only once with the ACTION_PACKAGE_REPLACED.
                            break;
                        }
                        // pass-through
                    case Intent.ACTION_PACKAGE_CHANGED:
                    case Intent.ACTION_PACKAGES_SUSPENDED:
                    case Intent.ACTION_PACKAGES_UNSUSPENDED:
                    case Intent.ACTION_PACKAGE_REPLACED:
                        buildMediaSessionService2List();
                }
            }
        }, UserHandle.ALL, filter, null, BackgroundThread.getHandler());
    }

    private void buildMediaSessionService2List() {
        if (DEBUG) {
            Log.d(TAG, "buildMediaSessionService2List");
        }

        // TODO(jaewan): Query per users.
        // TODO(jaewan): Also query for managed profile users.
        // TODO(jaewan): Similar codes are also at the updatable. Can't we share codes?
        PackageManager manager = getContext().getPackageManager();
        List<ResolveInfo> services = new ArrayList<>();
@@ -458,9 +521,13 @@ public class MediaSessionService extends SystemService implements Monitor {
            services.addAll(sessionServices);
        }
        synchronized (mLock) {
            mSessions.clear();
            if (services == null) {
                return;
            // List to keep the session services that need be removed because they don't exist
            // in the 'services' above.
            List<MediaSession2Record> removeCandidates = new ArrayList<>();
            for (int i = 0; i < mSessions.size(); i++) {
                if (mSessions.get(i).getToken().getType() != TYPE_SESSION) {
                    removeCandidates.add(mSessions.get(i));
                }
            }
            for (int i = 0; i < services.size(); i++) {
                if (services.get(i) == null || services.get(i).serviceInfo == null) {
@@ -475,17 +542,35 @@ public class MediaSessionService extends SystemService implements Monitor {
                } catch (NameNotFoundException e) {
                    continue;
                }

                SessionToken2 token;
                try {
                    SessionToken2 token = new SessionToken2(getContext(),
                    token = new SessionToken2(getContext(),
                            serviceInfo.packageName, serviceInfo.name, uid);
                } catch (IllegalArgumentException e) {
                    Log.w(TAG, "Invalid session service", e);
                    continue;
                }
                boolean found = false;
                for (int j = 0; j < mSessions.size(); j++) {
                    if (token.equals(mSessions.get(j).getToken())) {
                        // If the token already exists, keep it in the mSessions.
                        removeCandidates.remove(mSessions.get(j));
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    // New session service is found.
                    MediaSession2Record record = new MediaSession2Record(getContext(),
                            token, mSessionDestroyedListener);
                    mSessions.add(record);
                } catch (IllegalArgumentException e) {
                    Log.d(TAG, "Invalid session service", e);
                }
            }
            for (int i = 0; i < removeCandidates.size(); i++) {
                removeCandidates.get(i).onSessionDestroyed();
                mSessions.remove(removeCandidates.get(i));
            }
            removeCandidates.clear();
        }
        if (DEBUG) {
            Log.d(TAG, "Found " + mSessions.size() + " session services");
@@ -1503,12 +1588,14 @@ public class MediaSessionService extends SystemService implements Monitor {
            return tokens;
        }

        // TODO(jaewan): Protect this API with permission
        @Override
        public void addSessionTokensListener(ISessionTokensListener listener, int userId,
                String packageName) {
            // TODO(jaewan): Implement.
        }

        // TODO(jaewan): Protect this API with permission
        @Override
        public void removeSessionTokensListener(ISessionTokensListener listener) {
            // TODO(jaewan): Implement