blob: fe118e51cdcee5af86183de161312aeccc7641b7 [file] [log] [blame]
Kyunglyul Hyund51666d2019-04-11 04:08:40 +00001/*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.media;
18
Hyundo Moone962ed42020-02-16 19:47:41 +090019import android.annotation.NonNull;
Kyunglyul Hyund51666d2019-04-11 04:08:40 +000020import android.content.BroadcastReceiver;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.pm.PackageManager;
26import android.content.pm.ResolveInfo;
27import android.content.pm.ServiceInfo;
28import android.media.MediaRoute2ProviderService;
29import android.os.Handler;
30import android.os.UserHandle;
31import android.util.Log;
32import android.util.Slog;
33
34import java.io.PrintWriter;
35import java.util.ArrayList;
36import java.util.Collections;
37
38/**
Hyundo Moon7d3ab332019-10-16 11:09:56 +090039 * Watches changes of packages, or scan them for finding media route providers.
Kyunglyul Hyund51666d2019-04-11 04:08:40 +000040 */
41final class MediaRoute2ProviderWatcher {
Hyundo Moon7d3ab332019-10-16 11:09:56 +090042 private static final String TAG = "MR2ProviderWatcher";
Kyunglyul Hyund51666d2019-04-11 04:08:40 +000043 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
44
45 private final Context mContext;
46 private final Callback mCallback;
47 private final Handler mHandler;
48 private final int mUserId;
49 private final PackageManager mPackageManager;
50
Hyundo Moon56337492020-02-16 16:19:54 +090051 private final ArrayList<MediaRoute2ProviderServiceProxy> mProxies = new ArrayList<>();
Kyunglyul Hyund51666d2019-04-11 04:08:40 +000052 private boolean mRunning;
53
54 MediaRoute2ProviderWatcher(Context context,
55 Callback callback, Handler handler, int userId) {
56 mContext = context;
57 mCallback = callback;
58 mHandler = handler;
59 mUserId = userId;
60 mPackageManager = context.getPackageManager();
61 }
62
63 public void dump(PrintWriter pw, String prefix) {
64 pw.println(prefix + "Watcher");
65 pw.println(prefix + " mUserId=" + mUserId);
66 pw.println(prefix + " mRunning=" + mRunning);
Hyundo Moon56337492020-02-16 16:19:54 +090067 pw.println(prefix + " mProxies.size()=" + mProxies.size());
Kyunglyul Hyund51666d2019-04-11 04:08:40 +000068 }
69
70 public void start() {
71 if (!mRunning) {
72 mRunning = true;
73
74 IntentFilter filter = new IntentFilter();
75 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
76 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
77 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
78 filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
79 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
80 filter.addDataScheme("package");
81 mContext.registerReceiverAsUser(mScanPackagesReceiver,
82 new UserHandle(mUserId), filter, null, mHandler);
83
84 // Scan packages.
85 // Also has the side-effect of restarting providers if needed.
86 mHandler.post(mScanPackagesRunnable);
87 }
88 }
89
90 public void stop() {
91 if (mRunning) {
92 mRunning = false;
93
94 mContext.unregisterReceiver(mScanPackagesReceiver);
95 mHandler.removeCallbacks(mScanPackagesRunnable);
96
97 // Stop all providers.
Hyundo Moon56337492020-02-16 16:19:54 +090098 for (int i = mProxies.size() - 1; i >= 0; i--) {
99 mProxies.get(i).stop();
Kyunglyul Hyund51666d2019-04-11 04:08:40 +0000100 }
101 }
102 }
103
104 private void scanPackages() {
105 if (!mRunning) {
106 return;
107 }
108
109 // Add providers for all new services.
110 // Reorder the list so that providers left at the end will be the ones to remove.
111 int targetIndex = 0;
112 Intent intent = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE);
113 for (ResolveInfo resolveInfo : mPackageManager.queryIntentServicesAsUser(
114 intent, 0, mUserId)) {
115 ServiceInfo serviceInfo = resolveInfo.serviceInfo;
116 if (serviceInfo != null) {
117 int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
118 if (sourceIndex < 0) {
Hyundo Moon56337492020-02-16 16:19:54 +0900119 MediaRoute2ProviderServiceProxy proxy =
120 new MediaRoute2ProviderServiceProxy(mContext,
Kyunglyul Hyund51666d2019-04-11 04:08:40 +0000121 new ComponentName(serviceInfo.packageName, serviceInfo.name),
122 mUserId);
Hyundo Moon56337492020-02-16 16:19:54 +0900123 proxy.start();
124 mProxies.add(targetIndex++, proxy);
125 mCallback.onAddProviderService(proxy);
Kyunglyul Hyund51666d2019-04-11 04:08:40 +0000126 } else if (sourceIndex >= targetIndex) {
Hyundo Moon56337492020-02-16 16:19:54 +0900127 MediaRoute2ProviderServiceProxy proxy = mProxies.get(sourceIndex);
128 proxy.start(); // restart the proxy if needed
129 proxy.rebindIfDisconnected();
130 Collections.swap(mProxies, sourceIndex, targetIndex++);
Kyunglyul Hyund51666d2019-04-11 04:08:40 +0000131 }
132 }
133 }
134
135 // Remove providers for missing services.
Hyundo Moon56337492020-02-16 16:19:54 +0900136 if (targetIndex < mProxies.size()) {
137 for (int i = mProxies.size() - 1; i >= targetIndex; i--) {
138 MediaRoute2ProviderServiceProxy proxy = mProxies.get(i);
139 mCallback.onRemoveProviderService(proxy);
140 mProxies.remove(proxy);
141 proxy.stop();
Kyunglyul Hyund51666d2019-04-11 04:08:40 +0000142 }
143 }
144 }
145
146 private int findProvider(String packageName, String className) {
Hyundo Moon56337492020-02-16 16:19:54 +0900147 int count = mProxies.size();
Kyunglyul Hyund51666d2019-04-11 04:08:40 +0000148 for (int i = 0; i < count; i++) {
Hyundo Moon56337492020-02-16 16:19:54 +0900149 MediaRoute2ProviderServiceProxy proxy = mProxies.get(i);
150 if (proxy.hasComponentName(packageName, className)) {
Kyunglyul Hyund51666d2019-04-11 04:08:40 +0000151 return i;
152 }
153 }
154 return -1;
155 }
156
157 private final BroadcastReceiver mScanPackagesReceiver = new BroadcastReceiver() {
158 @Override
159 public void onReceive(Context context, Intent intent) {
160 if (DEBUG) {
161 Slog.d(TAG, "Received package manager broadcast: " + intent);
162 }
163 scanPackages();
164 }
165 };
166
167 private final Runnable mScanPackagesRunnable = new Runnable() {
168 @Override
169 public void run() {
170 scanPackages();
171 }
172 };
173
174 public interface Callback {
Hyundo Moone962ed42020-02-16 19:47:41 +0900175 void onAddProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy);
176 void onRemoveProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy);
Kyunglyul Hyund51666d2019-04-11 04:08:40 +0000177 }
178}