169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown/*
269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * Copyright (C) 2013 The Android Open Source Project
369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown *
469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * you may not use this file except in compliance with the License.
669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * You may obtain a copy of the License at
769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown *
869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown *
1069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * Unless required by applicable law or agreed to in writing, software
1169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
1269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * See the License for the specific language governing permissions and
1469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * limitations under the License.
1569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown */
1669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
1769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownpackage com.android.server.media;
1869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
19fe9a53bc45fd0124a876dc0a49680aaf86641d3eJeff Sharkeyimport com.android.internal.util.DumpUtils;
2069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport com.android.server.Watchdog;
2169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
2269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.Manifest;
2369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.app.ActivityManager;
2469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.content.BroadcastReceiver;
2569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.content.Context;
2669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.content.Intent;
2769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.content.IntentFilter;
2869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.content.pm.PackageManager;
2969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.media.AudioSystem;
3069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.media.IMediaRouterClient;
3169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.media.IMediaRouterService;
3269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.media.MediaRouter;
3369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.media.MediaRouterClientState;
3469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.media.RemoteDisplayState;
3569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.media.RemoteDisplayState.RemoteDisplayInfo;
3669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.Binder;
3769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.Handler;
3869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.IBinder;
3969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.Looper;
4069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.Message;
4169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.RemoteException;
4269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.SystemClock;
4369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.text.TextUtils;
4469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.util.ArrayMap;
4569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.util.Log;
4669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.util.Slog;
4769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.util.SparseArray;
4869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.util.TimeUtils;
4969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
5069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport java.io.FileDescriptor;
5169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport java.io.PrintWriter;
5269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport java.util.ArrayList;
5369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport java.util.Collections;
5469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport java.util.List;
55e6585b32ea586743258a5457e2184ffc087f2d2fKenny Rootimport java.util.Objects;
5669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
5769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown/**
5869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * Provides a mechanism for discovering media routes and manages media playback
5969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * behalf of applications.
6069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * <p>
6169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * Currently supports discovering remote displays via remote display provider
6269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * services that have been registered by applications.
6369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * </p>
6469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown */
6569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownpublic final class MediaRouterService extends IMediaRouterService.Stub
6669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        implements Watchdog.Monitor {
6769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private static final String TAG = "MediaRouterService";
6869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
6969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
7069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
7169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Timeout in milliseconds for a selected route to transition from a
7269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * disconnected state to a connecting state.  If we don't observe any
7369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * progress within this interval, then we will give up and unselect the route.
7469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
7569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static final long CONNECTING_TIMEOUT = 5000;
7669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
7769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
7869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Timeout in milliseconds for a selected route to transition from a
7969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * connecting state to a connected state.  If we don't observe any
8069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * progress within this interval, then we will give up and unselect the route.
8169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
8269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static final long CONNECTED_TIMEOUT = 60000;
8369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
8469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private final Context mContext;
8569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
8669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // State guarded by mLock.
8769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private final Object mLock = new Object();
8869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
8969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private final ArrayMap<IBinder, ClientRecord> mAllClientRecords =
9069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            new ArrayMap<IBinder, ClientRecord>();
9169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private int mCurrentUserId = -1;
9269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public MediaRouterService(Context context) {
9469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        mContext = context;
9569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        Watchdog.getInstance().addMonitor(this);
9669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
9769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
9869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void systemRunning() {
9969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
10069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        mContext.registerReceiver(new BroadcastReceiver() {
10169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
10269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public void onReceive(Context context, Intent intent) {
10369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (intent.getAction().equals(Intent.ACTION_USER_SWITCHED)) {
10469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    switchUser();
10569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
10669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
10769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }, filter);
10869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
10969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        switchUser();
11069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
11169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
11269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
11369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void monitor() {
11469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        synchronized (mLock) { /* check for deadlock */ }
11569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
11669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
11769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
11869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
11969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void registerClientAsUser(IMediaRouterClient client, String packageName, int userId) {
12069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (client == null) {
12169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("client must not be null");
12269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
12369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
12469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final int uid = Binder.getCallingUid();
12569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (!validatePackageName(uid, packageName)) {
12669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new SecurityException("packageName must match the calling uid");
12769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
12869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
12969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final int pid = Binder.getCallingPid();
13069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
13169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                false /*allowAll*/, true /*requireFull*/, "registerClientAsUser", packageName);
132af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        final boolean trusted = mContext.checkCallingOrSelfPermission(
133af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) ==
134af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                PackageManager.PERMISSION_GRANTED;
13569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final long token = Binder.clearCallingIdentity();
13669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        try {
13769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mLock) {
138af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                registerClientLocked(client, pid, packageName, resolvedUserId, trusted);
13969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
14069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } finally {
14169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Binder.restoreCallingIdentity(token);
14269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
14369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
14469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
14569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
14669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
14769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void unregisterClient(IMediaRouterClient client) {
14869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (client == null) {
14969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("client must not be null");
15069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
15169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
15269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final long token = Binder.clearCallingIdentity();
15369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        try {
15469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mLock) {
15569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                unregisterClientLocked(client, false);
15669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
15769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } finally {
15869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Binder.restoreCallingIdentity(token);
15969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
16069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
16169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
16269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
16369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
16469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public MediaRouterClientState getState(IMediaRouterClient client) {
16569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (client == null) {
16669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("client must not be null");
16769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
16869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
16969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final long token = Binder.clearCallingIdentity();
17069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        try {
17169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mLock) {
17269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return getStateLocked(client);
17369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
17469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } finally {
17569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Binder.restoreCallingIdentity(token);
17669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
17769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
17869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
17969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
18069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
18169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void setDiscoveryRequest(IMediaRouterClient client,
18269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int routeTypes, boolean activeScan) {
18369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (client == null) {
18469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("client must not be null");
18569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
18669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
18769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final long token = Binder.clearCallingIdentity();
18869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        try {
18969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mLock) {
19069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                setDiscoveryRequestLocked(client, routeTypes, activeScan);
19169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
19269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } finally {
19369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Binder.restoreCallingIdentity(token);
19469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
19569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
19669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
19769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
19869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // A null routeId means that the client wants to unselect its current route.
19969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // The explicit flag indicates whether the change was explicitly requested by the
20069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // user or the application which may cause changes to propagate out to the rest
20169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // of the system.  Should be false when the change is in response to a new globally
20269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // selected route or a default selection.
20369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
20469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit) {
20569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (client == null) {
20669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("client must not be null");
20769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
20869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
20969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final long token = Binder.clearCallingIdentity();
21069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        try {
21169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mLock) {
21269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                setSelectedRouteLocked(client, routeId, explicit);
21369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
21469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } finally {
21569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Binder.restoreCallingIdentity(token);
21669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
21769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
21869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
21969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
22069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
22169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void requestSetVolume(IMediaRouterClient client, String routeId, int volume) {
22269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (client == null) {
22369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("client must not be null");
22469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
22569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (routeId == null) {
22669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("routeId must not be null");
22769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
22869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
22969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final long token = Binder.clearCallingIdentity();
23069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        try {
23169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mLock) {
23269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                requestSetVolumeLocked(client, routeId, volume);
23369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
23469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } finally {
23569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Binder.restoreCallingIdentity(token);
23669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
23769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
23869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
23969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
24069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
24169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction) {
24269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (client == null) {
24369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("client must not be null");
24469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
24569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (routeId == null) {
24669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("routeId must not be null");
24769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
24869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
24969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final long token = Binder.clearCallingIdentity();
25069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        try {
25169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mLock) {
25269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                requestUpdateVolumeLocked(client, routeId, direction);
25369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
25469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } finally {
25569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Binder.restoreCallingIdentity(token);
25669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
25769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
25869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
25969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
26069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
26169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
262fe9a53bc45fd0124a876dc0a49680aaf86641d3eJeff Sharkey        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
26369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
26469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        pw.println("MEDIA ROUTER SERVICE (dumpsys media_router)");
26569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        pw.println();
26669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        pw.println("Global state");
26769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        pw.println("  mCurrentUserId=" + mCurrentUserId);
26869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
26969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        synchronized (mLock) {
27069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mUserRecords.size();
27169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
27269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                UserRecord userRecord = mUserRecords.valueAt(i);
27369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println();
27469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                userRecord.dump(pw, "");
27569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
27669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
27769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
27869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
27969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    void switchUser() {
28069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        synchronized (mLock) {
28169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int userId = ActivityManager.getCurrentUser();
28269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mCurrentUserId != userId) {
28369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int oldUserId = mCurrentUserId;
28469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mCurrentUserId = userId; // do this first
28569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
28669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                UserRecord oldUser = mUserRecords.get(oldUserId);
28769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (oldUser != null) {
28869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    oldUser.mHandler.sendEmptyMessage(UserHandler.MSG_STOP);
28969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    disposeUserIfNeededLocked(oldUser); // since no longer current user
29069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
29169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
29269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                UserRecord newUser = mUserRecords.get(userId);
29369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (newUser != null) {
29469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    newUser.mHandler.sendEmptyMessage(UserHandler.MSG_START);
29569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
29669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
29769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
29869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
29969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
30069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    void clientDied(ClientRecord clientRecord) {
30169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        synchronized (mLock) {
30269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            unregisterClientLocked(clientRecord.mClient, true);
30369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
30469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
30569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
30669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void registerClientLocked(IMediaRouterClient client,
307af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            int pid, String packageName, int userId, boolean trusted) {
30869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final IBinder binder = client.asBinder();
30969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        ClientRecord clientRecord = mAllClientRecords.get(binder);
31069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord == null) {
31169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean newUser = false;
31269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            UserRecord userRecord = mUserRecords.get(userId);
31369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (userRecord == null) {
31469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                userRecord = new UserRecord(userId);
31569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                newUser = true;
31669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
317af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            clientRecord = new ClientRecord(userRecord, client, pid, packageName, trusted);
31869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            try {
31969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                binder.linkToDeath(clientRecord, 0);
32069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } catch (RemoteException ex) {
32169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                throw new RuntimeException("Media router client died prematurely.", ex);
32269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
32369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
32469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (newUser) {
32569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mUserRecords.put(userId, userRecord);
32669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                initializeUserLocked(userRecord);
32769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
32869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
32969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            userRecord.mClientRecords.add(clientRecord);
33069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mAllClientRecords.put(binder, clientRecord);
33169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            initializeClientLocked(clientRecord);
33269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
33369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
33469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
33569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void unregisterClientLocked(IMediaRouterClient client, boolean died) {
33669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        ClientRecord clientRecord = mAllClientRecords.remove(client.asBinder());
33769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord != null) {
33869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            UserRecord userRecord = clientRecord.mUserRecord;
33969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            userRecord.mClientRecords.remove(clientRecord);
34069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            disposeClientLocked(clientRecord, died);
34169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            disposeUserIfNeededLocked(userRecord); // since client removed from user
34269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
34369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
34469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
34569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private MediaRouterClientState getStateLocked(IMediaRouterClient client) {
34669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
34769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord != null) {
348af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            return clientRecord.getState();
34969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
35069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        return null;
35169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
35269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
35369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void setDiscoveryRequestLocked(IMediaRouterClient client,
35469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int routeTypes, boolean activeScan) {
35569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final IBinder binder = client.asBinder();
35669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        ClientRecord clientRecord = mAllClientRecords.get(binder);
35769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord != null) {
358af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Only let the system discover remote display routes for now.
359af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (!clientRecord.mTrusted) {
360af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                routeTypes &= ~MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
361af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
362af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
36369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (clientRecord.mRouteTypes != routeTypes
36469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    || clientRecord.mActiveScan != activeScan) {
36569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (DEBUG) {
36669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Slog.d(TAG, clientRecord + ": Set discovery request, routeTypes=0x"
36769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + Integer.toHexString(routeTypes) + ", activeScan=" + activeScan);
36869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
36969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                clientRecord.mRouteTypes = routeTypes;
37069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                clientRecord.mActiveScan = activeScan;
37169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                clientRecord.mUserRecord.mHandler.sendEmptyMessage(
37269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        UserHandler.MSG_UPDATE_DISCOVERY_REQUEST);
37369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
37469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
37569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
37669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
37769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void setSelectedRouteLocked(IMediaRouterClient client,
37869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            String routeId, boolean explicit) {
37969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
38069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord != null) {
38169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final String oldRouteId = clientRecord.mSelectedRouteId;
382e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            if (!Objects.equals(routeId, oldRouteId)) {
38369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (DEBUG) {
38469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Slog.d(TAG, clientRecord + ": Set selected route, routeId=" + routeId
38569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + ", oldRouteId=" + oldRouteId
38669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + ", explicit=" + explicit);
38769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
38869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
38969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                clientRecord.mSelectedRouteId = routeId;
39069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (explicit) {
391af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    // Any app can disconnect from the globally selected route.
39269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (oldRouteId != null) {
39369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        clientRecord.mUserRecord.mHandler.obtainMessage(
39469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                UserHandler.MSG_UNSELECT_ROUTE, oldRouteId).sendToTarget();
39569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
396af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    // Only let the system connect to new global routes for now.
397af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    // A similar check exists in the display manager for wifi display.
398af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    if (routeId != null && clientRecord.mTrusted) {
39969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        clientRecord.mUserRecord.mHandler.obtainMessage(
40069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                UserHandler.MSG_SELECT_ROUTE, routeId).sendToTarget();
40169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
40269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
40369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
40469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
40569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
40669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
40769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void requestSetVolumeLocked(IMediaRouterClient client,
40869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            String routeId, int volume) {
40969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final IBinder binder = client.asBinder();
41069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        ClientRecord clientRecord = mAllClientRecords.get(binder);
41169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord != null) {
41269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            clientRecord.mUserRecord.mHandler.obtainMessage(
41369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    UserHandler.MSG_REQUEST_SET_VOLUME, volume, 0, routeId).sendToTarget();
41469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
41569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
41669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
41769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void requestUpdateVolumeLocked(IMediaRouterClient client,
41869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            String routeId, int direction) {
41969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final IBinder binder = client.asBinder();
42069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        ClientRecord clientRecord = mAllClientRecords.get(binder);
42169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord != null) {
42269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            clientRecord.mUserRecord.mHandler.obtainMessage(
42369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    UserHandler.MSG_REQUEST_UPDATE_VOLUME, direction, 0, routeId).sendToTarget();
42469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
42569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
42669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
42769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void initializeUserLocked(UserRecord userRecord) {
42869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (DEBUG) {
42969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Slog.d(TAG, userRecord + ": Initialized");
43069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
43169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (userRecord.mUserId == mCurrentUserId) {
43269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_START);
43369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
43469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
43569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
43669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void disposeUserIfNeededLocked(UserRecord userRecord) {
43769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // If there are no records left and the user is no longer current then go ahead
43869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // and purge the user record and all of its associated state.  If the user is current
43969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // then leave it alone since we might be connected to a route or want to query
44069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // the same route information again soon.
44169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (userRecord.mUserId != mCurrentUserId
44269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                && userRecord.mClientRecords.isEmpty()) {
44369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (DEBUG) {
44469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                Slog.d(TAG, userRecord + ": Disposed");
44569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
44669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mUserRecords.remove(userRecord.mUserId);
44769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Note: User already stopped (by switchUser) so no need to send stop message here.
44869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
44969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
45069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
45169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void initializeClientLocked(ClientRecord clientRecord) {
45269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (DEBUG) {
45369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Slog.d(TAG, clientRecord + ": Registered");
45469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
45569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
45669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
45769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void disposeClientLocked(ClientRecord clientRecord, boolean died) {
45869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (DEBUG) {
45969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (died) {
46069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                Slog.d(TAG, clientRecord + ": Died!");
46169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } else {
46269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                Slog.d(TAG, clientRecord + ": Unregistered");
46369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
46469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
46569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord.mRouteTypes != 0 || clientRecord.mActiveScan) {
46669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            clientRecord.mUserRecord.mHandler.sendEmptyMessage(
46769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    UserHandler.MSG_UPDATE_DISCOVERY_REQUEST);
46869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
46969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        clientRecord.dispose();
47069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
47169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
47269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private boolean validatePackageName(int uid, String packageName) {
47369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (packageName != null) {
47469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
47569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (packageNames != null) {
47669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (String n : packageNames) {
47769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (n.equals(packageName)) {
47869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return true;
47969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
48069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
48169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
48269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
48369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        return false;
48469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
48569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
48669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
48769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Information about a particular client of the media router.
48869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * The contents of this object is guarded by mLock.
48969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
49069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    final class ClientRecord implements DeathRecipient {
49169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public final UserRecord mUserRecord;
49269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public final IMediaRouterClient mClient;
49369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public final int mPid;
49469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public final String mPackageName;
495af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        public final boolean mTrusted;
49669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
49769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public int mRouteTypes;
49869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public boolean mActiveScan;
49969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public String mSelectedRouteId;
50069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
50169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public ClientRecord(UserRecord userRecord, IMediaRouterClient client,
502af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                int pid, String packageName, boolean trusted) {
50369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mUserRecord = userRecord;
50469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mClient = client;
50569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mPid = pid;
50669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mPackageName = packageName;
507af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            mTrusted = trusted;
50869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
50969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
51069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void dispose() {
51169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mClient.asBinder().unlinkToDeath(this, 0);
51269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
51369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
51469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
51569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void binderDied() {
51669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            clientDied(this);
51769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
51869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
519af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        MediaRouterClientState getState() {
520af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            return mTrusted ? mUserRecord.mTrustedState : mUserRecord.mUntrustedState;
521af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        }
522af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
52369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void dump(PrintWriter pw, String prefix) {
52469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(prefix + this);
52569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
52669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final String indent = prefix + "  ";
527af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            pw.println(indent + "mTrusted=" + mTrusted);
52869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mRouteTypes=0x" + Integer.toHexString(mRouteTypes));
52969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mActiveScan=" + mActiveScan);
53069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mSelectedRouteId=" + mSelectedRouteId);
53169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
53269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
53369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
53469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public String toString() {
53569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return "Client " + mPackageName + " (pid " + mPid + ")";
53669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
53769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
53869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
53969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
54069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Information about a particular user.
54169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * The contents of this object is guarded by mLock.
54269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
54369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    final class UserRecord {
54469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public final int mUserId;
54569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public final ArrayList<ClientRecord> mClientRecords = new ArrayList<ClientRecord>();
54669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public final UserHandler mHandler;
547af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        public MediaRouterClientState mTrustedState;
548af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        public MediaRouterClientState mUntrustedState;
54969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
55069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public UserRecord(int userId) {
55169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mUserId = userId;
55269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mHandler = new UserHandler(MediaRouterService.this, this);
55369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
55469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
55569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void dump(final PrintWriter pw, String prefix) {
55669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(prefix + this);
55769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
55869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final String indent = prefix + "  ";
55969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int clientCount = mClientRecords.size();
56069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (clientCount != 0) {
56169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < clientCount; i++) {
56269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientRecords.get(i).dump(pw, indent);
56369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
56469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } else {
56569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(indent + "<no clients>");
56669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
56769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
568af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            pw.println(indent + "State");
569af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            pw.println(indent + "mTrustedState=" + mTrustedState);
570af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            pw.println(indent + "mUntrustedState=" + mUntrustedState);
571af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
57269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (!mHandler.runWithScissors(new Runnable() {
57369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                @Override
57469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                public void run() {
57569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mHandler.dump(pw, indent);
57669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
57769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }, 1000)) {
57869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(indent + "<could not dump handler state>");
57969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
58069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         }
58169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
58269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
58369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public String toString() {
58469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return "User " + mUserId;
58569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
58669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
58769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
58869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
58969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Media router handler
59069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * <p>
59169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Since remote display providers are designed to be single-threaded by nature,
59269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * this class encapsulates all of the associated functionality and exports state
59369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * to the service as it evolves.
59469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * </p><p>
59569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * One important task of this class is to keep track of the current globally selected
59669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * route id for certain routes that have global effects, such as remote displays.
59769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Global route selections override local selections made within apps.  The change
59869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * is propagated to all apps so that they are all in sync.  Synchronization works
59969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * both ways.  Whenever the globally selected route is explicitly unselected by any
60069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * app, then it becomes unselected globally and all apps are informed.
60169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * </p><p>
60269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * This class is currently hardcoded to work with remote display providers but
60369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * it is intended to be eventually extended to support more general route providers
60469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * similar to the support library media router.
60569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * </p>
60669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
60769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static final class UserHandler extends Handler
60869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            implements RemoteDisplayProviderWatcher.Callback,
60969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            RemoteDisplayProviderProxy.Callback {
61069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public static final int MSG_START = 1;
61169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public static final int MSG_STOP = 2;
61269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public static final int MSG_UPDATE_DISCOVERY_REQUEST = 3;
61369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public static final int MSG_SELECT_ROUTE = 4;
61469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public static final int MSG_UNSELECT_ROUTE = 5;
61569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public static final int MSG_REQUEST_SET_VOLUME = 6;
61669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public static final int MSG_REQUEST_UPDATE_VOLUME = 7;
61769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private static final int MSG_UPDATE_CLIENT_STATE = 8;
61869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private static final int MSG_CONNECTION_TIMED_OUT = 9;
61969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
62069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private static final int TIMEOUT_REASON_NOT_AVAILABLE = 1;
62139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static final int TIMEOUT_REASON_CONNECTION_LOST = 2;
62239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static final int TIMEOUT_REASON_WAITING_FOR_CONNECTING = 3;
62339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static final int TIMEOUT_REASON_WAITING_FOR_CONNECTED = 4;
62439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
62539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        // The relative order of these constants is important and expresses progress
62639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        // through the process of connecting to a route.
62739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static final int PHASE_NOT_AVAILABLE = -1;
62839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static final int PHASE_NOT_CONNECTED = 0;
62939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static final int PHASE_CONNECTING = 1;
63039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static final int PHASE_CONNECTED = 2;
63169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
63269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private final MediaRouterService mService;
63369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private final UserRecord mUserRecord;
63469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private final RemoteDisplayProviderWatcher mWatcher;
63569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private final ArrayList<ProviderRecord> mProviderRecords =
63669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                new ArrayList<ProviderRecord>();
63769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private final ArrayList<IMediaRouterClient> mTempClients =
63869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                new ArrayList<IMediaRouterClient>();
63969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
64069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private boolean mRunning;
64169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private int mDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_NONE;
64269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private RouteRecord mGloballySelectedRouteRecord;
64339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private int mConnectionPhase = PHASE_NOT_AVAILABLE;
64469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private int mConnectionTimeoutReason;
64569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private long mConnectionTimeoutStartTime;
64669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private boolean mClientStateUpdateScheduled;
64769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
64869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public UserHandler(MediaRouterService service, UserRecord userRecord) {
64969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            super(Looper.getMainLooper(), null, true);
65069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mService = service;
65169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mUserRecord = userRecord;
65269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mWatcher = new RemoteDisplayProviderWatcher(service.mContext, this,
65369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    this, mUserRecord.mUserId);
65469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
65569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
65669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
65769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void handleMessage(Message msg) {
65869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            switch (msg.what) {
65969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_START: {
66069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    start();
66169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
66269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
66369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_STOP: {
66469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    stop();
66569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
66669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
66769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_UPDATE_DISCOVERY_REQUEST: {
66869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateDiscoveryRequest();
66969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
67069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
67169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_SELECT_ROUTE: {
67269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRoute((String)msg.obj);
67369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
67469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
67569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_UNSELECT_ROUTE: {
67669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    unselectRoute((String)msg.obj);
67769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
67869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
67969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_REQUEST_SET_VOLUME: {
68069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    requestSetVolume((String)msg.obj, msg.arg1);
68169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
68269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
68369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_REQUEST_UPDATE_VOLUME: {
68469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    requestUpdateVolume((String)msg.obj, msg.arg1);
68569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
68669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
68769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_UPDATE_CLIENT_STATE: {
68869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateClientState();
68969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
69069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
69169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_CONNECTION_TIMED_OUT: {
69269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    connectionTimedOut();
69369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
69469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
69569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
69669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
69769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
69869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void dump(PrintWriter pw, String prefix) {
69969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(prefix + "Handler");
70069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
70169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final String indent = prefix + "  ";
70269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mRunning=" + mRunning);
70369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mDiscoveryMode=" + mDiscoveryMode);
70469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mGloballySelectedRouteRecord=" + mGloballySelectedRouteRecord);
70539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            pw.println(indent + "mConnectionPhase=" + mConnectionPhase);
70669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mConnectionTimeoutReason=" + mConnectionTimeoutReason);
70769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mConnectionTimeoutStartTime=" + (mConnectionTimeoutReason != 0 ?
70869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    TimeUtils.formatUptime(mConnectionTimeoutStartTime) : "<n/a>"));
70969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
71069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mWatcher.dump(pw, prefix);
71169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
71269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int providerCount = mProviderRecords.size();
71369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (providerCount != 0) {
71469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < providerCount; i++) {
71569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mProviderRecords.get(i).dump(pw, prefix);
71669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
71769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } else {
71869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(indent + "<no providers>");
71969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
72069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
72169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
72269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void start() {
72369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (!mRunning) {
72469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mRunning = true;
72569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mWatcher.start(); // also starts all providers
72669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
72769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
72869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
72969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void stop() {
73069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mRunning) {
73169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mRunning = false;
73269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                unselectGloballySelectedRoute();
73369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mWatcher.stop(); // also stops all providers
73469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
73569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
73669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
73769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void updateDiscoveryRequest() {
73869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int routeTypes = 0;
73969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean activeScan = false;
74069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mService.mLock) {
74169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int count = mUserRecord.mClientRecords.size();
74269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < count; i++) {
74369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    ClientRecord clientRecord = mUserRecord.mClientRecords.get(i);
74469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeTypes |= clientRecord.mRouteTypes;
74569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    activeScan |= clientRecord.mActiveScan;
74669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
74769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
74869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
74969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int newDiscoveryMode;
750af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if ((routeTypes & MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
75169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (activeScan) {
75269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    newDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_ACTIVE;
75369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
75469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    newDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_PASSIVE;
75569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
75669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } else {
75769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                newDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_NONE;
75869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
75969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
76069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mDiscoveryMode != newDiscoveryMode) {
76169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDiscoveryMode = newDiscoveryMode;
76269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int count = mProviderRecords.size();
76369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < count; i++) {
76469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mProviderRecords.get(i).getProvider().setDiscoveryMode(mDiscoveryMode);
76569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
76669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
76769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
76869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
76969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void selectRoute(String routeId) {
77069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeId != null
77169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    && (mGloballySelectedRouteRecord == null
77269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            || !routeId.equals(mGloballySelectedRouteRecord.getUniqueId()))) {
77369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                RouteRecord routeRecord = findRouteRecord(routeId);
77469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (routeRecord != null) {
77569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    unselectGloballySelectedRoute();
77669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
77769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Slog.i(TAG, "Selected global route:" + routeRecord);
77869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mGloballySelectedRouteRecord = routeRecord;
77969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    checkGloballySelectedRouteState();
78069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeRecord.getProvider().setSelectedDisplay(routeRecord.getDescriptorId());
78169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
78269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    scheduleUpdateClientState();
78369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
78469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
78569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
78669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
78769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void unselectRoute(String routeId) {
78869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeId != null
78969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    && mGloballySelectedRouteRecord != null
79069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    && routeId.equals(mGloballySelectedRouteRecord.getUniqueId())) {
79169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                unselectGloballySelectedRoute();
79269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
79369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
79469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
79569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void unselectGloballySelectedRoute() {
79669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mGloballySelectedRouteRecord != null) {
79769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                Slog.i(TAG, "Unselected global route:" + mGloballySelectedRouteRecord);
79869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mGloballySelectedRouteRecord.getProvider().setSelectedDisplay(null);
79969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mGloballySelectedRouteRecord = null;
80069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                checkGloballySelectedRouteState();
80169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
80269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                scheduleUpdateClientState();
80369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
80469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
80569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
80669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void requestSetVolume(String routeId, int volume) {
80769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mGloballySelectedRouteRecord != null
80869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    && routeId.equals(mGloballySelectedRouteRecord.getUniqueId())) {
80969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mGloballySelectedRouteRecord.getProvider().setDisplayVolume(volume);
81069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
81169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
81269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
81369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void requestUpdateVolume(String routeId, int direction) {
81469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mGloballySelectedRouteRecord != null
81569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    && routeId.equals(mGloballySelectedRouteRecord.getUniqueId())) {
81669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mGloballySelectedRouteRecord.getProvider().adjustDisplayVolume(direction);
81769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
81869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
81969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
82069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
82169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void addProvider(RemoteDisplayProviderProxy provider) {
82269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            provider.setCallback(this);
82369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            provider.setDiscoveryMode(mDiscoveryMode);
82469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            provider.setSelectedDisplay(null); // just to be safe
82569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
82669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            ProviderRecord providerRecord = new ProviderRecord(provider);
82769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mProviderRecords.add(providerRecord);
82869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            providerRecord.updateDescriptor(provider.getDisplayState());
82969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
83069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            scheduleUpdateClientState();
83169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
83269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
83369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
83469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void removeProvider(RemoteDisplayProviderProxy provider) {
83569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int index = findProviderRecord(provider);
83669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (index >= 0) {
83769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                ProviderRecord providerRecord = mProviderRecords.remove(index);
83869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                providerRecord.updateDescriptor(null); // mark routes invalid
83969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                provider.setCallback(null);
84069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                provider.setDiscoveryMode(RemoteDisplayState.DISCOVERY_MODE_NONE);
84169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
84269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                checkGloballySelectedRouteState();
84369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                scheduleUpdateClientState();
84469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
84569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
84669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
84769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
84869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void onDisplayStateChanged(RemoteDisplayProviderProxy provider,
84969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                RemoteDisplayState state) {
85069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            updateProvider(provider, state);
85169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
85269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
85369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void updateProvider(RemoteDisplayProviderProxy provider,
85469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                RemoteDisplayState state) {
85569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int index = findProviderRecord(provider);
85669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (index >= 0) {
85769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                ProviderRecord providerRecord = mProviderRecords.get(index);
85869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (providerRecord.updateDescriptor(state)) {
85969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    checkGloballySelectedRouteState();
86069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    scheduleUpdateClientState();
86169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
86269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
86369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
86469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
86569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        /**
86669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * This function is called whenever the state of the globally selected route
86769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * may have changed.  It checks the state and updates timeouts or unselects
86869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * the route as appropriate.
86969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         */
87069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void checkGloballySelectedRouteState() {
87169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Unschedule timeouts when the route is unselected.
87269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mGloballySelectedRouteRecord == null) {
87339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                mConnectionPhase = PHASE_NOT_AVAILABLE;
87469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                updateConnectionTimeout(0);
87569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return;
87669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
87769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
87869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Ensure that the route is still present and enabled.
87969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (!mGloballySelectedRouteRecord.isValid()
88069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    || !mGloballySelectedRouteRecord.isEnabled()) {
88169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                updateConnectionTimeout(TIMEOUT_REASON_NOT_AVAILABLE);
88269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return;
88369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
88469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
88539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            // Make sure we haven't lost our connection.
88639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            final int oldPhase = mConnectionPhase;
88739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            mConnectionPhase = getConnectionPhase(mGloballySelectedRouteRecord.getStatus());
88839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (oldPhase >= PHASE_CONNECTING && mConnectionPhase < PHASE_CONNECTING) {
88939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                updateConnectionTimeout(TIMEOUT_REASON_CONNECTION_LOST);
89039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                return;
89139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
89239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
89369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Check the route status.
89439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            switch (mConnectionPhase) {
89539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case PHASE_CONNECTED:
89639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    if (oldPhase != PHASE_CONNECTED) {
89769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Slog.i(TAG, "Connected to global route: "
89869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                + mGloballySelectedRouteRecord);
89969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
90069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateConnectionTimeout(0);
90169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
90239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case PHASE_CONNECTING:
90339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    if (oldPhase != PHASE_CONNECTING) {
90469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Slog.i(TAG, "Connecting to global route: "
90569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                + mGloballySelectedRouteRecord);
90669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
90769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateConnectionTimeout(TIMEOUT_REASON_WAITING_FOR_CONNECTED);
90869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
90939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case PHASE_NOT_CONNECTED:
91069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateConnectionTimeout(TIMEOUT_REASON_WAITING_FOR_CONNECTING);
91169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
91239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case PHASE_NOT_AVAILABLE:
91369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                default:
91469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateConnectionTimeout(TIMEOUT_REASON_NOT_AVAILABLE);
91569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
91669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
91769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
91869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
91969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void updateConnectionTimeout(int reason) {
92069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (reason != mConnectionTimeoutReason) {
92169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (mConnectionTimeoutReason != 0) {
92269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    removeMessages(MSG_CONNECTION_TIMED_OUT);
92369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
92469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mConnectionTimeoutReason = reason;
92569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mConnectionTimeoutStartTime = SystemClock.uptimeMillis();
92669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                switch (reason) {
92769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case TIMEOUT_REASON_NOT_AVAILABLE:
92839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    case TIMEOUT_REASON_CONNECTION_LOST:
92939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                        // Route became unavailable or connection lost.
93039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                        // Unselect it immediately.
93169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        sendEmptyMessage(MSG_CONNECTION_TIMED_OUT);
93269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        break;
93369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case TIMEOUT_REASON_WAITING_FOR_CONNECTING:
93469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        // Waiting for route to start connecting.
93569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        sendEmptyMessageDelayed(MSG_CONNECTION_TIMED_OUT, CONNECTING_TIMEOUT);
93669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        break;
93769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case TIMEOUT_REASON_WAITING_FOR_CONNECTED:
93869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        // Waiting for route to complete connection.
93969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        sendEmptyMessageDelayed(MSG_CONNECTION_TIMED_OUT, CONNECTED_TIMEOUT);
94069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        break;
94169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
94269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
94369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
94469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
94569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void connectionTimedOut() {
94669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mConnectionTimeoutReason == 0 || mGloballySelectedRouteRecord == null) {
94769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // Shouldn't get here.  There must be a bug somewhere.
94869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                Log.wtf(TAG, "Handled connection timeout for no reason.");
94969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return;
95069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
95169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
95269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            switch (mConnectionTimeoutReason) {
95369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case TIMEOUT_REASON_NOT_AVAILABLE:
95469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Slog.i(TAG, "Global route no longer available: "
95569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + mGloballySelectedRouteRecord);
95669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
95739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case TIMEOUT_REASON_CONNECTION_LOST:
95839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    Slog.i(TAG, "Global route connection lost: "
95939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                            + mGloballySelectedRouteRecord);
96039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
96169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case TIMEOUT_REASON_WAITING_FOR_CONNECTING:
96269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Slog.i(TAG, "Global route timed out while waiting for "
96369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + "connection attempt to begin after "
96469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + (SystemClock.uptimeMillis() - mConnectionTimeoutStartTime)
96569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + " ms: " + mGloballySelectedRouteRecord);
96669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
96769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case TIMEOUT_REASON_WAITING_FOR_CONNECTED:
96869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Slog.i(TAG, "Global route timed out while connecting after "
96969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + (SystemClock.uptimeMillis() - mConnectionTimeoutStartTime)
97069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + " ms: " + mGloballySelectedRouteRecord);
97169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
97269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
97369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mConnectionTimeoutReason = 0;
97469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
97569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            unselectGloballySelectedRoute();
97669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
97769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
97869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void scheduleUpdateClientState() {
97969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (!mClientStateUpdateScheduled) {
98069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mClientStateUpdateScheduled = true;
98169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                sendEmptyMessage(MSG_UPDATE_CLIENT_STATE);
98269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
98369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
98469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
98569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void updateClientState() {
98669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mClientStateUpdateScheduled = false;
98769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
988af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final String globallySelectedRouteId = mGloballySelectedRouteRecord != null ?
98969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mGloballySelectedRouteRecord.getUniqueId() : null;
990af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
991af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Build a new client state for trusted clients.
992af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            MediaRouterClientState trustedState = new MediaRouterClientState();
993af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            trustedState.globallySelectedRouteId = globallySelectedRouteId;
99469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int providerCount = mProviderRecords.size();
99569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < providerCount; i++) {
996af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                mProviderRecords.get(i).appendClientState(trustedState);
997af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
998af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
999af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Build a new client state for untrusted clients that can only see
1000af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // the currently selected route.
1001af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            MediaRouterClientState untrustedState = new MediaRouterClientState();
1002af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            untrustedState.globallySelectedRouteId = globallySelectedRouteId;
1003af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (globallySelectedRouteId != null) {
1004af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                untrustedState.routes.add(trustedState.getRoute(globallySelectedRouteId));
100569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
100669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
100769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            try {
100869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                synchronized (mService.mLock) {
100969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Update the UserRecord.
1010af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    mUserRecord.mTrustedState = trustedState;
1011af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    mUserRecord.mUntrustedState = untrustedState;
101269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
101369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Collect all clients.
101469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    final int count = mUserRecord.mClientRecords.size();
101569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    for (int i = 0; i < count; i++) {
101669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        mTempClients.add(mUserRecord.mClientRecords.get(i).mClient);
101769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
101869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
101969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
102069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // Notify all clients (outside of the lock).
102169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int count = mTempClients.size();
102269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < count; i++) {
102369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    try {
102469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        mTempClients.get(i).onStateChanged();
102569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    } catch (RemoteException ex) {
102669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        // ignore errors, client probably died
102769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
102869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
102969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } finally {
103069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // Clear the list in preparation for the next time.
103169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mTempClients.clear();
103269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
103369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
103469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
103569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private int findProviderRecord(RemoteDisplayProviderProxy provider) {
103669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mProviderRecords.size();
103769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
103869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                ProviderRecord record = mProviderRecords.get(i);
103969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (record.getProvider() == provider) {
104069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    return i;
104169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
104269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
104369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return -1;
104469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
104569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
104669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private RouteRecord findRouteRecord(String uniqueId) {
104769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mProviderRecords.size();
104869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
104969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                RouteRecord record = mProviderRecords.get(i).findRouteByUniqueId(uniqueId);
105069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (record != null) {
105169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    return record;
105269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
105369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
105469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return null;
105569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
105669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
105739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static int getConnectionPhase(int status) {
105839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            switch (status) {
105939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case MediaRouter.RouteInfo.STATUS_NONE:
106039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case MediaRouter.RouteInfo.STATUS_CONNECTED:
106139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    return PHASE_CONNECTED;
106239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case MediaRouter.RouteInfo.STATUS_CONNECTING:
106339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    return PHASE_CONNECTING;
106439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case MediaRouter.RouteInfo.STATUS_SCANNING:
106539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case MediaRouter.RouteInfo.STATUS_AVAILABLE:
106639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    return PHASE_NOT_CONNECTED;
106739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case MediaRouter.RouteInfo.STATUS_NOT_AVAILABLE:
106839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case MediaRouter.RouteInfo.STATUS_IN_USE:
106939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                default:
107039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    return PHASE_NOT_AVAILABLE;
107139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
107239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        }
107339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
107469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        static final class ProviderRecord {
107569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private final RemoteDisplayProviderProxy mProvider;
107669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private final String mUniquePrefix;
107769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private final ArrayList<RouteRecord> mRoutes = new ArrayList<RouteRecord>();
107869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private RemoteDisplayState mDescriptor;
107969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
108069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public ProviderRecord(RemoteDisplayProviderProxy provider) {
108169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mProvider = provider;
108269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mUniquePrefix = provider.getFlattenedComponentName() + ":";
108369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
108469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
108569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public RemoteDisplayProviderProxy getProvider() {
108669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mProvider;
108769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
108869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
108969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public String getUniquePrefix() {
109069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mUniquePrefix;
109169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
109269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
109369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public boolean updateDescriptor(RemoteDisplayState descriptor) {
109469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                boolean changed = false;
109569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (mDescriptor != descriptor) {
109669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mDescriptor = descriptor;
109769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
109869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Update all existing routes and reorder them to match
109969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // the order of their descriptors.
110069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    int targetIndex = 0;
110169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (descriptor != null) {
110269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (descriptor.isValid()) {
110369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            final List<RemoteDisplayInfo> routeDescriptors = descriptor.displays;
110469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            final int routeCount = routeDescriptors.size();
110569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            for (int i = 0; i < routeCount; i++) {
110669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                final RemoteDisplayInfo routeDescriptor =
110769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                        routeDescriptors.get(i);
110869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                final String descriptorId = routeDescriptor.id;
110969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                final int sourceIndex = findRouteByDescriptorId(descriptorId);
111069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                if (sourceIndex < 0) {
111169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    // Add the route to the provider.
111269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    String uniqueId = assignRouteUniqueId(descriptorId);
111369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    RouteRecord route =
111469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                            new RouteRecord(this, descriptorId, uniqueId);
111569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    mRoutes.add(targetIndex++, route);
111669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    route.updateDescriptor(routeDescriptor);
111769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    changed = true;
111869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                } else if (sourceIndex < targetIndex) {
111969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    // Ignore route with duplicate id.
112069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    Slog.w(TAG, "Ignoring route descriptor with duplicate id: "
112169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                            + routeDescriptor);
112269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                } else {
112369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    // Reorder existing route within the list.
112469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    RouteRecord route = mRoutes.get(sourceIndex);
112569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    Collections.swap(mRoutes, sourceIndex, targetIndex++);
112669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    changed |= route.updateDescriptor(routeDescriptor);
112769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                }
112869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            }
112969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        } else {
113069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            Slog.w(TAG, "Ignoring invalid descriptor from media route provider: "
113169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    + mProvider.getFlattenedComponentName());
113269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
113369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
113469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
113569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Dispose all remaining routes that do not have matching descriptors.
113669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    for (int i = mRoutes.size() - 1; i >= targetIndex; i--) {
113769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        RouteRecord route = mRoutes.remove(i);
113869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        route.updateDescriptor(null); // mark route invalid
113969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        changed = true;
114069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
114169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
114269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return changed;
114369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
114469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
114569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public void appendClientState(MediaRouterClientState state) {
114669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int routeCount = mRoutes.size();
114769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < routeCount; i++) {
114869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    state.routes.add(mRoutes.get(i).getInfo());
114969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
115069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
115169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
115269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public RouteRecord findRouteByUniqueId(String uniqueId) {
115369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int routeCount = mRoutes.size();
115469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < routeCount; i++) {
115569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    RouteRecord route = mRoutes.get(i);
115669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (route.getUniqueId().equals(uniqueId)) {
115769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return route;
115869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
115969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
116069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return null;
116169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
116269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
116369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private int findRouteByDescriptorId(String descriptorId) {
116469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int routeCount = mRoutes.size();
116569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < routeCount; i++) {
116669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    RouteRecord route = mRoutes.get(i);
116769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (route.getDescriptorId().equals(descriptorId)) {
116869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return i;
116969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
117069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
117169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return -1;
117269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
117369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
117469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public void dump(PrintWriter pw, String prefix) {
117569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(prefix + this);
117669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
117769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final String indent = prefix + "  ";
117869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mProvider.dump(pw, indent);
117969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
118069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int routeCount = mRoutes.size();
118169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (routeCount != 0) {
118269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    for (int i = 0; i < routeCount; i++) {
118369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        mRoutes.get(i).dump(pw, indent);
118469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
118569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
118669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    pw.println(indent + "<no routes>");
118769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
118869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
118969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
119069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
119169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public String toString() {
119269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return "Provider " + mProvider.getFlattenedComponentName();
119369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
119469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
119569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private String assignRouteUniqueId(String descriptorId) {
119669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mUniquePrefix + descriptorId;
119769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
119869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
119969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
120069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        static final class RouteRecord {
120169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private final ProviderRecord mProviderRecord;
120269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private final String mDescriptorId;
120369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private final MediaRouterClientState.RouteInfo mMutableInfo;
120469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private MediaRouterClientState.RouteInfo mImmutableInfo;
120569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private RemoteDisplayInfo mDescriptor;
120669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
120769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public RouteRecord(ProviderRecord providerRecord,
120869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    String descriptorId, String uniqueId) {
120969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mProviderRecord = providerRecord;
121069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDescriptorId = descriptorId;
121169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mMutableInfo = new MediaRouterClientState.RouteInfo(uniqueId);
121269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
121369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
121469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public RemoteDisplayProviderProxy getProvider() {
121569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mProviderRecord.getProvider();
121669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
121769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
121869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public ProviderRecord getProviderRecord() {
121969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mProviderRecord;
122069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
122169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
122269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public String getDescriptorId() {
122369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mDescriptorId;
122469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
122569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
122669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public String getUniqueId() {
122769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mMutableInfo.id;
122869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
122969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
123069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public MediaRouterClientState.RouteInfo getInfo() {
123169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (mImmutableInfo == null) {
123269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mImmutableInfo = new MediaRouterClientState.RouteInfo(mMutableInfo);
123369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
123469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mImmutableInfo;
123569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
123669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
123769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public boolean isValid() {
123869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mDescriptor != null;
123969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
124069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
124169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public boolean isEnabled() {
124269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mMutableInfo.enabled;
124369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
124469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
124569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public int getStatus() {
124669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mMutableInfo.statusCode;
124769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
124869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
124969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public boolean updateDescriptor(RemoteDisplayInfo descriptor) {
125069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                boolean changed = false;
125169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (mDescriptor != descriptor) {
125269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mDescriptor = descriptor;
125369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (descriptor != null) {
125469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final String name = computeName(descriptor);
1255e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root                        if (!Objects.equals(mMutableInfo.name, name)) {
125669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.name = name;
125769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
125869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
125969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final String description = computeDescription(descriptor);
1260e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root                        if (!Objects.equals(mMutableInfo.description, description)) {
126169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.description = description;
126269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
126369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
126469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int supportedTypes = computeSupportedTypes(descriptor);
126569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.supportedTypes != supportedTypes) {
126669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.supportedTypes = supportedTypes;
126769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
126869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
126969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final boolean enabled = computeEnabled(descriptor);
127069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.enabled != enabled) {
127169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.enabled = enabled;
127269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
127369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
127469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int statusCode = computeStatusCode(descriptor);
127569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.statusCode != statusCode) {
127669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.statusCode = statusCode;
127769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
127869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
127969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int playbackType = computePlaybackType(descriptor);
128069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.playbackType != playbackType) {
128169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.playbackType = playbackType;
128269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
128369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
128469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int playbackStream = computePlaybackStream(descriptor);
128569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.playbackStream != playbackStream) {
128669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.playbackStream = playbackStream;
128769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
128869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
128969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int volume = computeVolume(descriptor);
129069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.volume != volume) {
129169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.volume = volume;
129269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
129369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
129469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int volumeMax = computeVolumeMax(descriptor);
129569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.volumeMax != volumeMax) {
129669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.volumeMax = volumeMax;
129769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
129869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
129969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int volumeHandling = computeVolumeHandling(descriptor);
130069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.volumeHandling != volumeHandling) {
130169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.volumeHandling = volumeHandling;
130269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
130369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
130469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int presentationDisplayId = computePresentationDisplayId(descriptor);
130569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.presentationDisplayId != presentationDisplayId) {
130669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.presentationDisplayId = presentationDisplayId;
130769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
130869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
130969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
131069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
131169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (changed) {
131269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mImmutableInfo = null;
131369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
131469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return changed;
131569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
131669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
131769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public void dump(PrintWriter pw, String prefix) {
131869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(prefix + this);
131969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
132069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final String indent = prefix + "  ";
132169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(indent + "mMutableInfo=" + mMutableInfo);
132269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(indent + "mDescriptorId=" + mDescriptorId);
132369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(indent + "mDescriptor=" + mDescriptor);
132469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
132569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
132669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
132769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public String toString() {
132869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return "Route " + mMutableInfo.name + " (" + mMutableInfo.id + ")";
132969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
133069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
133169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static String computeName(RemoteDisplayInfo descriptor) {
133269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // Note that isValid() already ensures the name is non-empty.
133369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return descriptor.name;
133469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
133569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
133669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static String computeDescription(RemoteDisplayInfo descriptor) {
133769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final String description = descriptor.description;
133869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return TextUtils.isEmpty(description) ? null : description;
133969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
134069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
134169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computeSupportedTypes(RemoteDisplayInfo descriptor) {
134269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return MediaRouter.ROUTE_TYPE_LIVE_AUDIO
134369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        | MediaRouter.ROUTE_TYPE_LIVE_VIDEO
134469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        | MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
134569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
134669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
134769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static boolean computeEnabled(RemoteDisplayInfo descriptor) {
134869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                switch (descriptor.status) {
134969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_CONNECTED:
135069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_CONNECTING:
135169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_AVAILABLE:
135269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return true;
135369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    default:
135469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return false;
135569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
135669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
135769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
135869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computeStatusCode(RemoteDisplayInfo descriptor) {
135969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                switch (descriptor.status) {
136069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_NOT_AVAILABLE:
136169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.STATUS_NOT_AVAILABLE;
136269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_AVAILABLE:
136369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.STATUS_AVAILABLE;
136469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_IN_USE:
136569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.STATUS_IN_USE;
136669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_CONNECTING:
136769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.STATUS_CONNECTING;
136869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_CONNECTED:
136969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.STATUS_CONNECTED;
137069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    default:
137169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.STATUS_NONE;
137269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
137369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
137469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
137569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computePlaybackType(RemoteDisplayInfo descriptor) {
137669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
137769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
137869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
137969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computePlaybackStream(RemoteDisplayInfo descriptor) {
138069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return AudioSystem.STREAM_MUSIC;
138169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
138269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
138369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computeVolume(RemoteDisplayInfo descriptor) {
138469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int volume = descriptor.volume;
138569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int volumeMax = descriptor.volumeMax;
138669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (volume < 0) {
138769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    return 0;
138869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else if (volume > volumeMax) {
138969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    return volumeMax;
139069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
139169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return volume;
139269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
139369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
139469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computeVolumeMax(RemoteDisplayInfo descriptor) {
139569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int volumeMax = descriptor.volumeMax;
139669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return volumeMax > 0 ? volumeMax : 0;
139769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
139869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
139969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computeVolumeHandling(RemoteDisplayInfo descriptor) {
140069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int volumeHandling = descriptor.volumeHandling;
140169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                switch (volumeHandling) {
140269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.PLAYBACK_VOLUME_VARIABLE:
140369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
140469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.PLAYBACK_VOLUME_FIXED:
140569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    default:
140669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED;
140769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
140869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
140969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
141069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computePresentationDisplayId(RemoteDisplayInfo descriptor) {
141169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // The MediaRouter class validates that the id corresponds to an extant
141269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // presentation display.  So all we do here is canonicalize the null case.
141369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int displayId = descriptor.presentationDisplayId;
141469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return displayId < 0 ? -1 : displayId;
141569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
141669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
141769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
141869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown}
1419