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