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.server.Watchdog;
2069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
2169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.Manifest;
2269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.app.ActivityManager;
2369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.content.BroadcastReceiver;
2469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.content.Context;
2569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.content.Intent;
2669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.content.IntentFilter;
2769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.content.pm.PackageManager;
2869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.media.AudioSystem;
2969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.media.IMediaRouterClient;
3069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.media.IMediaRouterService;
3169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.media.MediaRouter;
3269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.media.MediaRouterClientState;
3369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.media.RemoteDisplayState;
3469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.media.RemoteDisplayState.RemoteDisplayInfo;
3569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.Binder;
3669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.Handler;
3769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.IBinder;
3869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.Looper;
3969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.Message;
4069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.RemoteException;
4169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.SystemClock;
4269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.text.TextUtils;
4369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.util.ArrayMap;
4469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.util.Log;
4569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.util.Slog;
4669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.util.SparseArray;
4769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.util.TimeUtils;
4869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
4969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport java.io.FileDescriptor;
5069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport java.io.PrintWriter;
5169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport java.util.ArrayList;
5269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport java.util.Collections;
5369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport java.util.List;
54e6585b32ea586743258a5457e2184ffc087f2d2fKenny Rootimport java.util.Objects;
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);
131af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        final boolean trusted = mContext.checkCallingOrSelfPermission(
132af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) ==
133af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                PackageManager.PERMISSION_GRANTED;
13469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final long token = Binder.clearCallingIdentity();
13569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        try {
13669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mLock) {
137af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                registerClientLocked(client, pid, packageName, resolvedUserId, trusted);
13869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
13969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } finally {
14069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Binder.restoreCallingIdentity(token);
14169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
14269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
14369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
14469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
14569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
14669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void unregisterClient(IMediaRouterClient client) {
14769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (client == null) {
14869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("client must not be null");
14969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
15069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
15169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final long token = Binder.clearCallingIdentity();
15269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        try {
15369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mLock) {
15469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                unregisterClientLocked(client, false);
15569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
15669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } finally {
15769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Binder.restoreCallingIdentity(token);
15869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
15969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
16069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
16169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
16269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
16369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public MediaRouterClientState getState(IMediaRouterClient client) {
16469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (client == null) {
16569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("client must not be null");
16669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
16769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
16869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final long token = Binder.clearCallingIdentity();
16969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        try {
17069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mLock) {
17169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return getStateLocked(client);
17269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
17369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } finally {
17469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Binder.restoreCallingIdentity(token);
17569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
17669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
17769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
17869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
17969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
18069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void setDiscoveryRequest(IMediaRouterClient client,
18169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int routeTypes, boolean activeScan) {
18269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (client == null) {
18369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("client must not be null");
18469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
18569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
18669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final long token = Binder.clearCallingIdentity();
18769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        try {
18869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mLock) {
18969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                setDiscoveryRequestLocked(client, routeTypes, activeScan);
19069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
19169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } finally {
19269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Binder.restoreCallingIdentity(token);
19369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
19469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
19569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
19669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
19769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // A null routeId means that the client wants to unselect its current route.
19869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // The explicit flag indicates whether the change was explicitly requested by the
19969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // user or the application which may cause changes to propagate out to the rest
20069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // of the system.  Should be false when the change is in response to a new globally
20169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // selected route or a default selection.
20269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
20369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit) {
20469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (client == null) {
20569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("client must not be null");
20669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
20769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
20869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final long token = Binder.clearCallingIdentity();
20969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        try {
21069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mLock) {
21169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                setSelectedRouteLocked(client, routeId, explicit);
21269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
21369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } finally {
21469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Binder.restoreCallingIdentity(token);
21569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
21669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
21769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
21869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
21969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
22069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void requestSetVolume(IMediaRouterClient client, String routeId, int volume) {
22169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (client == null) {
22269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("client must not be null");
22369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
22469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (routeId == null) {
22569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("routeId must not be null");
22669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
22769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
22869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final long token = Binder.clearCallingIdentity();
22969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        try {
23069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mLock) {
23169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                requestSetVolumeLocked(client, routeId, volume);
23269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
23369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } finally {
23469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Binder.restoreCallingIdentity(token);
23569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
23669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
23769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
23869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
23969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
24069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction) {
24169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (client == null) {
24269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("client must not be null");
24369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
24469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (routeId == null) {
24569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            throw new IllegalArgumentException("routeId must not be null");
24669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
24769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
24869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final long token = Binder.clearCallingIdentity();
24969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        try {
25069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mLock) {
25169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                requestUpdateVolumeLocked(client, routeId, direction);
25269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
25369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        } finally {
25469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Binder.restoreCallingIdentity(token);
25569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
25669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
25769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
25869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    // Binder call
25969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    @Override
26069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
26169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
26269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                != PackageManager.PERMISSION_GRANTED) {
26369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println("Permission Denial: can't dump MediaRouterService from from pid="
26469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    + Binder.getCallingPid()
26569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    + ", uid=" + Binder.getCallingUid());
26669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return;
26769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
26869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
26969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        pw.println("MEDIA ROUTER SERVICE (dumpsys media_router)");
27069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        pw.println();
27169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        pw.println("Global state");
27269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        pw.println("  mCurrentUserId=" + mCurrentUserId);
27369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
27469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        synchronized (mLock) {
27569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mUserRecords.size();
27669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
27769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                UserRecord userRecord = mUserRecords.valueAt(i);
27869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println();
27969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                userRecord.dump(pw, "");
28069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
28169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
28269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
28369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
28469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    void switchUser() {
28569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        synchronized (mLock) {
28669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int userId = ActivityManager.getCurrentUser();
28769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mCurrentUserId != userId) {
28869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int oldUserId = mCurrentUserId;
28969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mCurrentUserId = userId; // do this first
29069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
29169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                UserRecord oldUser = mUserRecords.get(oldUserId);
29269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (oldUser != null) {
29369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    oldUser.mHandler.sendEmptyMessage(UserHandler.MSG_STOP);
29469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    disposeUserIfNeededLocked(oldUser); // since no longer current user
29569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
29669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
29769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                UserRecord newUser = mUserRecords.get(userId);
29869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (newUser != null) {
29969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    newUser.mHandler.sendEmptyMessage(UserHandler.MSG_START);
30069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
30169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
30269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
30369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
30469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
30569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    void clientDied(ClientRecord clientRecord) {
30669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        synchronized (mLock) {
30769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            unregisterClientLocked(clientRecord.mClient, true);
30869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
30969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
31069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
31169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void registerClientLocked(IMediaRouterClient client,
312af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            int pid, String packageName, int userId, boolean trusted) {
31369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final IBinder binder = client.asBinder();
31469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        ClientRecord clientRecord = mAllClientRecords.get(binder);
31569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord == null) {
31669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean newUser = false;
31769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            UserRecord userRecord = mUserRecords.get(userId);
31869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (userRecord == null) {
31969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                userRecord = new UserRecord(userId);
32069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                newUser = true;
32169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
322af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            clientRecord = new ClientRecord(userRecord, client, pid, packageName, trusted);
32369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            try {
32469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                binder.linkToDeath(clientRecord, 0);
32569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } catch (RemoteException ex) {
32669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                throw new RuntimeException("Media router client died prematurely.", ex);
32769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
32869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
32969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (newUser) {
33069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mUserRecords.put(userId, userRecord);
33169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                initializeUserLocked(userRecord);
33269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
33369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
33469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            userRecord.mClientRecords.add(clientRecord);
33569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mAllClientRecords.put(binder, clientRecord);
33669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            initializeClientLocked(clientRecord);
33769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
33869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
33969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
34069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void unregisterClientLocked(IMediaRouterClient client, boolean died) {
34169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        ClientRecord clientRecord = mAllClientRecords.remove(client.asBinder());
34269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord != null) {
34369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            UserRecord userRecord = clientRecord.mUserRecord;
34469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            userRecord.mClientRecords.remove(clientRecord);
34569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            disposeClientLocked(clientRecord, died);
34669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            disposeUserIfNeededLocked(userRecord); // since client removed from user
34769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
34869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
34969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
35069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private MediaRouterClientState getStateLocked(IMediaRouterClient client) {
35169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
35269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord != null) {
353af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            return clientRecord.getState();
35469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
35569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        return null;
35669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
35769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
35869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void setDiscoveryRequestLocked(IMediaRouterClient client,
35969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int routeTypes, boolean activeScan) {
36069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final IBinder binder = client.asBinder();
36169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        ClientRecord clientRecord = mAllClientRecords.get(binder);
36269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord != null) {
363af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Only let the system discover remote display routes for now.
364af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (!clientRecord.mTrusted) {
365af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                routeTypes &= ~MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
366af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
367af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
36869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (clientRecord.mRouteTypes != routeTypes
36969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    || clientRecord.mActiveScan != activeScan) {
37069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (DEBUG) {
37169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Slog.d(TAG, clientRecord + ": Set discovery request, routeTypes=0x"
37269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + Integer.toHexString(routeTypes) + ", activeScan=" + activeScan);
37369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
37469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                clientRecord.mRouteTypes = routeTypes;
37569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                clientRecord.mActiveScan = activeScan;
37669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                clientRecord.mUserRecord.mHandler.sendEmptyMessage(
37769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        UserHandler.MSG_UPDATE_DISCOVERY_REQUEST);
37869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
37969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
38069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
38169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
38269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void setSelectedRouteLocked(IMediaRouterClient client,
38369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            String routeId, boolean explicit) {
38469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
38569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord != null) {
38669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final String oldRouteId = clientRecord.mSelectedRouteId;
387e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root            if (!Objects.equals(routeId, oldRouteId)) {
38869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (DEBUG) {
38969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Slog.d(TAG, clientRecord + ": Set selected route, routeId=" + routeId
39069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + ", oldRouteId=" + oldRouteId
39169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + ", explicit=" + explicit);
39269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
39369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
39469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                clientRecord.mSelectedRouteId = routeId;
39569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (explicit) {
396af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    // Any app can disconnect from the globally selected route.
39769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (oldRouteId != null) {
39869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        clientRecord.mUserRecord.mHandler.obtainMessage(
39969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                UserHandler.MSG_UNSELECT_ROUTE, oldRouteId).sendToTarget();
40069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
401af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    // Only let the system connect to new global routes for now.
402af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    // A similar check exists in the display manager for wifi display.
403af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    if (routeId != null && clientRecord.mTrusted) {
40469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        clientRecord.mUserRecord.mHandler.obtainMessage(
40569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                UserHandler.MSG_SELECT_ROUTE, routeId).sendToTarget();
40669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
40769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
40869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
40969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
41069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
41169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
41269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void requestSetVolumeLocked(IMediaRouterClient client,
41369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            String routeId, int volume) {
41469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final IBinder binder = client.asBinder();
41569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        ClientRecord clientRecord = mAllClientRecords.get(binder);
41669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord != null) {
41769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            clientRecord.mUserRecord.mHandler.obtainMessage(
41869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    UserHandler.MSG_REQUEST_SET_VOLUME, volume, 0, routeId).sendToTarget();
41969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
42069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
42169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
42269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void requestUpdateVolumeLocked(IMediaRouterClient client,
42369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            String routeId, int direction) {
42469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        final IBinder binder = client.asBinder();
42569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        ClientRecord clientRecord = mAllClientRecords.get(binder);
42669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord != null) {
42769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            clientRecord.mUserRecord.mHandler.obtainMessage(
42869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    UserHandler.MSG_REQUEST_UPDATE_VOLUME, direction, 0, routeId).sendToTarget();
42969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
43069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
43169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
43269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void initializeUserLocked(UserRecord userRecord) {
43369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (DEBUG) {
43469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Slog.d(TAG, userRecord + ": Initialized");
43569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
43669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (userRecord.mUserId == mCurrentUserId) {
43769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_START);
43869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
43969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
44069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
44169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void disposeUserIfNeededLocked(UserRecord userRecord) {
44269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // If there are no records left and the user is no longer current then go ahead
44369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // and purge the user record and all of its associated state.  If the user is current
44469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // then leave it alone since we might be connected to a route or want to query
44569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        // the same route information again soon.
44669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (userRecord.mUserId != mCurrentUserId
44769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                && userRecord.mClientRecords.isEmpty()) {
44869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (DEBUG) {
44969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                Slog.d(TAG, userRecord + ": Disposed");
45069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
45169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mUserRecords.remove(userRecord.mUserId);
45269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Note: User already stopped (by switchUser) so no need to send stop message here.
45369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
45469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
45569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
45669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void initializeClientLocked(ClientRecord clientRecord) {
45769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (DEBUG) {
45869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            Slog.d(TAG, clientRecord + ": Registered");
45969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
46069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
46169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
46269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private void disposeClientLocked(ClientRecord clientRecord, boolean died) {
46369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (DEBUG) {
46469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (died) {
46569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                Slog.d(TAG, clientRecord + ": Died!");
46669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } else {
46769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                Slog.d(TAG, clientRecord + ": Unregistered");
46869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
46969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
47069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (clientRecord.mRouteTypes != 0 || clientRecord.mActiveScan) {
47169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            clientRecord.mUserRecord.mHandler.sendEmptyMessage(
47269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    UserHandler.MSG_UPDATE_DISCOVERY_REQUEST);
47369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
47469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        clientRecord.dispose();
47569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
47669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
47769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    private boolean validatePackageName(int uid, String packageName) {
47869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        if (packageName != null) {
47969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
48069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (packageNames != null) {
48169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (String n : packageNames) {
48269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (n.equals(packageName)) {
48369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return true;
48469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
48569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
48669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
48769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
48869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        return false;
48969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
49069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
49169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
49269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Information about a particular client of the media router.
49369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * The contents of this object is guarded by mLock.
49469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
49569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    final class ClientRecord implements DeathRecipient {
49669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public final UserRecord mUserRecord;
49769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public final IMediaRouterClient mClient;
49869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public final int mPid;
49969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public final String mPackageName;
500af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        public final boolean mTrusted;
50169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
50269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public int mRouteTypes;
50369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public boolean mActiveScan;
50469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public String mSelectedRouteId;
50569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
50669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public ClientRecord(UserRecord userRecord, IMediaRouterClient client,
507af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                int pid, String packageName, boolean trusted) {
50869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mUserRecord = userRecord;
50969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mClient = client;
51069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mPid = pid;
51169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mPackageName = packageName;
512af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            mTrusted = trusted;
51369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
51469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
51569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void dispose() {
51669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mClient.asBinder().unlinkToDeath(this, 0);
51769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
51869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
51969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
52069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void binderDied() {
52169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            clientDied(this);
52269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
52369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
524af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        MediaRouterClientState getState() {
525af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            return mTrusted ? mUserRecord.mTrustedState : mUserRecord.mUntrustedState;
526af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        }
527af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
52869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void dump(PrintWriter pw, String prefix) {
52969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(prefix + this);
53069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
53169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final String indent = prefix + "  ";
532af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            pw.println(indent + "mTrusted=" + mTrusted);
53369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mRouteTypes=0x" + Integer.toHexString(mRouteTypes));
53469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mActiveScan=" + mActiveScan);
53569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mSelectedRouteId=" + mSelectedRouteId);
53669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
53769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
53869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
53969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public String toString() {
54069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return "Client " + mPackageName + " (pid " + mPid + ")";
54169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
54269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
54369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
54469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
54569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Information about a particular user.
54669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * The contents of this object is guarded by mLock.
54769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
54869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    final class UserRecord {
54969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public final int mUserId;
55069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public final ArrayList<ClientRecord> mClientRecords = new ArrayList<ClientRecord>();
55169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public final UserHandler mHandler;
552af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        public MediaRouterClientState mTrustedState;
553af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown        public MediaRouterClientState mUntrustedState;
55469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
55569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public UserRecord(int userId) {
55669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mUserId = userId;
55769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mHandler = new UserHandler(MediaRouterService.this, this);
55869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
55969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
56069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void dump(final PrintWriter pw, String prefix) {
56169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(prefix + this);
56269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
56369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final String indent = prefix + "  ";
56469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int clientCount = mClientRecords.size();
56569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (clientCount != 0) {
56669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < clientCount; i++) {
56769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mClientRecords.get(i).dump(pw, indent);
56869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
56969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } else {
57069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(indent + "<no clients>");
57169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
57269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
573af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            pw.println(indent + "State");
574af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            pw.println(indent + "mTrustedState=" + mTrustedState);
575af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            pw.println(indent + "mUntrustedState=" + mUntrustedState);
576af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
57769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (!mHandler.runWithScissors(new Runnable() {
57869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                @Override
57969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                public void run() {
58069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mHandler.dump(pw, indent);
58169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
58269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }, 1000)) {
58369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(indent + "<could not dump handler state>");
58469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
58569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         }
58669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
58769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
58869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public String toString() {
58969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return "User " + mUserId;
59069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
59169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
59269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
59369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    /**
59469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Media router handler
59569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * <p>
59669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Since remote display providers are designed to be single-threaded by nature,
59769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * this class encapsulates all of the associated functionality and exports state
59869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * to the service as it evolves.
59969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * </p><p>
60069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * One important task of this class is to keep track of the current globally selected
60169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * route id for certain routes that have global effects, such as remote displays.
60269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * Global route selections override local selections made within apps.  The change
60369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * is propagated to all apps so that they are all in sync.  Synchronization works
60469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * both ways.  Whenever the globally selected route is explicitly unselected by any
60569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * app, then it becomes unselected globally and all apps are informed.
60669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * </p><p>
60769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * This class is currently hardcoded to work with remote display providers but
60869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * it is intended to be eventually extended to support more general route providers
60969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * similar to the support library media router.
61069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     * </p>
61169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown     */
61269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    static final class UserHandler extends Handler
61369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            implements RemoteDisplayProviderWatcher.Callback,
61469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            RemoteDisplayProviderProxy.Callback {
61569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public static final int MSG_START = 1;
61669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public static final int MSG_STOP = 2;
61769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public static final int MSG_UPDATE_DISCOVERY_REQUEST = 3;
61869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public static final int MSG_SELECT_ROUTE = 4;
61969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public static final int MSG_UNSELECT_ROUTE = 5;
62069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public static final int MSG_REQUEST_SET_VOLUME = 6;
62169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public static final int MSG_REQUEST_UPDATE_VOLUME = 7;
62269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private static final int MSG_UPDATE_CLIENT_STATE = 8;
62369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private static final int MSG_CONNECTION_TIMED_OUT = 9;
62469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
62569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private static final int TIMEOUT_REASON_NOT_AVAILABLE = 1;
62639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static final int TIMEOUT_REASON_CONNECTION_LOST = 2;
62739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static final int TIMEOUT_REASON_WAITING_FOR_CONNECTING = 3;
62839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static final int TIMEOUT_REASON_WAITING_FOR_CONNECTED = 4;
62939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
63039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        // The relative order of these constants is important and expresses progress
63139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        // through the process of connecting to a route.
63239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static final int PHASE_NOT_AVAILABLE = -1;
63339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static final int PHASE_NOT_CONNECTED = 0;
63439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static final int PHASE_CONNECTING = 1;
63539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static final int PHASE_CONNECTED = 2;
63669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
63769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private final MediaRouterService mService;
63869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private final UserRecord mUserRecord;
63969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private final RemoteDisplayProviderWatcher mWatcher;
64069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private final ArrayList<ProviderRecord> mProviderRecords =
64169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                new ArrayList<ProviderRecord>();
64269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private final ArrayList<IMediaRouterClient> mTempClients =
64369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                new ArrayList<IMediaRouterClient>();
64469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
64569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private boolean mRunning;
64669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private int mDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_NONE;
64769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private RouteRecord mGloballySelectedRouteRecord;
64839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private int mConnectionPhase = PHASE_NOT_AVAILABLE;
64969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private int mConnectionTimeoutReason;
65069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private long mConnectionTimeoutStartTime;
65169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private boolean mClientStateUpdateScheduled;
65269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
65369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public UserHandler(MediaRouterService service, UserRecord userRecord) {
65469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            super(Looper.getMainLooper(), null, true);
65569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mService = service;
65669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mUserRecord = userRecord;
65769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mWatcher = new RemoteDisplayProviderWatcher(service.mContext, this,
65869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    this, mUserRecord.mUserId);
65969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
66069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
66169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
66269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void handleMessage(Message msg) {
66369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            switch (msg.what) {
66469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_START: {
66569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    start();
66669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
66769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
66869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_STOP: {
66969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    stop();
67069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
67169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
67269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_UPDATE_DISCOVERY_REQUEST: {
67369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateDiscoveryRequest();
67469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
67569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
67669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_SELECT_ROUTE: {
67769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    selectRoute((String)msg.obj);
67869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
67969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
68069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_UNSELECT_ROUTE: {
68169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    unselectRoute((String)msg.obj);
68269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
68369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
68469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_REQUEST_SET_VOLUME: {
68569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    requestSetVolume((String)msg.obj, msg.arg1);
68669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
68769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
68869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_REQUEST_UPDATE_VOLUME: {
68969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    requestUpdateVolume((String)msg.obj, msg.arg1);
69069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
69169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
69269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_UPDATE_CLIENT_STATE: {
69369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateClientState();
69469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
69569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
69669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case MSG_CONNECTION_TIMED_OUT: {
69769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    connectionTimedOut();
69869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
69969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
70069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
70169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
70269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
70369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void dump(PrintWriter pw, String prefix) {
70469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(prefix + "Handler");
70569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
70669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final String indent = prefix + "  ";
70769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mRunning=" + mRunning);
70869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mDiscoveryMode=" + mDiscoveryMode);
70969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mGloballySelectedRouteRecord=" + mGloballySelectedRouteRecord);
71039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            pw.println(indent + "mConnectionPhase=" + mConnectionPhase);
71169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mConnectionTimeoutReason=" + mConnectionTimeoutReason);
71269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            pw.println(indent + "mConnectionTimeoutStartTime=" + (mConnectionTimeoutReason != 0 ?
71369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    TimeUtils.formatUptime(mConnectionTimeoutStartTime) : "<n/a>"));
71469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
71569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mWatcher.dump(pw, prefix);
71669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
71769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int providerCount = mProviderRecords.size();
71869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (providerCount != 0) {
71969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < providerCount; i++) {
72069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mProviderRecords.get(i).dump(pw, prefix);
72169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
72269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } else {
72369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(indent + "<no providers>");
72469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
72569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
72669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
72769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void start() {
72869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (!mRunning) {
72969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mRunning = true;
73069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mWatcher.start(); // also starts all providers
73169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
73269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
73369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
73469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void stop() {
73569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mRunning) {
73669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mRunning = false;
73769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                unselectGloballySelectedRoute();
73869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mWatcher.stop(); // also stops all providers
73969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
74069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
74169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
74269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void updateDiscoveryRequest() {
74369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int routeTypes = 0;
74469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            boolean activeScan = false;
74569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            synchronized (mService.mLock) {
74669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int count = mUserRecord.mClientRecords.size();
74769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < count; i++) {
74869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    ClientRecord clientRecord = mUserRecord.mClientRecords.get(i);
74969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeTypes |= clientRecord.mRouteTypes;
75069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    activeScan |= clientRecord.mActiveScan;
75169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
75269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
75369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
75469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int newDiscoveryMode;
755af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if ((routeTypes & MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
75669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (activeScan) {
75769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    newDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_ACTIVE;
75869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
75969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    newDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_PASSIVE;
76069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
76169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } else {
76269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                newDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_NONE;
76369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
76469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
76569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mDiscoveryMode != newDiscoveryMode) {
76669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDiscoveryMode = newDiscoveryMode;
76769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int count = mProviderRecords.size();
76869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < count; i++) {
76969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mProviderRecords.get(i).getProvider().setDiscoveryMode(mDiscoveryMode);
77069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
77169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
77269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
77369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
77469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void selectRoute(String routeId) {
77569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeId != null
77669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    && (mGloballySelectedRouteRecord == null
77769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            || !routeId.equals(mGloballySelectedRouteRecord.getUniqueId()))) {
77869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                RouteRecord routeRecord = findRouteRecord(routeId);
77969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (routeRecord != null) {
78069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    unselectGloballySelectedRoute();
78169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
78269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Slog.i(TAG, "Selected global route:" + routeRecord);
78369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mGloballySelectedRouteRecord = routeRecord;
78469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    checkGloballySelectedRouteState();
78569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    routeRecord.getProvider().setSelectedDisplay(routeRecord.getDescriptorId());
78669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
78769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    scheduleUpdateClientState();
78869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
78969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
79069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
79169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
79269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void unselectRoute(String routeId) {
79369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (routeId != null
79469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    && mGloballySelectedRouteRecord != null
79569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    && routeId.equals(mGloballySelectedRouteRecord.getUniqueId())) {
79669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                unselectGloballySelectedRoute();
79769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
79869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
79969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
80069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void unselectGloballySelectedRoute() {
80169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mGloballySelectedRouteRecord != null) {
80269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                Slog.i(TAG, "Unselected global route:" + mGloballySelectedRouteRecord);
80369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mGloballySelectedRouteRecord.getProvider().setSelectedDisplay(null);
80469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mGloballySelectedRouteRecord = null;
80569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                checkGloballySelectedRouteState();
80669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
80769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                scheduleUpdateClientState();
80869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
80969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
81069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
81169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void requestSetVolume(String routeId, int volume) {
81269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mGloballySelectedRouteRecord != null
81369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    && routeId.equals(mGloballySelectedRouteRecord.getUniqueId())) {
81469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mGloballySelectedRouteRecord.getProvider().setDisplayVolume(volume);
81569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
81669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
81769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
81869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void requestUpdateVolume(String routeId, int direction) {
81969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mGloballySelectedRouteRecord != null
82069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    && routeId.equals(mGloballySelectedRouteRecord.getUniqueId())) {
82169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mGloballySelectedRouteRecord.getProvider().adjustDisplayVolume(direction);
82269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
82369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
82469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
82569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
82669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void addProvider(RemoteDisplayProviderProxy provider) {
82769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            provider.setCallback(this);
82869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            provider.setDiscoveryMode(mDiscoveryMode);
82969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            provider.setSelectedDisplay(null); // just to be safe
83069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
83169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            ProviderRecord providerRecord = new ProviderRecord(provider);
83269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mProviderRecords.add(providerRecord);
83369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            providerRecord.updateDescriptor(provider.getDisplayState());
83469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
83569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            scheduleUpdateClientState();
83669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
83769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
83869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
83969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void removeProvider(RemoteDisplayProviderProxy provider) {
84069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int index = findProviderRecord(provider);
84169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (index >= 0) {
84269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                ProviderRecord providerRecord = mProviderRecords.remove(index);
84369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                providerRecord.updateDescriptor(null); // mark routes invalid
84469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                provider.setCallback(null);
84569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                provider.setDiscoveryMode(RemoteDisplayState.DISCOVERY_MODE_NONE);
84669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
84769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                checkGloballySelectedRouteState();
84869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                scheduleUpdateClientState();
84969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
85069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
85169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
85269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        @Override
85369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        public void onDisplayStateChanged(RemoteDisplayProviderProxy provider,
85469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                RemoteDisplayState state) {
85569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            updateProvider(provider, state);
85669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
85769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
85869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void updateProvider(RemoteDisplayProviderProxy provider,
85969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                RemoteDisplayState state) {
86069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            int index = findProviderRecord(provider);
86169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (index >= 0) {
86269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                ProviderRecord providerRecord = mProviderRecords.get(index);
86369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (providerRecord.updateDescriptor(state)) {
86469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    checkGloballySelectedRouteState();
86569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    scheduleUpdateClientState();
86669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
86769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
86869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
86969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
87069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        /**
87169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * This function is called whenever the state of the globally selected route
87269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * may have changed.  It checks the state and updates timeouts or unselects
87369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         * the route as appropriate.
87469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown         */
87569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void checkGloballySelectedRouteState() {
87669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Unschedule timeouts when the route is unselected.
87769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mGloballySelectedRouteRecord == null) {
87839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                mConnectionPhase = PHASE_NOT_AVAILABLE;
87969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                updateConnectionTimeout(0);
88069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return;
88169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
88269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
88369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Ensure that the route is still present and enabled.
88469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (!mGloballySelectedRouteRecord.isValid()
88569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    || !mGloballySelectedRouteRecord.isEnabled()) {
88669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                updateConnectionTimeout(TIMEOUT_REASON_NOT_AVAILABLE);
88769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return;
88869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
88969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
89039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            // Make sure we haven't lost our connection.
89139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            final int oldPhase = mConnectionPhase;
89239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            mConnectionPhase = getConnectionPhase(mGloballySelectedRouteRecord.getStatus());
89339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            if (oldPhase >= PHASE_CONNECTING && mConnectionPhase < PHASE_CONNECTING) {
89439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                updateConnectionTimeout(TIMEOUT_REASON_CONNECTION_LOST);
89539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                return;
89639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
89739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
89869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            // Check the route status.
89939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            switch (mConnectionPhase) {
90039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case PHASE_CONNECTED:
90139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    if (oldPhase != PHASE_CONNECTED) {
90269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Slog.i(TAG, "Connected to global route: "
90369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                + mGloballySelectedRouteRecord);
90469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
90569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateConnectionTimeout(0);
90669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
90739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case PHASE_CONNECTING:
90839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    if (oldPhase != PHASE_CONNECTING) {
90969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        Slog.i(TAG, "Connecting to global route: "
91069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                + mGloballySelectedRouteRecord);
91169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
91269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateConnectionTimeout(TIMEOUT_REASON_WAITING_FOR_CONNECTED);
91369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
91439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case PHASE_NOT_CONNECTED:
91569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateConnectionTimeout(TIMEOUT_REASON_WAITING_FOR_CONNECTING);
91669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
91739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case PHASE_NOT_AVAILABLE:
91869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                default:
91969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    updateConnectionTimeout(TIMEOUT_REASON_NOT_AVAILABLE);
92069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
92169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
92269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
92369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
92469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void updateConnectionTimeout(int reason) {
92569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (reason != mConnectionTimeoutReason) {
92669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (mConnectionTimeoutReason != 0) {
92769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    removeMessages(MSG_CONNECTION_TIMED_OUT);
92869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
92969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mConnectionTimeoutReason = reason;
93069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mConnectionTimeoutStartTime = SystemClock.uptimeMillis();
93169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                switch (reason) {
93269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case TIMEOUT_REASON_NOT_AVAILABLE:
93339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    case TIMEOUT_REASON_CONNECTION_LOST:
93439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                        // Route became unavailable or connection lost.
93539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                        // Unselect it immediately.
93669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        sendEmptyMessage(MSG_CONNECTION_TIMED_OUT);
93769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        break;
93869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case TIMEOUT_REASON_WAITING_FOR_CONNECTING:
93969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        // Waiting for route to start connecting.
94069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        sendEmptyMessageDelayed(MSG_CONNECTION_TIMED_OUT, CONNECTING_TIMEOUT);
94169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        break;
94269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case TIMEOUT_REASON_WAITING_FOR_CONNECTED:
94369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        // Waiting for route to complete connection.
94469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        sendEmptyMessageDelayed(MSG_CONNECTION_TIMED_OUT, CONNECTED_TIMEOUT);
94569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        break;
94669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
94769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
94869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
94969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
95069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void connectionTimedOut() {
95169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (mConnectionTimeoutReason == 0 || mGloballySelectedRouteRecord == null) {
95269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // Shouldn't get here.  There must be a bug somewhere.
95369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                Log.wtf(TAG, "Handled connection timeout for no reason.");
95469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return;
95569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
95669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
95769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            switch (mConnectionTimeoutReason) {
95869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case TIMEOUT_REASON_NOT_AVAILABLE:
95969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Slog.i(TAG, "Global route no longer available: "
96069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + mGloballySelectedRouteRecord);
96169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
96239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case TIMEOUT_REASON_CONNECTION_LOST:
96339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    Slog.i(TAG, "Global route connection lost: "
96439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                            + mGloballySelectedRouteRecord);
96539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    break;
96669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case TIMEOUT_REASON_WAITING_FOR_CONNECTING:
96769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Slog.i(TAG, "Global route timed out while waiting for "
96869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + "connection attempt to begin after "
96969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + (SystemClock.uptimeMillis() - mConnectionTimeoutStartTime)
97069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + " ms: " + mGloballySelectedRouteRecord);
97169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
97269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                case TIMEOUT_REASON_WAITING_FOR_CONNECTED:
97369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    Slog.i(TAG, "Global route timed out while connecting after "
97469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + (SystemClock.uptimeMillis() - mConnectionTimeoutStartTime)
97569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            + " ms: " + mGloballySelectedRouteRecord);
97669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    break;
97769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
97869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mConnectionTimeoutReason = 0;
97969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
98069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            unselectGloballySelectedRoute();
98169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
98269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
98369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void scheduleUpdateClientState() {
98469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            if (!mClientStateUpdateScheduled) {
98569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mClientStateUpdateScheduled = true;
98669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                sendEmptyMessage(MSG_UPDATE_CLIENT_STATE);
98769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
98869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
98969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
99069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private void updateClientState() {
99169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            mClientStateUpdateScheduled = false;
99269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
993af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            final String globallySelectedRouteId = mGloballySelectedRouteRecord != null ?
99469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mGloballySelectedRouteRecord.getUniqueId() : null;
995af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
996af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Build a new client state for trusted clients.
997af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            MediaRouterClientState trustedState = new MediaRouterClientState();
998af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            trustedState.globallySelectedRouteId = globallySelectedRouteId;
99969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int providerCount = mProviderRecords.size();
100069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < providerCount; i++) {
1001af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                mProviderRecords.get(i).appendClientState(trustedState);
1002af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            }
1003af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown
1004af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // Build a new client state for untrusted clients that can only see
1005af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            // the currently selected route.
1006af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            MediaRouterClientState untrustedState = new MediaRouterClientState();
1007af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            untrustedState.globallySelectedRouteId = globallySelectedRouteId;
1008af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown            if (globallySelectedRouteId != null) {
1009af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                untrustedState.routes.add(trustedState.getRoute(globallySelectedRouteId));
101069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
101169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
101269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            try {
101369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                synchronized (mService.mLock) {
101469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Update the UserRecord.
1015af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    mUserRecord.mTrustedState = trustedState;
1016af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown                    mUserRecord.mUntrustedState = untrustedState;
101769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
101869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Collect all clients.
101969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    final int count = mUserRecord.mClientRecords.size();
102069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    for (int i = 0; i < count; i++) {
102169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        mTempClients.add(mUserRecord.mClientRecords.get(i).mClient);
102269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
102369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
102469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
102569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // Notify all clients (outside of the lock).
102669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int count = mTempClients.size();
102769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < count; i++) {
102869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    try {
102969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        mTempClients.get(i).onStateChanged();
103069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    } catch (RemoteException ex) {
103169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        // ignore errors, client probably died
103269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
103369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
103469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            } finally {
103569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // Clear the list in preparation for the next time.
103669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mTempClients.clear();
103769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
103869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
103969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
104069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private int findProviderRecord(RemoteDisplayProviderProxy provider) {
104169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mProviderRecords.size();
104269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
104369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                ProviderRecord record = mProviderRecords.get(i);
104469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (record.getProvider() == provider) {
104569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    return i;
104669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
104769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
104869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return -1;
104969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
105069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
105169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        private RouteRecord findRouteRecord(String uniqueId) {
105269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            final int count = mProviderRecords.size();
105369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            for (int i = 0; i < count; i++) {
105469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                RouteRecord record = mProviderRecords.get(i).findRouteByUniqueId(uniqueId);
105569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (record != null) {
105669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    return record;
105769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
105869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
105969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            return null;
106069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
106169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
106239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        private static int getConnectionPhase(int status) {
106339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            switch (status) {
106439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case MediaRouter.RouteInfo.STATUS_NONE:
106539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case MediaRouter.RouteInfo.STATUS_CONNECTED:
106639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    return PHASE_CONNECTED;
106739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case MediaRouter.RouteInfo.STATUS_CONNECTING:
106839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    return PHASE_CONNECTING;
106939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case MediaRouter.RouteInfo.STATUS_SCANNING:
107039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case MediaRouter.RouteInfo.STATUS_AVAILABLE:
107139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    return PHASE_NOT_CONNECTED;
107239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case MediaRouter.RouteInfo.STATUS_NOT_AVAILABLE:
107339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                case MediaRouter.RouteInfo.STATUS_IN_USE:
107439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                default:
107539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown                    return PHASE_NOT_AVAILABLE;
107639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown            }
107739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown        }
107839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown
107969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        static final class ProviderRecord {
108069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private final RemoteDisplayProviderProxy mProvider;
108169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private final String mUniquePrefix;
108269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private final ArrayList<RouteRecord> mRoutes = new ArrayList<RouteRecord>();
108369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private RemoteDisplayState mDescriptor;
108469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
108569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public ProviderRecord(RemoteDisplayProviderProxy provider) {
108669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mProvider = provider;
108769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mUniquePrefix = provider.getFlattenedComponentName() + ":";
108869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
108969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
109069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public RemoteDisplayProviderProxy getProvider() {
109169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mProvider;
109269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
109369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
109469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public String getUniquePrefix() {
109569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mUniquePrefix;
109669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
109769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
109869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public boolean updateDescriptor(RemoteDisplayState descriptor) {
109969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                boolean changed = false;
110069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (mDescriptor != descriptor) {
110169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mDescriptor = descriptor;
110269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
110369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Update all existing routes and reorder them to match
110469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // the order of their descriptors.
110569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    int targetIndex = 0;
110669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (descriptor != null) {
110769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (descriptor.isValid()) {
110869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            final List<RemoteDisplayInfo> routeDescriptors = descriptor.displays;
110969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            final int routeCount = routeDescriptors.size();
111069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            for (int i = 0; i < routeCount; i++) {
111169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                final RemoteDisplayInfo routeDescriptor =
111269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                        routeDescriptors.get(i);
111369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                final String descriptorId = routeDescriptor.id;
111469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                final int sourceIndex = findRouteByDescriptorId(descriptorId);
111569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                if (sourceIndex < 0) {
111669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    // Add the route to the provider.
111769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    String uniqueId = assignRouteUniqueId(descriptorId);
111869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    RouteRecord route =
111969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                            new RouteRecord(this, descriptorId, uniqueId);
112069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    mRoutes.add(targetIndex++, route);
112169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    route.updateDescriptor(routeDescriptor);
112269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    changed = true;
112369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                } else if (sourceIndex < targetIndex) {
112469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    // Ignore route with duplicate id.
112569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    Slog.w(TAG, "Ignoring route descriptor with duplicate id: "
112669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                            + routeDescriptor);
112769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                } else {
112869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    // Reorder existing route within the list.
112969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    RouteRecord route = mRoutes.get(sourceIndex);
113069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    Collections.swap(mRoutes, sourceIndex, targetIndex++);
113169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    changed |= route.updateDescriptor(routeDescriptor);
113269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                }
113369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            }
113469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        } else {
113569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            Slog.w(TAG, "Ignoring invalid descriptor from media route provider: "
113669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                                    + mProvider.getFlattenedComponentName());
113769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
113869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
113969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
114069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    // Dispose all remaining routes that do not have matching descriptors.
114169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    for (int i = mRoutes.size() - 1; i >= targetIndex; i--) {
114269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        RouteRecord route = mRoutes.remove(i);
114369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        route.updateDescriptor(null); // mark route invalid
114469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        changed = true;
114569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
114669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
114769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return changed;
114869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
114969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
115069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public void appendClientState(MediaRouterClientState state) {
115169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int routeCount = mRoutes.size();
115269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < routeCount; i++) {
115369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    state.routes.add(mRoutes.get(i).getInfo());
115469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
115569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
115669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
115769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public RouteRecord findRouteByUniqueId(String uniqueId) {
115869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int routeCount = mRoutes.size();
115969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < routeCount; i++) {
116069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    RouteRecord route = mRoutes.get(i);
116169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (route.getUniqueId().equals(uniqueId)) {
116269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return route;
116369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
116469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
116569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return null;
116669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
116769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
116869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private int findRouteByDescriptorId(String descriptorId) {
116969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int routeCount = mRoutes.size();
117069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                for (int i = 0; i < routeCount; i++) {
117169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    RouteRecord route = mRoutes.get(i);
117269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (route.getDescriptorId().equals(descriptorId)) {
117369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return i;
117469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
117569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
117669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return -1;
117769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
117869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
117969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public void dump(PrintWriter pw, String prefix) {
118069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(prefix + this);
118169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
118269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final String indent = prefix + "  ";
118369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mProvider.dump(pw, indent);
118469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
118569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int routeCount = mRoutes.size();
118669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (routeCount != 0) {
118769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    for (int i = 0; i < routeCount; i++) {
118869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        mRoutes.get(i).dump(pw, indent);
118969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
119069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else {
119169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    pw.println(indent + "<no routes>");
119269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
119369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
119469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
119569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
119669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public String toString() {
119769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return "Provider " + mProvider.getFlattenedComponentName();
119869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
119969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
120069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private String assignRouteUniqueId(String descriptorId) {
120169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mUniquePrefix + descriptorId;
120269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
120369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
120469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
120569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        static final class RouteRecord {
120669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private final ProviderRecord mProviderRecord;
120769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private final String mDescriptorId;
120869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private final MediaRouterClientState.RouteInfo mMutableInfo;
120969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private MediaRouterClientState.RouteInfo mImmutableInfo;
121069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private RemoteDisplayInfo mDescriptor;
121169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
121269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public RouteRecord(ProviderRecord providerRecord,
121369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    String descriptorId, String uniqueId) {
121469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mProviderRecord = providerRecord;
121569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mDescriptorId = descriptorId;
121669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                mMutableInfo = new MediaRouterClientState.RouteInfo(uniqueId);
121769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
121869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
121969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public RemoteDisplayProviderProxy getProvider() {
122069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mProviderRecord.getProvider();
122169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
122269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
122369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public ProviderRecord getProviderRecord() {
122469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mProviderRecord;
122569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
122669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
122769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public String getDescriptorId() {
122869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mDescriptorId;
122969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
123069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
123169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public String getUniqueId() {
123269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mMutableInfo.id;
123369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
123469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
123569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public MediaRouterClientState.RouteInfo getInfo() {
123669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (mImmutableInfo == null) {
123769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mImmutableInfo = new MediaRouterClientState.RouteInfo(mMutableInfo);
123869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
123969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mImmutableInfo;
124069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
124169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
124269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public boolean isValid() {
124369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mDescriptor != null;
124469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
124569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
124669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public boolean isEnabled() {
124769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mMutableInfo.enabled;
124869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
124969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
125069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public int getStatus() {
125169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return mMutableInfo.statusCode;
125269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
125369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
125469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public boolean updateDescriptor(RemoteDisplayInfo descriptor) {
125569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                boolean changed = false;
125669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (mDescriptor != descriptor) {
125769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mDescriptor = descriptor;
125869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    if (descriptor != null) {
125969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final String name = computeName(descriptor);
1260e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root                        if (!Objects.equals(mMutableInfo.name, name)) {
126169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.name = name;
126269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
126369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
126469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final String description = computeDescription(descriptor);
1265e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root                        if (!Objects.equals(mMutableInfo.description, description)) {
126669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.description = description;
126769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
126869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
126969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int supportedTypes = computeSupportedTypes(descriptor);
127069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.supportedTypes != supportedTypes) {
127169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.supportedTypes = supportedTypes;
127269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
127369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
127469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final boolean enabled = computeEnabled(descriptor);
127569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.enabled != enabled) {
127669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.enabled = enabled;
127769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
127869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
127969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int statusCode = computeStatusCode(descriptor);
128069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.statusCode != statusCode) {
128169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.statusCode = statusCode;
128269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
128369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
128469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int playbackType = computePlaybackType(descriptor);
128569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.playbackType != playbackType) {
128669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.playbackType = playbackType;
128769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
128869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
128969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int playbackStream = computePlaybackStream(descriptor);
129069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.playbackStream != playbackStream) {
129169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.playbackStream = playbackStream;
129269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
129369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
129469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int volume = computeVolume(descriptor);
129569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.volume != volume) {
129669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.volume = volume;
129769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
129869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
129969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int volumeMax = computeVolumeMax(descriptor);
130069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.volumeMax != volumeMax) {
130169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.volumeMax = volumeMax;
130269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
130369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
130469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int volumeHandling = computeVolumeHandling(descriptor);
130569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.volumeHandling != volumeHandling) {
130669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.volumeHandling = volumeHandling;
130769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
130869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
130969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        final int presentationDisplayId = computePresentationDisplayId(descriptor);
131069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        if (mMutableInfo.presentationDisplayId != presentationDisplayId) {
131169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            mMutableInfo.presentationDisplayId = presentationDisplayId;
131269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                            changed = true;
131369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        }
131469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    }
131569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
131669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (changed) {
131769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    mImmutableInfo = null;
131869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
131969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return changed;
132069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
132169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
132269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public void dump(PrintWriter pw, String prefix) {
132369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(prefix + this);
132469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
132569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final String indent = prefix + "  ";
132669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(indent + "mMutableInfo=" + mMutableInfo);
132769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(indent + "mDescriptorId=" + mDescriptorId);
132869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                pw.println(indent + "mDescriptor=" + mDescriptor);
132969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
133069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
133169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            @Override
133269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            public String toString() {
133369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return "Route " + mMutableInfo.name + " (" + mMutableInfo.id + ")";
133469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
133569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
133669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static String computeName(RemoteDisplayInfo descriptor) {
133769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // Note that isValid() already ensures the name is non-empty.
133869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return descriptor.name;
133969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
134069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
134169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static String computeDescription(RemoteDisplayInfo descriptor) {
134269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final String description = descriptor.description;
134369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return TextUtils.isEmpty(description) ? null : description;
134469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
134569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
134669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computeSupportedTypes(RemoteDisplayInfo descriptor) {
134769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return MediaRouter.ROUTE_TYPE_LIVE_AUDIO
134869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        | MediaRouter.ROUTE_TYPE_LIVE_VIDEO
134969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        | MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
135069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
135169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
135269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static boolean computeEnabled(RemoteDisplayInfo descriptor) {
135369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                switch (descriptor.status) {
135469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_CONNECTED:
135569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_CONNECTING:
135669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_AVAILABLE:
135769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return true;
135869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    default:
135969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return false;
136069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
136169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
136269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
136369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computeStatusCode(RemoteDisplayInfo descriptor) {
136469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                switch (descriptor.status) {
136569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_NOT_AVAILABLE:
136669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.STATUS_NOT_AVAILABLE;
136769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_AVAILABLE:
136869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.STATUS_AVAILABLE;
136969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_IN_USE:
137069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.STATUS_IN_USE;
137169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_CONNECTING:
137269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.STATUS_CONNECTING;
137369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.STATUS_CONNECTED:
137469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.STATUS_CONNECTED;
137569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    default:
137669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.STATUS_NONE;
137769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
137869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
137969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
138069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computePlaybackType(RemoteDisplayInfo descriptor) {
138169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
138269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
138369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
138469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computePlaybackStream(RemoteDisplayInfo descriptor) {
138569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return AudioSystem.STREAM_MUSIC;
138669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
138769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
138869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computeVolume(RemoteDisplayInfo descriptor) {
138969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int volume = descriptor.volume;
139069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int volumeMax = descriptor.volumeMax;
139169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                if (volume < 0) {
139269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    return 0;
139369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                } else if (volume > volumeMax) {
139469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    return volumeMax;
139569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
139669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return volume;
139769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
139869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
139969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computeVolumeMax(RemoteDisplayInfo descriptor) {
140069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int volumeMax = descriptor.volumeMax;
140169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return volumeMax > 0 ? volumeMax : 0;
140269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
140369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
140469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computeVolumeHandling(RemoteDisplayInfo descriptor) {
140569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int volumeHandling = descriptor.volumeHandling;
140669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                switch (volumeHandling) {
140769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.PLAYBACK_VOLUME_VARIABLE:
140869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
140969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    case RemoteDisplayInfo.PLAYBACK_VOLUME_FIXED:
141069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    default:
141169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                        return MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED;
141269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                }
141369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
141469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown
141569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            private static int computePresentationDisplayId(RemoteDisplayInfo descriptor) {
141669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // The MediaRouter class validates that the id corresponds to an extant
141769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                // presentation display.  So all we do here is canonicalize the null case.
141869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                final int displayId = descriptor.presentationDisplayId;
141969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                return displayId < 0 ? -1 : displayId;
142069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown            }
142169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown        }
142269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown    }
142369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown}
1424