1fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown/*
2fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Copyright (C) 2013 The Android Open Source Project
3fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown *
4fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
5fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * you may not use this file except in compliance with the License.
6fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * You may obtain a copy of the License at
7fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown *
8fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
9fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown *
10fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Unless required by applicable law or agreed to in writing, software
11fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
12fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * See the License for the specific language governing permissions and
14fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * limitations under the License.
15fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */
16fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
17b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownpackage android.support.v7.media;
18fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
19fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.BroadcastReceiver;
20fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.ComponentName;
21fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.Context;
22fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.Intent;
23fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.IntentFilter;
24fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.pm.PackageManager;
25fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.pm.ResolveInfo;
26fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.pm.ServiceInfo;
273efa63d3b896244713e84acbb5945562dce41d77Jeff Brownimport android.os.Handler;
28fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
29fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport java.util.ArrayList;
30fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport java.util.Collections;
31fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
32fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown/**
33fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Watches for media route provider services to be installed.
34fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Adds a provider to the media router for each registered service.
35fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown *
36fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see RegisteredMediaRouteProvider
37fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */
38fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownfinal class RegisteredMediaRouteProviderWatcher {
39fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final Context mContext;
403efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    private final Callback mCallback;
413efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    private final Handler mHandler;
423efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    private final PackageManager mPackageManager;
433efa63d3b896244713e84acbb5945562dce41d77Jeff Brown
44fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final ArrayList<RegisteredMediaRouteProvider> mProviders =
45fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            new ArrayList<RegisteredMediaRouteProvider>();
463efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    private boolean mRunning;
47fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
483efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    public RegisteredMediaRouteProviderWatcher(Context context, Callback callback) {
49fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        mContext = context;
503efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        mCallback = callback;
513efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        mHandler = new Handler();
52fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        mPackageManager = context.getPackageManager();
53fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
54fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
55fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public void start() {
563efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        if (!mRunning) {
573efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            mRunning = true;
583efa63d3b896244713e84acbb5945562dce41d77Jeff Brown
593efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            IntentFilter filter = new IntentFilter();
603efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            filter.addAction(Intent.ACTION_PACKAGE_ADDED);
613efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
623efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
63adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown            filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
64adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown            filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
65adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown            filter.addDataScheme("package");
66adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown            mContext.registerReceiver(mScanPackagesReceiver, filter, null, mHandler);
673efa63d3b896244713e84acbb5945562dce41d77Jeff Brown
683efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            // Scan packages.
693efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            // Also has the side-effect of restarting providers if needed.
703efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            mHandler.post(mScanPackagesRunnable);
713efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        }
723efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    }
733efa63d3b896244713e84acbb5945562dce41d77Jeff Brown
743efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    public void stop() {
753efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        if (mRunning) {
763efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            mRunning = false;
773efa63d3b896244713e84acbb5945562dce41d77Jeff Brown
783efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            mContext.unregisterReceiver(mScanPackagesReceiver);
793efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            mHandler.removeCallbacks(mScanPackagesRunnable);
80fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
813efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            // Stop all providers.
823efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            for (int i = mProviders.size() - 1; i >= 0; i--) {
833efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                mProviders.get(i).stop();
843efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            }
853efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        }
86fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
87fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
88fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private void scanPackages() {
893efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        if (!mRunning) {
903efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            return;
913efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        }
923efa63d3b896244713e84acbb5945562dce41d77Jeff Brown
93fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        // Add providers for all new services.
94fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        // Reorder the list so that providers left at the end will be the ones to remove.
95fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        int targetIndex = 0;
96fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        Intent intent = new Intent(MediaRouteProviderService.SERVICE_INTERFACE);
97fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        for (ResolveInfo resolveInfo : mPackageManager.queryIntentServices(intent, 0)) {
98fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            ServiceInfo serviceInfo = resolveInfo.serviceInfo;
99fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (serviceInfo != null) {
100fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
101fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (sourceIndex < 0) {
102fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    RegisteredMediaRouteProvider provider =
103fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            new RegisteredMediaRouteProvider(mContext,
104fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                            new ComponentName(serviceInfo.packageName, serviceInfo.name));
105f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    provider.start();
106fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mProviders.add(targetIndex++, provider);
1073efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                    mCallback.addProvider(provider);
108fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                } else if (sourceIndex >= targetIndex) {
109fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    RegisteredMediaRouteProvider provider = mProviders.get(sourceIndex);
1103efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                    provider.start(); // restart the provider if needed
111fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    provider.rebindIfDisconnected();
112fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    Collections.swap(mProviders, sourceIndex, targetIndex++);
113fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
114fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
115fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
116fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
117fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        // Remove providers for missing services.
118fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (targetIndex < mProviders.size()) {
119fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            for (int i = mProviders.size() - 1; i >= targetIndex; i--) {
120fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                RegisteredMediaRouteProvider provider = mProviders.get(i);
1213efa63d3b896244713e84acbb5945562dce41d77Jeff Brown                mCallback.removeProvider(provider);
122fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mProviders.remove(provider);
123f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                provider.stop();
124fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
125fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
126fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
127fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
128fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private int findProvider(String packageName, String className) {
129fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        int count = mProviders.size();
130fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        for (int i = 0; i < count; i++) {
131fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            RegisteredMediaRouteProvider provider = mProviders.get(i);
132fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (provider.hasComponentName(packageName, className)) {
133fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return i;
134fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
135fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
136fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        return -1;
137fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
1383efa63d3b896244713e84acbb5945562dce41d77Jeff Brown
1393efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    private final BroadcastReceiver mScanPackagesReceiver = new BroadcastReceiver() {
1403efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        @Override
1413efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        public void onReceive(Context context, Intent intent) {
1423efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            scanPackages();
1433efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        }
1443efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    };
1453efa63d3b896244713e84acbb5945562dce41d77Jeff Brown
1463efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    private final Runnable mScanPackagesRunnable = new Runnable() {
1473efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        @Override
1483efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        public void run() {
1493efa63d3b896244713e84acbb5945562dce41d77Jeff Brown            scanPackages();
1503efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        }
1513efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    };
1523efa63d3b896244713e84acbb5945562dce41d77Jeff Brown
1533efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    public interface Callback {
1543efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        void addProvider(MediaRouteProvider provider);
1553efa63d3b896244713e84acbb5945562dce41d77Jeff Brown        void removeProvider(MediaRouteProvider provider);
1563efa63d3b896244713e84acbb5945562dce41d77Jeff Brown    }
157fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown}
158