| /* |
| * Copyright (C) 2013 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 androidx.mediarouter.media; |
| |
| import android.content.BroadcastReceiver; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.content.pm.ServiceInfo; |
| import android.os.Handler; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| |
| /** |
| * Watches for media route provider services to be installed. |
| * Adds a provider to the media router for each registered service. |
| * |
| * @see RegisteredMediaRouteProvider |
| */ |
| final class RegisteredMediaRouteProviderWatcher { |
| private final Context mContext; |
| private final Callback mCallback; |
| private final Handler mHandler; |
| private final PackageManager mPackageManager; |
| |
| private final ArrayList<RegisteredMediaRouteProvider> mProviders = |
| new ArrayList<RegisteredMediaRouteProvider>(); |
| private boolean mRunning; |
| |
| public RegisteredMediaRouteProviderWatcher(Context context, Callback callback) { |
| mContext = context; |
| mCallback = callback; |
| mHandler = new Handler(); |
| mPackageManager = context.getPackageManager(); |
| } |
| |
| public void start() { |
| if (!mRunning) { |
| mRunning = true; |
| |
| IntentFilter filter = new IntentFilter(); |
| filter.addAction(Intent.ACTION_PACKAGE_ADDED); |
| filter.addAction(Intent.ACTION_PACKAGE_REMOVED); |
| filter.addAction(Intent.ACTION_PACKAGE_CHANGED); |
| filter.addAction(Intent.ACTION_PACKAGE_REPLACED); |
| filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); |
| filter.addDataScheme("package"); |
| mContext.registerReceiver(mScanPackagesReceiver, filter, null, mHandler); |
| |
| // Scan packages. |
| // Also has the side-effect of restarting providers if needed. |
| mHandler.post(mScanPackagesRunnable); |
| } |
| } |
| |
| public void stop() { |
| if (mRunning) { |
| mRunning = false; |
| |
| mContext.unregisterReceiver(mScanPackagesReceiver); |
| mHandler.removeCallbacks(mScanPackagesRunnable); |
| |
| // Stop all providers. |
| for (int i = mProviders.size() - 1; i >= 0; i--) { |
| mProviders.get(i).stop(); |
| } |
| } |
| } |
| |
| void scanPackages() { |
| if (!mRunning) { |
| return; |
| } |
| |
| // Add providers for all new services. |
| // Reorder the list so that providers left at the end will be the ones to remove. |
| int targetIndex = 0; |
| Intent intent = new Intent(MediaRouteProviderService.SERVICE_INTERFACE); |
| for (ResolveInfo resolveInfo : mPackageManager.queryIntentServices(intent, 0)) { |
| ServiceInfo serviceInfo = resolveInfo.serviceInfo; |
| if (serviceInfo != null) { |
| int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name); |
| if (sourceIndex < 0) { |
| RegisteredMediaRouteProvider provider = |
| new RegisteredMediaRouteProvider(mContext, |
| new ComponentName(serviceInfo.packageName, serviceInfo.name)); |
| provider.start(); |
| mProviders.add(targetIndex++, provider); |
| mCallback.addProvider(provider); |
| } else if (sourceIndex >= targetIndex) { |
| RegisteredMediaRouteProvider provider = mProviders.get(sourceIndex); |
| provider.start(); // restart the provider if needed |
| provider.rebindIfDisconnected(); |
| Collections.swap(mProviders, sourceIndex, targetIndex++); |
| } |
| } |
| } |
| |
| // Remove providers for missing services. |
| if (targetIndex < mProviders.size()) { |
| for (int i = mProviders.size() - 1; i >= targetIndex; i--) { |
| RegisteredMediaRouteProvider provider = mProviders.get(i); |
| mCallback.removeProvider(provider); |
| mProviders.remove(provider); |
| provider.stop(); |
| } |
| } |
| } |
| |
| private int findProvider(String packageName, String className) { |
| int count = mProviders.size(); |
| for (int i = 0; i < count; i++) { |
| RegisteredMediaRouteProvider provider = mProviders.get(i); |
| if (provider.hasComponentName(packageName, className)) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| private final BroadcastReceiver mScanPackagesReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| scanPackages(); |
| } |
| }; |
| |
| private final Runnable mScanPackagesRunnable = new Runnable() { |
| @Override |
| public void run() { |
| scanPackages(); |
| } |
| }; |
| |
| public interface Callback { |
| void addProvider(MediaRouteProvider provider); |
| void removeProvider(MediaRouteProvider provider); |
| } |
| } |