Merge "Make AudioPackage11 (N7 v2) more like AudioPackage12_48 (N5)" into klp-dev
diff --git a/Android.mk b/Android.mk
index f97849c..b0a27e4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -251,10 +251,14 @@
media/java/android/media/IAudioService.aidl \
media/java/android/media/IAudioFocusDispatcher.aidl \
media/java/android/media/IAudioRoutesObserver.aidl \
+ media/java/android/media/IMediaRouterClient.aidl \
+ media/java/android/media/IMediaRouterService.aidl \
media/java/android/media/IMediaScannerListener.aidl \
media/java/android/media/IMediaScannerService.aidl \
media/java/android/media/IRemoteControlClient.aidl \
media/java/android/media/IRemoteControlDisplay.aidl \
+ media/java/android/media/IRemoteDisplayCallback.aidl \
+ media/java/android/media/IRemoteDisplayProvider.aidl \
media/java/android/media/IRemoteVolumeObserver.aidl \
media/java/android/media/IRingtonePlayer.aidl \
telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index bb04063..7ca3459 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2272,9 +2272,12 @@
public static void dumpPackageStateStatic(FileDescriptor fd, String packageName) {
FileOutputStream fout = new FileOutputStream(fd);
PrintWriter pw = new FastPrintWriter(fout);
- dumpService(pw, fd, Context.ACTIVITY_SERVICE, new String[] { "package", packageName });
+ dumpService(pw, fd, Context.ACTIVITY_SERVICE, new String[] {
+ "-a", "package", packageName });
pw.println();
- dumpService(pw, fd, ProcessStats.SERVICE_NAME, new String[] { packageName });
+ dumpService(pw, fd, "meminfo", new String[] { "--local", packageName });
+ pw.println();
+ dumpService(pw, fd, ProcessStats.SERVICE_NAME, new String[] { "-a", packageName });
pw.println();
dumpService(pw, fd, "usagestats", new String[] { "--packages", packageName });
pw.println();
@@ -2296,7 +2299,7 @@
pw.flush();
tp = new TransferPipe();
tp.setBufferPrefix(" ");
- service.dump(tp.getWriteFd().getFileDescriptor(), args);
+ service.dumpAsync(tp.getWriteFd().getFileDescriptor(), args);
tp.go(fd);
} catch (Throwable e) {
if (tp != null) {
diff --git a/core/java/android/app/MediaRouteActionProvider.java b/core/java/android/app/MediaRouteActionProvider.java
index 63b641c..dffa969 100644
--- a/core/java/android/app/MediaRouteActionProvider.java
+++ b/core/java/android/app/MediaRouteActionProvider.java
@@ -16,10 +16,7 @@
package android.app;
-import com.android.internal.app.MediaRouteChooserDialogFragment;
-
import android.content.Context;
-import android.content.ContextWrapper;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.util.Log;
@@ -30,22 +27,38 @@
import java.lang.ref.WeakReference;
+/**
+ * The media route action provider displays a {@link MediaRouteButton media route button}
+ * in the application's {@link ActionBar} to allow the user to select routes and
+ * to control the currently selected route.
+ * <p>
+ * The application must specify the kinds of routes that the user should be allowed
+ * to select by specifying the route types with the {@link #setRouteTypes} method.
+ * </p><p>
+ * Refer to {@link MediaRouteButton} for a description of the button that will
+ * appear in the action bar menu. Note that instead of disabling the button
+ * when no routes are available, the action provider will instead make the
+ * menu item invisible. In this way, the button will only be visible when it
+ * is possible for the user to discover and select a matching route.
+ * </p>
+ */
public class MediaRouteActionProvider extends ActionProvider {
private static final String TAG = "MediaRouteActionProvider";
- private Context mContext;
- private MediaRouter mRouter;
- private MenuItem mMenuItem;
- private MediaRouteButton mView;
+ private final Context mContext;
+ private final MediaRouter mRouter;
+ private final MediaRouterCallback mCallback;
+
private int mRouteTypes;
+ private MediaRouteButton mButton;
private View.OnClickListener mExtendedSettingsListener;
- private RouterCallback mCallback;
public MediaRouteActionProvider(Context context) {
super(context);
+
mContext = context;
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
- mCallback = new RouterCallback(this);
+ mCallback = new MediaRouterCallback(this);
// Start with live audio by default.
// TODO Update this when new route types are added; segment by API level
@@ -53,80 +66,74 @@
setRouteTypes(MediaRouter.ROUTE_TYPE_LIVE_AUDIO);
}
+ /**
+ * Sets the types of routes that will be shown in the media route chooser dialog
+ * launched by this button.
+ *
+ * @param types The route types to match.
+ */
public void setRouteTypes(int types) {
- if (mRouteTypes == types) return;
- if (mRouteTypes != 0) {
- mRouter.removeCallback(mCallback);
+ if (mRouteTypes != types) {
+ // FIXME: We currently have no way of knowing whether the action provider
+ // is still needed by the UI. Unfortunately this means the action provider
+ // may leak callbacks until garbage collection occurs. This may result in
+ // media route providers doing more work than necessary in the short term
+ // while trying to discover routes that are no longer of interest to the
+ // application. To solve this problem, the action provider will need some
+ // indication from the framework that it is being destroyed.
+ if (mRouteTypes != 0) {
+ mRouter.removeCallback(mCallback);
+ }
+ mRouteTypes = types;
+ if (types != 0) {
+ mRouter.addCallback(types, mCallback,
+ MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
+ }
+ refreshRoute();
+
+ if (mButton != null) {
+ mButton.setRouteTypes(mRouteTypes);
+ }
}
- mRouteTypes = types;
- if (types != 0) {
- mRouter.addCallback(types, mCallback);
- }
- if (mView != null) {
- mView.setRouteTypes(mRouteTypes);
+ }
+
+ public void setExtendedSettingsClickListener(View.OnClickListener listener) {
+ mExtendedSettingsListener = listener;
+ if (mButton != null) {
+ mButton.setExtendedSettingsClickListener(listener);
}
}
@Override
+ @SuppressWarnings("deprecation")
public View onCreateActionView() {
throw new UnsupportedOperationException("Use onCreateActionView(MenuItem) instead.");
}
@Override
public View onCreateActionView(MenuItem item) {
- if (mMenuItem != null || mView != null) {
+ if (mButton != null) {
Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " +
"with a menu item. Don't reuse MediaRouteActionProvider instances! " +
"Abandoning the old one...");
}
- mMenuItem = item;
- mView = new MediaRouteButton(mContext);
- mView.setCheatSheetEnabled(true);
- mView.setRouteTypes(mRouteTypes);
- mView.setExtendedSettingsClickListener(mExtendedSettingsListener);
- mView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+
+ mButton = new MediaRouteButton(mContext);
+ mButton.setCheatSheetEnabled(true);
+ mButton.setRouteTypes(mRouteTypes);
+ mButton.setExtendedSettingsClickListener(mExtendedSettingsListener);
+ mButton.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.MATCH_PARENT));
- return mView;
+ return mButton;
}
@Override
public boolean onPerformDefaultAction() {
- final FragmentManager fm = getActivity().getFragmentManager();
- // See if one is already attached to this activity.
- MediaRouteChooserDialogFragment dialogFragment =
- (MediaRouteChooserDialogFragment) fm.findFragmentByTag(
- MediaRouteChooserDialogFragment.FRAGMENT_TAG);
- if (dialogFragment != null) {
- Log.w(TAG, "onPerformDefaultAction(): Chooser dialog already showing!");
- return false;
+ if (mButton != null) {
+ return mButton.showDialogInternal();
}
-
- dialogFragment = new MediaRouteChooserDialogFragment();
- dialogFragment.setExtendedSettingsClickListener(mExtendedSettingsListener);
- dialogFragment.setRouteTypes(mRouteTypes);
- dialogFragment.show(fm, MediaRouteChooserDialogFragment.FRAGMENT_TAG);
- return true;
- }
-
- private Activity getActivity() {
- // Gross way of unwrapping the Activity so we can get the FragmentManager
- Context context = mContext;
- while (context instanceof ContextWrapper && !(context instanceof Activity)) {
- context = ((ContextWrapper) context).getBaseContext();
- }
- if (!(context instanceof Activity)) {
- throw new IllegalStateException("The MediaRouteActionProvider's Context " +
- "is not an Activity.");
- }
-
- return (Activity) context;
- }
-
- public void setExtendedSettingsClickListener(View.OnClickListener listener) {
- mExtendedSettingsListener = listener;
- if (mView != null) {
- mView.setExtendedSettingsClickListener(listener);
- }
+ return false;
}
@Override
@@ -136,36 +143,43 @@
@Override
public boolean isVisible() {
- return mRouter.getRouteCount() > 1;
+ return mRouter.isRouteAvailable(mRouteTypes,
+ MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE);
}
- private static class RouterCallback extends MediaRouter.SimpleCallback {
- private WeakReference<MediaRouteActionProvider> mAp;
+ private void refreshRoute() {
+ refreshVisibility();
+ }
- RouterCallback(MediaRouteActionProvider ap) {
- mAp = new WeakReference<MediaRouteActionProvider>(ap);
+ private static class MediaRouterCallback extends MediaRouter.SimpleCallback {
+ private final WeakReference<MediaRouteActionProvider> mProviderWeak;
+
+ public MediaRouterCallback(MediaRouteActionProvider provider) {
+ mProviderWeak = new WeakReference<MediaRouteActionProvider>(provider);
}
@Override
public void onRouteAdded(MediaRouter router, RouteInfo info) {
- final MediaRouteActionProvider ap = mAp.get();
- if (ap == null) {
- router.removeCallback(this);
- return;
- }
-
- ap.refreshVisibility();
+ refreshRoute(router);
}
@Override
public void onRouteRemoved(MediaRouter router, RouteInfo info) {
- final MediaRouteActionProvider ap = mAp.get();
- if (ap == null) {
- router.removeCallback(this);
- return;
- }
+ refreshRoute(router);
+ }
- ap.refreshVisibility();
+ @Override
+ public void onRouteChanged(MediaRouter router, RouteInfo info) {
+ refreshRoute(router);
+ }
+
+ private void refreshRoute(MediaRouter router) {
+ MediaRouteActionProvider provider = mProviderWeak.get();
+ if (provider != null) {
+ provider.refreshRoute();
+ } else {
+ router.removeCallback(this);
+ }
}
}
}
diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java
index 7e0a27a..a7982f4 100644
--- a/core/java/android/app/MediaRouteButton.java
+++ b/core/java/android/app/MediaRouteButton.java
@@ -17,7 +17,7 @@
package android.app;
import com.android.internal.R;
-import com.android.internal.app.MediaRouteChooserDialogFragment;
+import com.android.internal.app.MediaRouteDialogPresenter;
import android.content.Context;
import android.content.ContextWrapper;
@@ -30,7 +30,6 @@
import android.media.MediaRouter.RouteInfo;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.SoundEffectConstants;
@@ -38,17 +37,15 @@
import android.widget.Toast;
public class MediaRouteButton extends View {
- private static final String TAG = "MediaRouteButton";
+ private final MediaRouter mRouter;
+ private final MediaRouterCallback mCallback;
- private MediaRouter mRouter;
- private final MediaRouteCallback mRouterCallback = new MediaRouteCallback();
private int mRouteTypes;
private boolean mAttachedToWindow;
private Drawable mRemoteIndicator;
private boolean mRemoteActive;
- private boolean mToggleMode;
private boolean mCheatSheetEnabled;
private boolean mIsConnecting;
@@ -56,12 +53,13 @@
private int mMinHeight;
private OnClickListener mExtendedSettingsClickListener;
- private MediaRouteChooserDialogFragment mDialogFragment;
+ // The checked state is used when connected to a remote route.
private static final int[] CHECKED_STATE_SET = {
R.attr.state_checked
};
+ // The activated state is used while connecting to a remote route.
private static final int[] ACTIVATED_STATE_SET = {
R.attr.state_activated
};
@@ -78,6 +76,7 @@
super(context, attrs, defStyleAttr);
mRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+ mCallback = new MediaRouterCallback();
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.MediaRouteButton, defStyleAttr, 0);
@@ -98,19 +97,87 @@
setRouteTypes(routeTypes);
}
- private void setRemoteIndicatorDrawable(Drawable d) {
- if (mRemoteIndicator != null) {
- mRemoteIndicator.setCallback(null);
- unscheduleDrawable(mRemoteIndicator);
+ /**
+ * Gets the media route types for filtering the routes that the user can
+ * select using the media route chooser dialog.
+ *
+ * @return The route types.
+ */
+ public int getRouteTypes() {
+ return mRouteTypes;
+ }
+
+ /**
+ * Sets the types of routes that will be shown in the media route chooser dialog
+ * launched by this button.
+ *
+ * @param types The route types to match.
+ */
+ public void setRouteTypes(int types) {
+ if (mRouteTypes != types) {
+ if (mAttachedToWindow && mRouteTypes != 0) {
+ mRouter.removeCallback(mCallback);
+ }
+
+ mRouteTypes = types;
+
+ if (mAttachedToWindow && types != 0) {
+ mRouter.addCallback(types, mCallback,
+ MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
+ }
+
+ refreshRoute();
}
- mRemoteIndicator = d;
- if (d != null) {
- d.setCallback(this);
- d.setState(getDrawableState());
- d.setVisible(getVisibility() == VISIBLE, false);
+ }
+
+ public void setExtendedSettingsClickListener(OnClickListener listener) {
+ mExtendedSettingsClickListener = listener;
+ }
+
+ /**
+ * Show the route chooser or controller dialog.
+ * <p>
+ * If the default route is selected or if the currently selected route does
+ * not match the {@link #getRouteTypes route types}, then shows the route chooser dialog.
+ * Otherwise, shows the route controller dialog to offer the user
+ * a choice to disconnect from the route or perform other control actions
+ * such as setting the route's volume.
+ * </p><p>
+ * This will attach a {@link DialogFragment} to the containing Activity.
+ * </p>
+ */
+ public void showDialog() {
+ showDialogInternal();
+ }
+
+ boolean showDialogInternal() {
+ if (!mAttachedToWindow) {
+ return false;
}
- refreshDrawableState();
+ DialogFragment f = MediaRouteDialogPresenter.showDialogFragment(getActivity(),
+ mRouteTypes, mExtendedSettingsClickListener);
+ return f != null;
+ }
+
+ private Activity getActivity() {
+ // Gross way of unwrapping the Activity so we can get the FragmentManager
+ Context context = getContext();
+ while (context instanceof ContextWrapper) {
+ if (context instanceof Activity) {
+ return (Activity)context;
+ }
+ context = ((ContextWrapper)context).getBaseContext();
+ }
+ throw new IllegalStateException("The MediaRouteButton's Context is not an Activity.");
+ }
+
+ /**
+ * Sets whether to enable showing a toast with the content descriptor of the
+ * button when the button is long pressed.
+ */
+ void setCheatSheetEnabled(boolean enable) {
+ mCheatSheetEnabled = enable;
}
@Override
@@ -120,29 +187,7 @@
if (!handled) {
playSoundEffect(SoundEffectConstants.CLICK);
}
-
- if (mToggleMode) {
- if (mRemoteActive) {
- mRouter.selectRouteInt(mRouteTypes, mRouter.getDefaultRoute());
- } else {
- final int N = mRouter.getRouteCount();
- for (int i = 0; i < N; i++) {
- final RouteInfo route = mRouter.getRouteAt(i);
- if ((route.getSupportedTypes() & mRouteTypes) != 0 &&
- route != mRouter.getDefaultRoute()) {
- mRouter.selectRouteInt(mRouteTypes, route);
- }
- }
- }
- } else {
- showDialog();
- }
-
- return handled;
- }
-
- void setCheatSheetEnabled(boolean enable) {
- mCheatSheetEnabled = enable;
+ return showDialogInternal() || handled;
}
@Override
@@ -183,85 +228,9 @@
}
cheatSheet.show();
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-
return true;
}
- public void setRouteTypes(int types) {
- if (types == mRouteTypes) {
- // Already registered; nothing to do.
- return;
- }
-
- if (mAttachedToWindow && mRouteTypes != 0) {
- mRouter.removeCallback(mRouterCallback);
- }
-
- mRouteTypes = types;
-
- if (mAttachedToWindow) {
- updateRouteInfo();
- mRouter.addCallback(types, mRouterCallback);
- }
- }
-
- private void updateRouteInfo() {
- updateRemoteIndicator();
- updateRouteCount();
- }
-
- public int getRouteTypes() {
- return mRouteTypes;
- }
-
- void updateRemoteIndicator() {
- final RouteInfo selected = mRouter.getSelectedRoute(mRouteTypes);
- final boolean isRemote = selected != mRouter.getDefaultRoute();
- final boolean isConnecting = selected != null &&
- selected.getStatusCode() == RouteInfo.STATUS_CONNECTING;
-
- boolean needsRefresh = false;
- if (mRemoteActive != isRemote) {
- mRemoteActive = isRemote;
- needsRefresh = true;
- }
- if (mIsConnecting != isConnecting) {
- mIsConnecting = isConnecting;
- needsRefresh = true;
- }
-
- if (needsRefresh) {
- refreshDrawableState();
- }
- }
-
- void updateRouteCount() {
- final int N = mRouter.getRouteCount();
- int count = 0;
- boolean hasVideoRoutes = false;
- for (int i = 0; i < N; i++) {
- final RouteInfo route = mRouter.getRouteAt(i);
- final int routeTypes = route.getSupportedTypes();
- if ((routeTypes & mRouteTypes) != 0) {
- if (route instanceof RouteGroup) {
- count += ((RouteGroup) route).getRouteCount();
- } else {
- count++;
- }
- if ((routeTypes & MediaRouter.ROUTE_TYPE_LIVE_VIDEO) != 0) {
- hasVideoRoutes = true;
- }
- }
- }
-
- setEnabled(count != 0);
-
- // Only allow toggling if we have more than just user routes.
- // Don't toggle if we support video routes, we may have to let the dialog scan.
- mToggleMode = count == 2 && (mRouteTypes & MediaRouter.ROUTE_TYPE_LIVE_AUDIO) != 0 &&
- !hasVideoRoutes;
- }
-
@Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
@@ -289,6 +258,21 @@
}
}
+ private void setRemoteIndicatorDrawable(Drawable d) {
+ if (mRemoteIndicator != null) {
+ mRemoteIndicator.setCallback(null);
+ unscheduleDrawable(mRemoteIndicator);
+ }
+ mRemoteIndicator = d;
+ if (d != null) {
+ d.setCallback(this);
+ d.setState(getDrawableState());
+ d.setVisible(getVisibility() == VISIBLE, false);
+ }
+
+ refreshDrawableState();
+ }
+
@Override
protected boolean verifyDrawable(Drawable who) {
return super.verifyDrawable(who) || who == mRemoteIndicator;
@@ -297,12 +281,16 @@
@Override
public void jumpDrawablesToCurrentState() {
super.jumpDrawablesToCurrentState();
- if (mRemoteIndicator != null) mRemoteIndicator.jumpToCurrentState();
+
+ if (mRemoteIndicator != null) {
+ mRemoteIndicator.jumpToCurrentState();
+ }
}
@Override
public void setVisibility(int visibility) {
super.setVisibility(visibility);
+
if (mRemoteIndicator != null) {
mRemoteIndicator.setVisible(getVisibility() == VISIBLE, false);
}
@@ -311,19 +299,22 @@
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
+
mAttachedToWindow = true;
if (mRouteTypes != 0) {
- mRouter.addCallback(mRouteTypes, mRouterCallback);
- updateRouteInfo();
+ mRouter.addCallback(mRouteTypes, mCallback,
+ MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
}
+ refreshRoute();
}
@Override
public void onDetachedFromWindow() {
- if (mRouteTypes != 0) {
- mRouter.removeCallback(mRouterCallback);
- }
mAttachedToWindow = false;
+ if (mRouteTypes != 0) {
+ mRouter.removeCallback(mCallback);
+ }
+
super.onDetachedFromWindow();
}
@@ -386,93 +377,71 @@
final int drawLeft = left + (right - left - drawWidth) / 2;
final int drawTop = top + (bottom - top - drawHeight) / 2;
- mRemoteIndicator.setBounds(drawLeft, drawTop, drawLeft + drawWidth, drawTop + drawHeight);
+ mRemoteIndicator.setBounds(drawLeft, drawTop,
+ drawLeft + drawWidth, drawTop + drawHeight);
mRemoteIndicator.draw(canvas);
}
- public void setExtendedSettingsClickListener(OnClickListener listener) {
- mExtendedSettingsClickListener = listener;
- if (mDialogFragment != null) {
- mDialogFragment.setExtendedSettingsClickListener(listener);
- }
- }
+ private void refreshRoute() {
+ if (mAttachedToWindow) {
+ final MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
+ final boolean isRemote = !route.isDefault() && route.matchesTypes(mRouteTypes);
+ final boolean isConnecting = isRemote && route.isConnecting();
- /**
- * Asynchronously show the route chooser dialog.
- * This will attach a {@link DialogFragment} to the containing Activity.
- */
- public void showDialog() {
- final FragmentManager fm = getActivity().getFragmentManager();
- if (mDialogFragment == null) {
- // See if one is already attached to this activity.
- mDialogFragment = (MediaRouteChooserDialogFragment) fm.findFragmentByTag(
- MediaRouteChooserDialogFragment.FRAGMENT_TAG);
- }
- if (mDialogFragment != null) {
- Log.w(TAG, "showDialog(): Already showing!");
- return;
- }
-
- mDialogFragment = new MediaRouteChooserDialogFragment();
- mDialogFragment.setExtendedSettingsClickListener(mExtendedSettingsClickListener);
- mDialogFragment.setLauncherListener(new MediaRouteChooserDialogFragment.LauncherListener() {
- @Override
- public void onDetached(MediaRouteChooserDialogFragment detachedFragment) {
- mDialogFragment = null;
+ boolean needsRefresh = false;
+ if (mRemoteActive != isRemote) {
+ mRemoteActive = isRemote;
+ needsRefresh = true;
}
- });
- mDialogFragment.setRouteTypes(mRouteTypes);
- mDialogFragment.show(fm, MediaRouteChooserDialogFragment.FRAGMENT_TAG);
+ if (mIsConnecting != isConnecting) {
+ mIsConnecting = isConnecting;
+ needsRefresh = true;
+ }
+
+ if (needsRefresh) {
+ refreshDrawableState();
+ }
+
+ setEnabled(mRouter.isRouteAvailable(mRouteTypes,
+ MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE));
+ }
}
- private Activity getActivity() {
- // Gross way of unwrapping the Activity so we can get the FragmentManager
- Context context = getContext();
- while (context instanceof ContextWrapper && !(context instanceof Activity)) {
- context = ((ContextWrapper) context).getBaseContext();
- }
- if (!(context instanceof Activity)) {
- throw new IllegalStateException("The MediaRouteButton's Context is not an Activity.");
- }
-
- return (Activity) context;
- }
-
- private class MediaRouteCallback extends MediaRouter.SimpleCallback {
- @Override
- public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
- updateRemoteIndicator();
- }
-
- @Override
- public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
- updateRemoteIndicator();
- }
-
- @Override
- public void onRouteChanged(MediaRouter router, RouteInfo info) {
- updateRemoteIndicator();
- }
-
+ private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
@Override
public void onRouteAdded(MediaRouter router, RouteInfo info) {
- updateRouteCount();
+ refreshRoute();
}
@Override
public void onRouteRemoved(MediaRouter router, RouteInfo info) {
- updateRouteCount();
+ refreshRoute();
+ }
+
+ @Override
+ public void onRouteChanged(MediaRouter router, RouteInfo info) {
+ refreshRoute();
+ }
+
+ @Override
+ public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
+ refreshRoute();
+ }
+
+ @Override
+ public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
+ refreshRoute();
}
@Override
public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
int index) {
- updateRouteCount();
+ refreshRoute();
}
@Override
public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
- updateRouteCount();
+ refreshRoute();
}
}
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 4fe2c4d..a38beec 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -334,6 +334,27 @@
/**
* <p>
+ * If set to 1, the HAL will always split result
+ * metadata for a single capture into multiple buffers,
+ * returned using multiple process_capture_result calls.
+ * </p>
+ * <p>
+ * Does not need to be listed in static
+ * metadata. Support for partial results will be reworked in
+ * future versions of camera service. This quirk will stop
+ * working at that point; DO NOT USE without careful
+ * consideration of future support.
+ * </p>
+ *
+ * <b>Optional</b> - This value may be null on some devices.
+ *
+ * @hide
+ */
+ public static final Key<Byte> QUIRKS_USE_PARTIAL_RESULT =
+ new Key<Byte>("android.quirks.usePartialResult", byte.class);
+
+ /**
+ * <p>
* How many output streams can be allocated at
* the same time for each type of stream
* </p>
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 7095e4d..9e8d7d1 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -631,6 +631,36 @@
}
/**
+ * This method is called when some results from an image capture are
+ * available.
+ *
+ * <p>The result provided here will contain some subset of the fields of
+ * a full result. Multiple onCapturePartial calls may happen per
+ * capture; a given result field will only be present in one partial
+ * capture at most. The final onCaptureCompleted call will always
+ * contain all the fields, whether onCapturePartial was called or
+ * not.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param camera The CameraDevice sending the callback.
+ * @param request The request that was given to the CameraDevice
+ * @param result The partial output metadata from the capture, which
+ * includes a subset of the CaptureResult fields.
+ *
+ * @see #capture
+ * @see #captureBurst
+ * @see #setRepeatingRequest
+ * @see #setRepeatingBurst
+ *
+ * @hide
+ */
+ public void onCapturePartial(CameraDevice camera,
+ CaptureRequest request, CaptureResult result) {
+ // default empty implementation
+ }
+
+ /**
* This method is called when an image capture has completed and the
* result metadata is available.
*
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index dbd0457..535b963 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -591,6 +591,32 @@
/**
* <p>
+ * Whether a result given to the framework is the
+ * final one for the capture, or only a partial that contains a
+ * subset of the full set of dynamic metadata
+ * values.
+ * </p>
+ * <p>
+ * The entries in the result metadata buffers for a
+ * single capture may not overlap, except for this entry. The
+ * FINAL buffers must retain FIFO ordering relative to the
+ * requests that generate them, so the FINAL buffer for frame 3 must
+ * always be sent to the framework after the FINAL buffer for frame 2, and
+ * before the FINAL buffer for frame 4. PARTIAL buffers may be returned
+ * in any order relative to other frames, but all PARTIAL buffers for a given
+ * capture must arrive before the FINAL buffer for that capture. This entry may
+ * only be used by the HAL if quirks.usePartialResult is set to 1.
+ * </p>
+ *
+ * <b>Optional</b> - This value may be null on some devices.
+ *
+ * @hide
+ */
+ public static final Key<Boolean> QUIRKS_PARTIAL_RESULT =
+ new Key<Boolean>("android.quirks.partialResult", boolean.class);
+
+ /**
+ * <p>
* A frame counter set by the framework. This value monotonically
* increases with every new result (that is, each new result has a unique
* frameCount value).
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index c428a17..40586f0 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -279,6 +279,10 @@
checkIfCameraClosed();
int requestId;
+ if (repeating) {
+ stopRepeating();
+ }
+
try {
requestId = mRemoteDevice.submitRequest(request, repeating);
} catch (CameraRuntimeException e) {
@@ -293,10 +297,6 @@
}
if (repeating) {
- // Queue for deletion after in-flight requests finish
- if (mRepeatingRequestId != REQUEST_ID_NONE) {
- mRepeatingRequestIdDeletedList.add(mRepeatingRequestId);
- }
mRepeatingRequestId = requestId;
}
@@ -577,6 +577,9 @@
}
final CaptureListenerHolder holder;
+ Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
+ boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
+
synchronized (mLock) {
// TODO: move this whole map into this class to make it more testable,
// exposing the methods necessary like subscribeToRequest, unsubscribe..
@@ -585,7 +588,7 @@
holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
// Clean up listener once we no longer expect to see it.
- if (holder != null && !holder.isRepeating()) {
+ if (holder != null && !holder.isRepeating() && !quirkIsPartialResult) {
CameraDevice.this.mCaptureListenerMap.remove(requestId);
}
@@ -595,7 +598,7 @@
// If we received a result for a repeating request and have
// prior repeating requests queued for deletion, remove those
// requests from mCaptureListenerMap.
- if (holder != null && holder.isRepeating()
+ if (holder != null && holder.isRepeating() && !quirkIsPartialResult
&& mRepeatingRequestIdDeletedList.size() > 0) {
Iterator<Integer> iter = mRepeatingRequestIdDeletedList.iterator();
while (iter.hasNext()) {
@@ -619,8 +622,25 @@
final CaptureRequest request = holder.getRequest();
final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
- holder.getHandler().post(
- new Runnable() {
+ Runnable resultDispatch = null;
+
+ // Either send a partial result or the final capture completed result
+ if (quirkIsPartialResult) {
+ // Partial result
+ resultDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDevice.this.isClosed()){
+ holder.getListener().onCapturePartial(
+ CameraDevice.this,
+ request,
+ resultAsCapture);
+ }
+ }
+ };
+ } else {
+ // Final capture result
+ resultDispatch = new Runnable() {
@Override
public void run() {
if (!CameraDevice.this.isClosed()){
@@ -630,7 +650,10 @@
resultAsCapture);
}
}
- });
+ };
+ }
+
+ holder.getHandler().post(resultDispatch);
}
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 7ae8ca8..9ada6e6 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -106,6 +106,11 @@
public static final int FOREGROUND_ACTIVITY = 10;
/**
+ * A constant indicating a wifi batched scan is active
+ */
+ public static final int WIFI_BATCHED_SCAN = 11;
+
+ /**
* Include all of the data in the stats, including previously saved data.
*/
public static final int STATS_SINCE_CHARGED = 0;
@@ -270,6 +275,8 @@
public abstract void noteFullWifiLockReleasedLocked();
public abstract void noteWifiScanStartedLocked();
public abstract void noteWifiScanStoppedLocked();
+ public abstract void noteWifiBatchedScanStartedLocked(int csph);
+ public abstract void noteWifiBatchedScanStoppedLocked();
public abstract void noteWifiMulticastEnabledLocked();
public abstract void noteWifiMulticastDisabledLocked();
public abstract void noteAudioTurnedOnLocked();
@@ -281,6 +288,7 @@
public abstract long getWifiRunningTime(long batteryRealtime, int which);
public abstract long getFullWifiLockTime(long batteryRealtime, int which);
public abstract long getWifiScanTime(long batteryRealtime, int which);
+ public abstract long getWifiBatchedScanTime(int csphBin, long batteryRealtime, int which);
public abstract long getWifiMulticastTime(long batteryRealtime,
int which);
public abstract long getAudioTurnedOnTime(long batteryRealtime, int which);
@@ -288,6 +296,8 @@
public abstract Timer getForegroundActivityTimer();
public abstract Timer getVibratorOnTimer();
+ public static final int NUM_WIFI_BATCHED_SCAN_BINS = 5;
+
/**
* Note that these must match the constants in android.os.PowerManager.
* Also, if the user activity types change, the BatteryStatsImpl.VERSION must
@@ -2081,9 +2091,11 @@
TimeUtils.formatDuration(ew.usedTime, pw);
pw.print(" over ");
TimeUtils.formatDuration(ew.overTime, pw);
- pw.print(" (");
- pw.print((ew.usedTime*100)/ew.overTime);
- pw.println("%)");
+ if (ew.overTime != 0) {
+ pw.print(" (");
+ pw.print((ew.usedTime*100)/ew.overTime);
+ pw.println("%)");
+ }
}
}
uidActivity = true;
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 37a8102..f7d1eb7 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -1069,11 +1069,11 @@
* @return 0 if the same; less than 0 if this Preference sorts ahead of <var>another</var>;
* greater than 0 if this Preference sorts after <var>another</var>.
*/
+ @Override
public int compareTo(Preference another) {
- if (mOrder != DEFAULT_ORDER
- || (mOrder == DEFAULT_ORDER && another.mOrder != DEFAULT_ORDER)) {
+ if (mOrder != another.mOrder) {
// Do order comparison
- return mOrder - another.mOrder;
+ return mOrder - another.mOrder;
} else if (mTitle == another.mTitle) {
// If titles are null or share same object comparison
return 0;
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index ed9264a..7ddfa87 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -521,7 +521,9 @@
int initialTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
int initialShortTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_SHORT_TITLE, 0);
- if (savedInstanceState != null) {
+ // Restore from headers only if they are supported which
+ // is in multi-pane mode.
+ if (savedInstanceState != null && !mSinglePane) {
// We are restarting from a previous saved state; used that to
// initialize, instead of starting fresh.
ArrayList<Header> headers = savedInstanceState.getParcelableArrayList(HEADERS_TAG);
@@ -573,14 +575,12 @@
// Single pane, showing just a prefs fragment.
findViewById(com.android.internal.R.id.headers).setVisibility(View.GONE);
mPrefsContainer.setVisibility(View.VISIBLE);
- CharSequence initialTitleStr = null;
- CharSequence initialShortTitleStr = null;
if (initialTitle != 0) {
- initialTitleStr = getText(initialTitle);
- initialShortTitleStr = initialShortTitle != 0
+ CharSequence initialTitleStr = getText(initialTitle);
+ CharSequence initialShortTitleStr = initialShortTitle != 0
? getText(initialShortTitle) : null;
+ showBreadCrumbs(initialTitleStr, initialShortTitleStr);
}
- showBreadCrumbs(initialTitleStr, initialShortTitleStr);
} else if (mHeaders.size() > 0) {
setListAdapter(new HeaderAdapter(this, mHeaders));
if (!mSinglePane) {
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 354ea66..7d310a2 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -643,6 +643,15 @@
|| uid == 0;
}
+ /**
+ * Returns true if the display is a public presentation display.
+ * @hide
+ */
+ public boolean isPublicPresentation() {
+ return (mFlags & (Display.FLAG_PRIVATE | Display.FLAG_PRESENTATION)) ==
+ Display.FLAG_PRESENTATION;
+ }
+
private void updateDisplayInfoLocked() {
// Note: The display manager caches display info objects on our behalf.
DisplayInfo newInfo = mGlobal.getDisplayInfo(mDisplayId);
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 9e35a23..7daf798 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -710,6 +710,7 @@
}
d.setLevel(mLevel);
d.setLayoutDirection(getLayoutDirection());
+ d.setVisible(getVisibility() == VISIBLE, true);
mDrawableWidth = d.getIntrinsicWidth();
mDrawableHeight = d.getIntrinsicHeight();
applyColorMod();
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 525517c..43c4b49 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -68,6 +68,8 @@
void noteFullWifiLockReleasedFromSource(in WorkSource ws);
void noteWifiScanStartedFromSource(in WorkSource ws);
void noteWifiScanStoppedFromSource(in WorkSource ws);
+ void noteWifiBatchedScanStartedFromSource(in WorkSource ws, int csph);
+ void noteWifiBatchedScanStoppedFromSource(in WorkSource ws);
void noteWifiMulticastEnabledFromSource(in WorkSource ws);
void noteWifiMulticastDisabledFromSource(in WorkSource ws);
void noteNetworkInterfaceType(String iface, int type);
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialog.java b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
new file mode 100644
index 0000000..944cc83
--- /dev/null
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import com.android.internal.R;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.media.MediaRouter;
+import android.media.MediaRouter.RouteInfo;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * This class implements the route chooser dialog for {@link MediaRouter}.
+ * <p>
+ * This dialog allows the user to choose a route that matches a given selector.
+ * </p>
+ *
+ * @see MediaRouteButton
+ * @see MediaRouteActionProvider
+ *
+ * TODO: Move this back into the API, as in the support library media router.
+ */
+public class MediaRouteChooserDialog extends Dialog {
+ private final MediaRouter mRouter;
+ private final MediaRouterCallback mCallback;
+
+ private int mRouteTypes;
+ private View.OnClickListener mExtendedSettingsClickListener;
+ private RouteAdapter mAdapter;
+ private ListView mListView;
+ private Button mExtendedSettingsButton;
+ private boolean mAttachedToWindow;
+
+ public MediaRouteChooserDialog(Context context, int theme) {
+ super(context, theme);
+
+ mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+ mCallback = new MediaRouterCallback();
+ }
+
+ /**
+ * Gets the media route types for filtering the routes that the user can
+ * select using the media route chooser dialog.
+ *
+ * @return The route types.
+ */
+ public int getRouteTypes() {
+ return mRouteTypes;
+ }
+
+ /**
+ * Sets the types of routes that will be shown in the media route chooser dialog
+ * launched by this button.
+ *
+ * @param types The route types to match.
+ */
+ public void setRouteTypes(int types) {
+ if (mRouteTypes != types) {
+ mRouteTypes = types;
+
+ if (mAttachedToWindow) {
+ mRouter.removeCallback(mCallback);
+ mRouter.addCallback(types, mCallback,
+ MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
+ }
+
+ refreshRoutes();
+ }
+ }
+
+ public void setExtendedSettingsClickListener(View.OnClickListener listener) {
+ if (listener != mExtendedSettingsClickListener) {
+ mExtendedSettingsClickListener = listener;
+ updateExtendedSettingsButton();
+ }
+ }
+
+ /**
+ * Returns true if the route should be included in the list.
+ * <p>
+ * The default implementation returns true for enabled non-default routes that
+ * match the route types. Subclasses can override this method to filter routes
+ * differently.
+ * </p>
+ *
+ * @param route The route to consider, never null.
+ * @return True if the route should be included in the chooser dialog.
+ */
+ public boolean onFilterRoute(MediaRouter.RouteInfo route) {
+ return !route.isDefault() && route.isEnabled() && route.matchesTypes(mRouteTypes);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().requestFeature(Window.FEATURE_LEFT_ICON);
+
+ setContentView(R.layout.media_route_chooser_dialog);
+ setTitle(R.string.media_route_chooser_title);
+
+ // Must be called after setContentView.
+ getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
+ R.drawable.ic_media_route_off_holo_dark);
+
+ mAdapter = new RouteAdapter(getContext());
+ mListView = (ListView)findViewById(R.id.media_route_list);
+ mListView.setAdapter(mAdapter);
+ mListView.setOnItemClickListener(mAdapter);
+ mListView.setEmptyView(findViewById(android.R.id.empty));
+
+ mExtendedSettingsButton = (Button)findViewById(R.id.media_route_extended_settings_button);
+ updateExtendedSettingsButton();
+ }
+
+ private void updateExtendedSettingsButton() {
+ if (mExtendedSettingsButton != null) {
+ mExtendedSettingsButton.setOnClickListener(mExtendedSettingsClickListener);
+ mExtendedSettingsButton.setVisibility(
+ mExtendedSettingsClickListener != null ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mAttachedToWindow = true;
+ mRouter.addCallback(mRouteTypes, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
+ refreshRoutes();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ mAttachedToWindow = false;
+ mRouter.removeCallback(mCallback);
+
+ super.onDetachedFromWindow();
+ }
+
+ /**
+ * Refreshes the list of routes that are shown in the chooser dialog.
+ */
+ public void refreshRoutes() {
+ if (mAttachedToWindow) {
+ mAdapter.update();
+ }
+ }
+
+ private final class RouteAdapter extends ArrayAdapter<MediaRouter.RouteInfo>
+ implements ListView.OnItemClickListener {
+ private final LayoutInflater mInflater;
+
+ public RouteAdapter(Context context) {
+ super(context, 0);
+ mInflater = LayoutInflater.from(context);
+ }
+
+ public void update() {
+ clear();
+ final int count = mRouter.getRouteCount();
+ for (int i = 0; i < count; i++) {
+ MediaRouter.RouteInfo route = mRouter.getRouteAt(i);
+ if (onFilterRoute(route)) {
+ add(route);
+ }
+ }
+ sort(RouteComparator.sInstance);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return getItem(position).isEnabled();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = convertView;
+ if (view == null) {
+ view = mInflater.inflate(R.layout.media_route_list_item, parent, false);
+ }
+ MediaRouter.RouteInfo route = getItem(position);
+ TextView text1 = (TextView)view.findViewById(android.R.id.text1);
+ TextView text2 = (TextView)view.findViewById(android.R.id.text2);
+ text1.setText(route.getName());
+ CharSequence description = route.getDescription();
+ if (TextUtils.isEmpty(description)) {
+ text2.setVisibility(View.GONE);
+ text2.setText("");
+ } else {
+ text2.setVisibility(View.VISIBLE);
+ text2.setText(description);
+ }
+ view.setEnabled(route.isEnabled());
+ return view;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ MediaRouter.RouteInfo route = getItem(position);
+ if (route.isEnabled()) {
+ route.select();
+ dismiss();
+ }
+ }
+ }
+
+ private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
+ @Override
+ public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
+ refreshRoutes();
+ }
+
+ @Override
+ public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
+ refreshRoutes();
+ }
+
+ @Override
+ public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
+ refreshRoutes();
+ }
+
+ @Override
+ public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
+ dismiss();
+ }
+ }
+
+ private static final class RouteComparator implements Comparator<MediaRouter.RouteInfo> {
+ public static final RouteComparator sInstance = new RouteComparator();
+
+ @Override
+ public int compare(MediaRouter.RouteInfo lhs, MediaRouter.RouteInfo rhs) {
+ return lhs.getName().toString().compareTo(rhs.getName().toString());
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
index e300021..ae362af 100644
--- a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,675 +16,86 @@
package com.android.internal.app;
-import com.android.internal.R;
-
-import android.app.Activity;
import android.app.Dialog;
import android.app.DialogFragment;
-import android.app.MediaRouteActionProvider;
-import android.app.MediaRouteButton;
import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.hardware.display.DisplayManager;
-import android.media.MediaRouter;
-import android.media.MediaRouter.RouteCategory;
-import android.media.MediaRouter.RouteGroup;
-import android.media.MediaRouter.RouteInfo;
import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.CheckBox;
-import android.widget.Checkable;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
+import android.view.View.OnClickListener;
/**
- * This class implements the route chooser dialog for {@link MediaRouter}.
+ * Media route chooser dialog fragment.
+ * <p>
+ * Creates a {@link MediaRouteChooserDialog}. The application may subclass
+ * this dialog fragment to customize the media route chooser dialog.
+ * </p>
*
- * @see MediaRouteButton
- * @see MediaRouteActionProvider
+ * TODO: Move this back into the API, as in the support library media router.
*/
public class MediaRouteChooserDialogFragment extends DialogFragment {
- private static final String TAG = "MediaRouteChooserDialogFragment";
- public static final String FRAGMENT_TAG = "android:MediaRouteChooserDialogFragment";
+ private final String ARGUMENT_ROUTE_TYPES = "routeTypes";
- private static final int[] ITEM_LAYOUTS = new int[] {
- R.layout.media_route_list_item_top_header,
- R.layout.media_route_list_item_section_header,
- R.layout.media_route_list_item,
- R.layout.media_route_list_item_checkable,
- R.layout.media_route_list_item_collapse_group
- };
+ private View.OnClickListener mExtendedSettingsClickListener;
- MediaRouter mRouter;
- private int mRouteTypes;
-
- private LayoutInflater mInflater;
- private LauncherListener mLauncherListener;
- private View.OnClickListener mExtendedSettingsListener;
- private RouteAdapter mAdapter;
- private ListView mListView;
- private SeekBar mVolumeSlider;
- private ImageView mVolumeIcon;
-
- final RouteComparator mComparator = new RouteComparator();
- final MediaRouterCallback mCallback = new MediaRouterCallback();
- private boolean mIgnoreSliderVolumeChanges;
- private boolean mIgnoreCallbackVolumeChanges;
-
+ /**
+ * Creates a media route chooser dialog fragment.
+ * <p>
+ * All subclasses of this class must also possess a default constructor.
+ * </p>
+ */
public MediaRouteChooserDialogFragment() {
- setStyle(STYLE_NO_TITLE, R.style.Theme_DeviceDefault_Dialog);
+ setCancelable(true);
+ setStyle(STYLE_NORMAL, android.R.style.Theme_DeviceDefault_Dialog);
}
- public void setLauncherListener(LauncherListener listener) {
- mLauncherListener = listener;
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- mRouter = (MediaRouter) activity.getSystemService(Context.MEDIA_ROUTER_SERVICE);
- mRouter.addCallback(mRouteTypes, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
- }
-
- @Override
- public void onDetach() {
- super.onDetach();
- if (mLauncherListener != null) {
- mLauncherListener.onDetached(this);
- }
- if (mAdapter != null) {
- mAdapter = null;
- }
- mInflater = null;
- mRouter.removeCallback(mCallback);
- mRouter = null;
- }
-
- public void setExtendedSettingsClickListener(View.OnClickListener listener) {
- mExtendedSettingsListener = listener;
+ public int getRouteTypes() {
+ Bundle args = getArguments();
+ return args != null ? args.getInt(ARGUMENT_ROUTE_TYPES) : 0;
}
public void setRouteTypes(int types) {
- mRouteTypes = types;
- }
-
- void updateVolume() {
- if (mRouter == null) return;
-
- final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
- mVolumeIcon.setImageResource(selectedRoute == null ||
- selectedRoute.getPlaybackType() == RouteInfo.PLAYBACK_TYPE_LOCAL ?
- R.drawable.ic_audio_vol : R.drawable.ic_media_route_on_holo_dark);
-
- mIgnoreSliderVolumeChanges = true;
-
- if (selectedRoute == null ||
- selectedRoute.getVolumeHandling() == RouteInfo.PLAYBACK_VOLUME_FIXED) {
- // Disable the slider and show it at max volume.
- mVolumeSlider.setMax(1);
- mVolumeSlider.setProgress(1);
- mVolumeSlider.setEnabled(false);
- } else {
- mVolumeSlider.setEnabled(true);
- mVolumeSlider.setMax(selectedRoute.getVolumeMax());
- mVolumeSlider.setProgress(selectedRoute.getVolume());
- }
-
- mIgnoreSliderVolumeChanges = false;
- }
-
- void changeVolume(int newValue) {
- if (mIgnoreSliderVolumeChanges) return;
-
- final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
- if (selectedRoute != null &&
- selectedRoute.getVolumeHandling() == RouteInfo.PLAYBACK_VOLUME_VARIABLE) {
- final int maxVolume = selectedRoute.getVolumeMax();
- newValue = Math.max(0, Math.min(newValue, maxVolume));
- selectedRoute.requestSetVolume(newValue);
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- mInflater = inflater;
- final View layout = inflater.inflate(R.layout.media_route_chooser_layout, container, false);
-
- mVolumeIcon = (ImageView) layout.findViewById(R.id.volume_icon);
- mVolumeSlider = (SeekBar) layout.findViewById(R.id.volume_slider);
- updateVolume();
- mVolumeSlider.setOnSeekBarChangeListener(new VolumeSliderChangeListener());
-
- if (mExtendedSettingsListener != null) {
- final View extendedSettingsButton = layout.findViewById(R.id.extended_settings);
- extendedSettingsButton.setVisibility(View.VISIBLE);
- extendedSettingsButton.setOnClickListener(mExtendedSettingsListener);
- }
-
- final ListView list = (ListView) layout.findViewById(R.id.list);
- list.setItemsCanFocus(true);
- list.setAdapter(mAdapter = new RouteAdapter());
- list.setOnItemClickListener(mAdapter);
-
- mListView = list;
-
- mAdapter.scrollToSelectedItem();
-
- return layout;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- return new RouteChooserDialog(getActivity(), getTheme());
- }
-
- private static class ViewHolder {
- public TextView text1;
- public TextView text2;
- public ImageView icon;
- public ImageButton expandGroupButton;
- public RouteAdapter.ExpandGroupListener expandGroupListener;
- public int position;
- public CheckBox check;
- }
-
- private class RouteAdapter extends BaseAdapter implements ListView.OnItemClickListener {
- private static final int VIEW_TOP_HEADER = 0;
- private static final int VIEW_SECTION_HEADER = 1;
- private static final int VIEW_ROUTE = 2;
- private static final int VIEW_GROUPING_ROUTE = 3;
- private static final int VIEW_GROUPING_DONE = 4;
-
- private int mSelectedItemPosition = -1;
- private final ArrayList<Object> mItems = new ArrayList<Object>();
-
- private RouteCategory mCategoryEditingGroups;
- private RouteGroup mEditingGroup;
-
- // Temporary lists for manipulation
- private final ArrayList<RouteInfo> mCatRouteList = new ArrayList<RouteInfo>();
- private final ArrayList<RouteInfo> mSortRouteList = new ArrayList<RouteInfo>();
-
- private boolean mIgnoreUpdates;
-
- RouteAdapter() {
- update();
- }
-
- void update() {
- /*
- * This is kind of wacky, but our data sets are going to be
- * fairly small on average. Ideally we should be able to do some of this stuff
- * in-place instead.
- *
- * Basic idea: each entry in mItems represents an item in the list for quick access.
- * Entries can be a RouteCategory (section header), a RouteInfo with a category of
- * mCategoryEditingGroups (a flattened RouteInfo pulled out of its group, allowing
- * the user to change the group),
- */
- if (mIgnoreUpdates) return;
-
- mItems.clear();
-
- final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
- mSelectedItemPosition = -1;
-
- List<RouteInfo> routes;
- final int catCount = mRouter.getCategoryCount();
- for (int i = 0; i < catCount; i++) {
- final RouteCategory cat = mRouter.getCategoryAt(i);
- routes = cat.getRoutes(mCatRouteList);
-
- if (!cat.isSystem()) {
- mItems.add(cat);
- }
-
- if (cat == mCategoryEditingGroups) {
- addGroupEditingCategoryRoutes(routes);
- } else {
- addSelectableRoutes(selectedRoute, routes);
- }
-
- routes.clear();
+ if (types != getRouteTypes()) {
+ Bundle args = getArguments();
+ if (args == null) {
+ args = new Bundle();
}
+ args.putInt(ARGUMENT_ROUTE_TYPES, types);
+ setArguments(args);
- notifyDataSetChanged();
- if (mListView != null && mSelectedItemPosition >= 0) {
- mListView.setItemChecked(mSelectedItemPosition, true);
- }
- }
-
- void scrollToEditingGroup() {
- if (mCategoryEditingGroups == null || mListView == null) return;
-
- int pos = 0;
- int bound = 0;
- final int itemCount = mItems.size();
- for (int i = 0; i < itemCount; i++) {
- final Object item = mItems.get(i);
- if (item != null && item == mCategoryEditingGroups) {
- bound = i;
- }
- if (item == null) {
- pos = i;
- break; // this is always below the category header; we can stop here.
- }
- }
-
- mListView.smoothScrollToPosition(pos, bound);
- }
-
- void scrollToSelectedItem() {
- if (mListView == null || mSelectedItemPosition < 0) return;
-
- mListView.smoothScrollToPosition(mSelectedItemPosition);
- }
-
- void addSelectableRoutes(RouteInfo selectedRoute, List<RouteInfo> from) {
- final int routeCount = from.size();
- for (int j = 0; j < routeCount; j++) {
- final RouteInfo info = from.get(j);
- if (info == selectedRoute) {
- mSelectedItemPosition = mItems.size();
- }
- mItems.add(info);
- }
- }
-
- void addGroupEditingCategoryRoutes(List<RouteInfo> from) {
- // Unpack groups and flatten for presentation
- // mSortRouteList will always be empty here.
- final int topCount = from.size();
- for (int i = 0; i < topCount; i++) {
- final RouteInfo route = from.get(i);
- final RouteGroup group = route.getGroup();
- if (group == route) {
- // This is a group, unpack it.
- final int groupCount = group.getRouteCount();
- for (int j = 0; j < groupCount; j++) {
- final RouteInfo innerRoute = group.getRouteAt(j);
- mSortRouteList.add(innerRoute);
- }
- } else {
- mSortRouteList.add(route);
- }
- }
- // Sort by name. This will keep the route positions relatively stable even though they
- // will be repeatedly added and removed.
- Collections.sort(mSortRouteList, mComparator);
-
- mItems.addAll(mSortRouteList);
- mSortRouteList.clear();
-
- mItems.add(null); // Sentinel reserving space for the "done" button.
- }
-
- @Override
- public int getCount() {
- return mItems.size();
- }
-
- @Override
- public int getViewTypeCount() {
- return 5;
- }
-
- @Override
- public int getItemViewType(int position) {
- final Object item = getItem(position);
- if (item instanceof RouteCategory) {
- return position == 0 ? VIEW_TOP_HEADER : VIEW_SECTION_HEADER;
- } else if (item == null) {
- return VIEW_GROUPING_DONE;
- } else {
- final RouteInfo info = (RouteInfo) item;
- if (info.getCategory() == mCategoryEditingGroups) {
- return VIEW_GROUPING_ROUTE;
- }
- return VIEW_ROUTE;
- }
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return false;
- }
-
- @Override
- public boolean isEnabled(int position) {
- switch (getItemViewType(position)) {
- case VIEW_ROUTE:
- return ((RouteInfo) mItems.get(position)).isEnabled();
- case VIEW_GROUPING_ROUTE:
- case VIEW_GROUPING_DONE:
- return true;
- default:
- return false;
- }
- }
-
- @Override
- public Object getItem(int position) {
- return mItems.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final int viewType = getItemViewType(position);
-
- ViewHolder holder;
- if (convertView == null) {
- convertView = mInflater.inflate(ITEM_LAYOUTS[viewType], parent, false);
- holder = new ViewHolder();
- holder.position = position;
- holder.text1 = (TextView) convertView.findViewById(R.id.text1);
- holder.text2 = (TextView) convertView.findViewById(R.id.text2);
- holder.icon = (ImageView) convertView.findViewById(R.id.icon);
- holder.check = (CheckBox) convertView.findViewById(R.id.check);
- holder.expandGroupButton = (ImageButton) convertView.findViewById(
- R.id.expand_button);
- if (holder.expandGroupButton != null) {
- holder.expandGroupListener = new ExpandGroupListener();
- holder.expandGroupButton.setOnClickListener(holder.expandGroupListener);
- }
-
- final View fview = convertView;
- final ListView list = (ListView) parent;
- final ViewHolder fholder = holder;
- convertView.setOnClickListener(new View.OnClickListener() {
- @Override public void onClick(View v) {
- list.performItemClick(fview, fholder.position, 0);
- }
- });
- convertView.setTag(holder);
- } else {
- holder = (ViewHolder) convertView.getTag();
- holder.position = position;
- }
-
- switch (viewType) {
- case VIEW_ROUTE:
- case VIEW_GROUPING_ROUTE:
- bindItemView(position, holder);
- break;
- case VIEW_SECTION_HEADER:
- case VIEW_TOP_HEADER:
- bindHeaderView(position, holder);
- break;
- }
-
- convertView.setActivated(position == mSelectedItemPosition);
- convertView.setEnabled(isEnabled(position));
-
- return convertView;
- }
-
- void bindItemView(int position, ViewHolder holder) {
- RouteInfo info = (RouteInfo) mItems.get(position);
- holder.text1.setText(info.getName(getActivity()));
- final CharSequence status = info.getStatus();
- if (TextUtils.isEmpty(status)) {
- holder.text2.setVisibility(View.GONE);
- } else {
- holder.text2.setVisibility(View.VISIBLE);
- holder.text2.setText(status);
- }
- Drawable icon = info.getIconDrawable();
- if (icon != null) {
- // Make sure we have a fresh drawable where it doesn't matter if we mutate it
- icon = icon.getConstantState().newDrawable(getResources());
- }
- holder.icon.setImageDrawable(icon);
- holder.icon.setVisibility(icon != null ? View.VISIBLE : View.GONE);
-
- RouteCategory cat = info.getCategory();
- boolean canGroup = false;
- if (cat == mCategoryEditingGroups) {
- RouteGroup group = info.getGroup();
- holder.check.setEnabled(group.getRouteCount() > 1);
- holder.check.setChecked(group == mEditingGroup);
- } else {
- if (cat.isGroupable()) {
- final RouteGroup group = (RouteGroup) info;
- canGroup = group.getRouteCount() > 1 ||
- getItemViewType(position - 1) == VIEW_ROUTE ||
- (position < getCount() - 1 &&
- getItemViewType(position + 1) == VIEW_ROUTE);
- }
- }
-
- if (holder.expandGroupButton != null) {
- holder.expandGroupButton.setVisibility(canGroup ? View.VISIBLE : View.GONE);
- holder.expandGroupListener.position = position;
- }
- }
-
- void bindHeaderView(int position, ViewHolder holder) {
- RouteCategory cat = (RouteCategory) mItems.get(position);
- holder.text1.setText(cat.getName(getActivity()));
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- final int type = getItemViewType(position);
- if (type == VIEW_SECTION_HEADER || type == VIEW_TOP_HEADER) {
- return;
- } else if (type == VIEW_GROUPING_DONE) {
- finishGrouping();
- return;
- } else {
- final Object item = getItem(position);
- if (!(item instanceof RouteInfo)) {
- // Oops. Stale event running around? Skip it.
- return;
- }
-
- final RouteInfo route = (RouteInfo) item;
- if (type == VIEW_ROUTE) {
- mRouter.selectRouteInt(mRouteTypes, route);
- dismiss();
- } else if (type == VIEW_GROUPING_ROUTE) {
- final Checkable c = (Checkable) view;
- final boolean wasChecked = c.isChecked();
-
- mIgnoreUpdates = true;
- RouteGroup oldGroup = route.getGroup();
- if (!wasChecked && oldGroup != mEditingGroup) {
- // Assumption: in a groupable category oldGroup will never be null.
- if (mRouter.getSelectedRoute(mRouteTypes) == oldGroup) {
- // Old group was selected but is now empty. Select the group
- // we're manipulating since that's where the last route went.
- mRouter.selectRouteInt(mRouteTypes, mEditingGroup);
- }
- oldGroup.removeRoute(route);
- mEditingGroup.addRoute(route);
- c.setChecked(true);
- } else if (wasChecked && mEditingGroup.getRouteCount() > 1) {
- mEditingGroup.removeRoute(route);
-
- // In a groupable category this will add
- // the route into its own new group.
- mRouter.addRouteInt(route);
- }
- mIgnoreUpdates = false;
- update();
- }
- }
- }
-
- boolean isGrouping() {
- return mCategoryEditingGroups != null;
- }
-
- void finishGrouping() {
- mCategoryEditingGroups = null;
- mEditingGroup = null;
- getDialog().setCanceledOnTouchOutside(true);
- update();
- scrollToSelectedItem();
- }
-
- class ExpandGroupListener implements View.OnClickListener {
- int position;
-
- @Override
- public void onClick(View v) {
- // Assumption: this is only available for the user to click if we're presenting
- // a groupable category, where every top-level route in the category is a group.
- final RouteGroup group = (RouteGroup) getItem(position);
- mEditingGroup = group;
- mCategoryEditingGroups = group.getCategory();
- getDialog().setCanceledOnTouchOutside(false);
- mRouter.selectRouteInt(mRouteTypes, mEditingGroup);
- update();
- scrollToEditingGroup();
+ MediaRouteChooserDialog dialog = (MediaRouteChooserDialog)getDialog();
+ if (dialog != null) {
+ dialog.setRouteTypes(types);
}
}
}
- class MediaRouterCallback extends MediaRouter.Callback {
- @Override
- public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
- mAdapter.update();
- updateVolume();
- }
+ public void setExtendedSettingsClickListener(View.OnClickListener listener) {
+ if (listener != mExtendedSettingsClickListener) {
+ mExtendedSettingsClickListener = listener;
- @Override
- public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
- mAdapter.update();
- }
-
- @Override
- public void onRouteAdded(MediaRouter router, RouteInfo info) {
- mAdapter.update();
- }
-
- @Override
- public void onRouteRemoved(MediaRouter router, RouteInfo info) {
- if (info == mAdapter.mEditingGroup) {
- mAdapter.finishGrouping();
- }
- mAdapter.update();
- }
-
- @Override
- public void onRouteChanged(MediaRouter router, RouteInfo info) {
- mAdapter.notifyDataSetChanged();
- }
-
- @Override
- public void onRouteGrouped(MediaRouter router, RouteInfo info,
- RouteGroup group, int index) {
- mAdapter.update();
- }
-
- @Override
- public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
- mAdapter.update();
- }
-
- @Override
- public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) {
- if (!mIgnoreCallbackVolumeChanges) {
- updateVolume();
- }
- }
- }
-
- class RouteComparator implements Comparator<RouteInfo> {
- @Override
- public int compare(RouteInfo lhs, RouteInfo rhs) {
- return lhs.getName(getActivity()).toString()
- .compareTo(rhs.getName(getActivity()).toString());
- }
- }
-
- class RouteChooserDialog extends Dialog {
- public RouteChooserDialog(Context context, int theme) {
- super(context, theme);
- }
-
- @Override
- public void onBackPressed() {
- if (mAdapter != null && mAdapter.isGrouping()) {
- mAdapter.finishGrouping();
- } else {
- super.onBackPressed();
- }
- }
-
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) {
- final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
- if (selectedRoute != null) {
- selectedRoute.requestUpdateVolume(-1);
- return true;
- }
- } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) {
- final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
- if (selectedRoute != null) {
- mRouter.getSelectedRoute(mRouteTypes).requestUpdateVolume(1);
- return true;
- }
- }
- return super.onKeyDown(keyCode, event);
- }
-
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) {
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) {
- return true;
- } else {
- return super.onKeyUp(keyCode, event);
+ MediaRouteChooserDialog dialog = (MediaRouteChooserDialog)getDialog();
+ if (dialog != null) {
+ dialog.setExtendedSettingsClickListener(listener);
}
}
}
/**
- * Implemented by the MediaRouteButton that launched this dialog
+ * Called when the chooser dialog is being created.
+ * <p>
+ * Subclasses may override this method to customize the dialog.
+ * </p>
*/
- public interface LauncherListener {
- public void onDetached(MediaRouteChooserDialogFragment detachedFragment);
+ public MediaRouteChooserDialog onCreateChooserDialog(
+ Context context, Bundle savedInstanceState) {
+ return new MediaRouteChooserDialog(context, getTheme());
}
- class VolumeSliderChangeListener implements SeekBar.OnSeekBarChangeListener {
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- changeVolume(progress);
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- mIgnoreCallbackVolumeChanges = true;
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- mIgnoreCallbackVolumeChanges = false;
- updateVolume();
- }
-
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ MediaRouteChooserDialog dialog = onCreateChooserDialog(getActivity(), savedInstanceState);
+ dialog.setRouteTypes(getRouteTypes());
+ dialog.setExtendedSettingsClickListener(mExtendedSettingsClickListener);
+ return dialog;
}
}
diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialog.java b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
new file mode 100644
index 0000000..8fc99c7
--- /dev/null
+++ b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import com.android.internal.R;
+
+import android.app.Dialog;
+import android.app.MediaRouteActionProvider;
+import android.app.MediaRouteButton;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.media.MediaRouter;
+import android.media.MediaRouter.RouteGroup;
+import android.media.MediaRouter.RouteInfo;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+
+/**
+ * This class implements the route controller dialog for {@link MediaRouter}.
+ * <p>
+ * This dialog allows the user to control or disconnect from the currently selected route.
+ * </p>
+ *
+ * @see MediaRouteButton
+ * @see MediaRouteActionProvider
+ *
+ * TODO: Move this back into the API, as in the support library media router.
+ */
+public class MediaRouteControllerDialog extends Dialog {
+ // Time to wait before updating the volume when the user lets go of the seek bar
+ // to allow the route provider time to propagate the change and publish a new
+ // route descriptor.
+ private static final int VOLUME_UPDATE_DELAY_MILLIS = 250;
+
+ private final MediaRouter mRouter;
+ private final MediaRouterCallback mCallback;
+ private final MediaRouter.RouteInfo mRoute;
+
+ private boolean mCreated;
+ private Drawable mMediaRouteConnectingDrawable;
+ private Drawable mMediaRouteOnDrawable;
+ private Drawable mCurrentIconDrawable;
+
+ private boolean mVolumeControlEnabled = true;
+ private LinearLayout mVolumeLayout;
+ private SeekBar mVolumeSlider;
+ private boolean mVolumeSliderTouched;
+
+ private View mControlView;
+
+ private Button mDisconnectButton;
+
+ public MediaRouteControllerDialog(Context context, int theme) {
+ super(context, theme);
+
+ mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+ mCallback = new MediaRouterCallback();
+ mRoute = mRouter.getSelectedRoute();
+ }
+
+ /**
+ * Gets the route that this dialog is controlling.
+ */
+ public MediaRouter.RouteInfo getRoute() {
+ return mRoute;
+ }
+
+ /**
+ * Provides the subclass an opportunity to create a view that will
+ * be included within the body of the dialog to offer additional media controls
+ * for the currently playing content.
+ *
+ * @param savedInstanceState The dialog's saved instance state.
+ * @return The media control view, or null if none.
+ */
+ public View onCreateMediaControlView(Bundle savedInstanceState) {
+ return null;
+ }
+
+ /**
+ * Gets the media control view that was created by {@link #onCreateMediaControlView(Bundle)}.
+ *
+ * @return The media control view, or null if none.
+ */
+ public View getMediaControlView() {
+ return mControlView;
+ }
+
+ /**
+ * Sets whether to enable the volume slider and volume control using the volume keys
+ * when the route supports it.
+ * <p>
+ * The default value is true.
+ * </p>
+ */
+ public void setVolumeControlEnabled(boolean enable) {
+ if (mVolumeControlEnabled != enable) {
+ mVolumeControlEnabled = enable;
+ if (mCreated) {
+ updateVolume();
+ }
+ }
+ }
+
+ /**
+ * Returns whether to enable the volume slider and volume control using the volume keys
+ * when the route supports it.
+ */
+ public boolean isVolumeControlEnabled() {
+ return mVolumeControlEnabled;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().requestFeature(Window.FEATURE_LEFT_ICON);
+
+ setContentView(R.layout.media_route_controller_dialog);
+
+ mVolumeLayout = (LinearLayout)findViewById(R.id.media_route_volume_layout);
+ mVolumeSlider = (SeekBar)findViewById(R.id.media_route_volume_slider);
+ mVolumeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ private final Runnable mStopTrackingTouch = new Runnable() {
+ @Override
+ public void run() {
+ if (mVolumeSliderTouched) {
+ mVolumeSliderTouched = false;
+ updateVolume();
+ }
+ }
+ };
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ if (mVolumeSliderTouched) {
+ mVolumeSlider.removeCallbacks(mStopTrackingTouch);
+ } else {
+ mVolumeSliderTouched = true;
+ }
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ // Defer resetting mVolumeSliderTouched to allow the media route provider
+ // a little time to settle into its new state and publish the final
+ // volume update.
+ mVolumeSlider.postDelayed(mStopTrackingTouch, VOLUME_UPDATE_DELAY_MILLIS);
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (fromUser) {
+ mRoute.requestSetVolume(progress);
+ }
+ }
+ });
+
+ mDisconnectButton = (Button)findViewById(R.id.media_route_disconnect_button);
+ mDisconnectButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mRoute.isSelected()) {
+ mRouter.getDefaultRoute().select();
+ }
+ dismiss();
+ }
+ });
+
+ mCreated = true;
+ if (update()) {
+ mControlView = onCreateMediaControlView(savedInstanceState);
+ FrameLayout controlFrame =
+ (FrameLayout)findViewById(R.id.media_route_control_frame);
+ if (mControlView != null) {
+ controlFrame.addView(mControlView);
+ controlFrame.setVisibility(View.VISIBLE);
+ } else {
+ controlFrame.setVisibility(View.GONE);
+ }
+ }
+ }
+
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mRouter.addCallback(0, mCallback, MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS);
+ update();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ mRouter.removeCallback(mCallback);
+
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+ || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
+ mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1);
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+ || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ private boolean update() {
+ if (!mRoute.isSelected() || mRoute.isDefault()) {
+ dismiss();
+ return false;
+ }
+
+ setTitle(mRoute.getName());
+ updateVolume();
+
+ Drawable icon = getIconDrawable();
+ if (icon != mCurrentIconDrawable) {
+ mCurrentIconDrawable = icon;
+ getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, icon);
+ }
+ return true;
+ }
+
+ private Drawable getIconDrawable() {
+ if (mRoute.isConnecting()) {
+ if (mMediaRouteConnectingDrawable == null) {
+ mMediaRouteConnectingDrawable = getContext().getResources().getDrawable(
+ R.drawable.ic_media_route_connecting_holo_dark);
+ }
+ return mMediaRouteConnectingDrawable;
+ } else {
+ if (mMediaRouteOnDrawable == null) {
+ mMediaRouteOnDrawable = getContext().getResources().getDrawable(
+ R.drawable.ic_media_route_on_holo_dark);
+ }
+ return mMediaRouteOnDrawable;
+ }
+ }
+
+ private void updateVolume() {
+ if (!mVolumeSliderTouched) {
+ if (isVolumeControlAvailable()) {
+ mVolumeLayout.setVisibility(View.VISIBLE);
+ mVolumeSlider.setMax(mRoute.getVolumeMax());
+ mVolumeSlider.setProgress(mRoute.getVolume());
+ } else {
+ mVolumeLayout.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private boolean isVolumeControlAvailable() {
+ return mVolumeControlEnabled && mRoute.getVolumeHandling() ==
+ MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
+ }
+
+ private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
+ @Override
+ public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
+ update();
+ }
+
+ @Override
+ public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
+ update();
+ }
+
+ @Override
+ public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) {
+ if (route == mRoute) {
+ updateVolume();
+ }
+ }
+
+ @Override
+ public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
+ int index) {
+ update();
+ }
+
+ @Override
+ public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
+ update();
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialogFragment.java b/core/java/com/android/internal/app/MediaRouteControllerDialogFragment.java
new file mode 100644
index 0000000..108e81f
--- /dev/null
+++ b/core/java/com/android/internal/app/MediaRouteControllerDialogFragment.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.os.Bundle;
+
+/**
+ * Media route controller dialog fragment.
+ * <p>
+ * Creates a {@link MediaRouteControllerDialog}. The application may subclass
+ * this dialog fragment to customize the media route controller dialog.
+ * </p>
+ *
+ * TODO: Move this back into the API, as in the support library media router.
+ */
+public class MediaRouteControllerDialogFragment extends DialogFragment {
+ /**
+ * Creates a media route controller dialog fragment.
+ * <p>
+ * All subclasses of this class must also possess a default constructor.
+ * </p>
+ */
+ public MediaRouteControllerDialogFragment() {
+ setCancelable(true);
+ setStyle(STYLE_NORMAL, android.R.style.Theme_DeviceDefault_Dialog);
+ }
+
+ /**
+ * Called when the controller dialog is being created.
+ * <p>
+ * Subclasses may override this method to customize the dialog.
+ * </p>
+ */
+ public MediaRouteControllerDialog onCreateControllerDialog(
+ Context context, Bundle savedInstanceState) {
+ return new MediaRouteControllerDialog(context, getTheme());
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return onCreateControllerDialog(getActivity(), savedInstanceState);
+ }
+}
diff --git a/core/java/com/android/internal/app/MediaRouteDialogPresenter.java b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java
new file mode 100644
index 0000000..fad7fd4
--- /dev/null
+++ b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.Context;
+import android.media.MediaRouter;
+import android.util.Log;
+import android.view.View;
+
+/**
+ * Shows media route dialog as appropriate.
+ * @hide
+ */
+public abstract class MediaRouteDialogPresenter {
+ private static final String TAG = "MediaRouter";
+
+ private static final String CHOOSER_FRAGMENT_TAG =
+ "android.app.MediaRouteButton:MediaRouteChooserDialogFragment";
+ private static final String CONTROLLER_FRAGMENT_TAG =
+ "android.app.MediaRouteButton:MediaRouteControllerDialogFragment";
+
+ public static DialogFragment showDialogFragment(Activity activity,
+ int routeTypes, View.OnClickListener extendedSettingsClickListener) {
+ final MediaRouter router = (MediaRouter)activity.getSystemService(
+ Context.MEDIA_ROUTER_SERVICE);
+ final FragmentManager fm = activity.getFragmentManager();
+
+ MediaRouter.RouteInfo route = router.getSelectedRoute();
+ if (route.isDefault() || !route.matchesTypes(routeTypes)) {
+ if (fm.findFragmentByTag(CHOOSER_FRAGMENT_TAG) != null) {
+ Log.w(TAG, "showDialog(): Route chooser dialog already showing!");
+ return null;
+ }
+ MediaRouteChooserDialogFragment f = new MediaRouteChooserDialogFragment();
+ f.setRouteTypes(routeTypes);
+ f.setExtendedSettingsClickListener(extendedSettingsClickListener);
+ f.show(fm, CHOOSER_FRAGMENT_TAG);
+ return f;
+ } else {
+ if (fm.findFragmentByTag(CONTROLLER_FRAGMENT_TAG) != null) {
+ Log.w(TAG, "showDialog(): Route controller dialog already showing!");
+ return null;
+ }
+ MediaRouteControllerDialogFragment f = new MediaRouteControllerDialogFragment();
+ f.show(fm, CONTROLLER_FRAGMENT_TAG);
+ return f;
+ }
+ }
+
+ public static Dialog createDialog(Context context,
+ int routeTypes, View.OnClickListener extendedSettingsClickListener) {
+ final MediaRouter router = (MediaRouter)context.getSystemService(
+ Context.MEDIA_ROUTER_SERVICE);
+
+ MediaRouter.RouteInfo route = router.getSelectedRoute();
+ if (route.isDefault() || !route.matchesTypes(routeTypes)) {
+ final MediaRouteChooserDialog d = new MediaRouteChooserDialog(
+ context, android.R.style.Theme_DeviceDefault_Dialog);
+ d.setRouteTypes(routeTypes);
+ d.setExtendedSettingsClickListener(extendedSettingsClickListener);
+ return d;
+ } else {
+ MediaRouteControllerDialog d = new MediaRouteControllerDialog(
+ context, android.R.style.Theme_DeviceDefault_Dialog);
+ return d;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index 20b8c95..0cad33c 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -1758,21 +1758,34 @@
mStartTime, now);
ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
boolean printedHeader = false;
+ boolean sepNeeded = false;
for (int ip=0; ip<pkgMap.size(); ip++) {
- String pkgName = pkgMap.keyAt(ip);
- if (reqPackage != null && !reqPackage.equals(pkgName)) {
- continue;
- }
- SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ final String pkgName = pkgMap.keyAt(ip);
+ final SparseArray<PackageState> uids = pkgMap.valueAt(ip);
for (int iu=0; iu<uids.size(); iu++) {
- int uid = uids.keyAt(iu);
- PackageState pkgState = uids.valueAt(iu);
+ final int uid = uids.keyAt(iu);
+ final PackageState pkgState = uids.valueAt(iu);
final int NPROCS = pkgState.mProcesses.size();
final int NSRVS = pkgState.mServices.size();
+ final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
+ if (!pkgMatch) {
+ boolean procMatch = false;
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (reqPackage.equals(proc.mName)) {
+ procMatch = true;
+ break;
+ }
+ }
+ if (!procMatch) {
+ continue;
+ }
+ }
if (NPROCS > 0 || NSRVS > 0) {
if (!printedHeader) {
pw.println("Per-Package Stats:");
printedHeader = true;
+ sepNeeded = true;
}
pw.print(" * "); pw.print(pkgName); pw.print(" / ");
UserHandle.formatUid(pw, uid); pw.println(":");
@@ -1780,6 +1793,9 @@
if (!dumpSummary || dumpAll) {
for (int iproc=0; iproc<NPROCS; iproc++) {
ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (!pkgMatch && !reqPackage.equals(proc.mName)) {
+ continue;
+ }
if (activeOnly && !proc.isInUse()) {
pw.print(" (Not active: ");
pw.print(pkgState.mProcesses.keyAt(iproc)); pw.println(")");
@@ -1787,7 +1803,11 @@
}
pw.print(" Process ");
pw.print(pkgState.mProcesses.keyAt(iproc));
- pw.print(" (");
+ if (proc.mCommonProcess.mMultiPackage) {
+ pw.print(" (multi, ");
+ } else {
+ pw.print(" (unique, ");
+ }
pw.print(proc.mDurationsTableSize);
pw.print(" entries)");
pw.println(":");
@@ -1801,6 +1821,9 @@
ArrayList<ProcessState> procs = new ArrayList<ProcessState>();
for (int iproc=0; iproc<NPROCS; iproc++) {
ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (!pkgMatch && !reqPackage.equals(proc.mName)) {
+ continue;
+ }
if (activeOnly && !proc.isInUse()) {
continue;
}
@@ -1811,6 +1834,9 @@
}
for (int isvc=0; isvc<NSRVS; isvc++) {
ServiceState svc = pkgState.mServices.valueAt(isvc);
+ if (!pkgMatch && !reqPackage.equals(svc.mProcessName)) {
+ continue;
+ }
if (activeOnly && !svc.isInUse()) {
pw.print(" (Not active: ");
pw.print(pkgState.mServices.keyAt(isvc)); pw.println(")");
@@ -1840,64 +1866,73 @@
if (svc.mOwner != null) {
pw.print(" mOwner="); pw.println(svc.mOwner);
}
+ if (svc.mStarted || svc.mRestarting) {
+ pw.print(" mStarted="); pw.print(svc.mStarted);
+ pw.print(" mRestarting="); pw.println(svc.mRestarting);
+ }
}
}
}
}
- if (reqPackage == null) {
- ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
- printedHeader = false;
- int numShownProcs = 0, numTotalProcs = 0;
- for (int ip=0; ip<procMap.size(); ip++) {
- String procName = procMap.keyAt(ip);
- SparseArray<ProcessState> uids = procMap.valueAt(ip);
- for (int iu=0; iu<uids.size(); iu++) {
- int uid = uids.keyAt(iu);
- numTotalProcs++;
- ProcessState proc = uids.valueAt(iu);
- if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING
- && proc.mPssTableSize == 0) {
- continue;
- }
- numShownProcs++;
- if (!printedHeader) {
- pw.println();
- pw.println("Per-Process Stats:");
- printedHeader = true;
- }
- if (activeOnly && !proc.isInUse()) {
- pw.print(" (Not active: "); pw.print(procName); pw.println(")");
- continue;
- }
- pw.print(" * "); pw.print(procName); pw.print(" / ");
- UserHandle.formatUid(pw, uid);
- pw.print(" ("); pw.print(proc.mDurationsTableSize);
- pw.print(" entries)"); pw.println(":");
- dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES, now);
- dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES);
- if (dumpAll) {
- dumpProcessInternalLocked(pw, " ", proc, dumpAll);
- }
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ printedHeader = false;
+ int numShownProcs = 0, numTotalProcs = 0;
+ for (int ip=0; ip<procMap.size(); ip++) {
+ String procName = procMap.keyAt(ip);
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ int uid = uids.keyAt(iu);
+ numTotalProcs++;
+ ProcessState proc = uids.valueAt(iu);
+ if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING
+ && proc.mPssTableSize == 0) {
+ continue;
}
+ if (!proc.mMultiPackage) {
+ continue;
+ }
+ if (reqPackage != null && !reqPackage.equals(procName)
+ && !reqPackage.equals(proc.mPackage)) {
+ continue;
+ }
+ numShownProcs++;
+ if (sepNeeded) {
+ pw.println();
+ }
+ sepNeeded = true;
+ if (!printedHeader) {
+ pw.println("Multi-Package Common Processes:");
+ printedHeader = true;
+ }
+ if (activeOnly && !proc.isInUse()) {
+ pw.print(" (Not active: "); pw.print(procName); pw.println(")");
+ continue;
+ }
+ pw.print(" * "); pw.print(procName); pw.print(" / ");
+ UserHandle.formatUid(pw, uid);
+ pw.print(" ("); pw.print(proc.mDurationsTableSize);
+ pw.print(" entries)"); pw.println(":");
+ dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES, now);
+ dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES);
+ dumpProcessInternalLocked(pw, " ", proc, dumpAll);
}
- if (dumpAll) {
- pw.println();
- pw.print(" Total procs: "); pw.print(numShownProcs);
- pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total");
- }
+ }
+ if (dumpAll) {
+ pw.println();
+ pw.print(" Total procs: "); pw.print(numShownProcs);
+ pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total");
+ }
+ if (sepNeeded) {
pw.println();
- if (dumpSummary) {
- pw.println("Summary:");
- dumpSummaryLocked(pw, reqPackage, now, activeOnly);
- } else {
- dumpTotalsLocked(pw, now);
- }
+ }
+ if (dumpSummary) {
+ pw.println("Summary:");
+ dumpSummaryLocked(pw, reqPackage, now, activeOnly);
} else {
- pw.println();
dumpTotalsLocked(pw, now);
}
@@ -2031,17 +2066,20 @@
public ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates,
int[] procStates, int sortProcStates[], long now, String reqPackage,
boolean activeOnly) {
- ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>();
- ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ final ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>();
+ final ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
for (int ip=0; ip<pkgMap.size(); ip++) {
- if (reqPackage != null && !reqPackage.equals(pkgMap.keyAt(ip))) {
- continue;
- }
- SparseArray<PackageState> procs = pkgMap.valueAt(ip);
+ final String pkgName = pkgMap.keyAt(ip);
+ final SparseArray<PackageState> procs = pkgMap.valueAt(ip);
for (int iu=0; iu<procs.size(); iu++) {
- PackageState state = procs.valueAt(iu);
- for (int iproc=0; iproc<state.mProcesses.size(); iproc++) {
- ProcessState proc = state.mProcesses.valueAt(iproc);
+ final PackageState state = procs.valueAt(iu);
+ final int NPROCS = state.mProcesses.size();
+ final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ final ProcessState proc = state.mProcesses.valueAt(iproc);
+ if (!pkgMatch && !reqPackage.equals(proc.mName)) {
+ continue;
+ }
if (activeOnly && !proc.isInUse()) {
continue;
}
@@ -2601,23 +2639,35 @@
}
}
- void incStartedServices(int memFactor, long now) {
+ void incStartedServices(int memFactor, long now, String serviceName) {
+ if (false) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.d(TAG, "incStartedServices: " + this + " service=" + serviceName
+ + " to " + (mNumStartedServices+1), here);
+ }
if (mCommonProcess != this) {
- mCommonProcess.incStartedServices(memFactor, now);
+ mCommonProcess.incStartedServices(memFactor, now, serviceName);
}
mNumStartedServices++;
if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) {
- setState(STATE_NOTHING, memFactor, now, null);
+ setState(STATE_SERVICE_RESTARTING + (memFactor*STATE_COUNT), now);
}
}
- void decStartedServices(int memFactor, long now) {
+ void decStartedServices(int memFactor, long now, String serviceName) {
+ if (false) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.d(TAG, "decActiveServices: " + this + " service=" + serviceName
+ + " to " + (mNumStartedServices-1), here);
+ }
if (mCommonProcess != this) {
- mCommonProcess.decStartedServices(memFactor, now);
+ mCommonProcess.decStartedServices(memFactor, now, serviceName);
}
mNumStartedServices--;
- if (mNumStartedServices == 0 && mCurState == STATE_SERVICE_RESTARTING) {
- setState(STATE_NOTHING, memFactor, now, null);
+ if (mNumStartedServices == 0 && (mCurState%STATE_COUNT) == STATE_SERVICE_RESTARTING) {
+ setState(STATE_NOTHING, now);
} else if (mNumStartedServices < 0) {
Slog.wtfStack(TAG, "Proc started services underrun: pkg="
+ mPackage + " uid=" + mUid + " name=" + mName);
@@ -2873,6 +2923,8 @@
public int mRunState = STATE_NOTHING;
long mRunStartTime;
+ boolean mStarted;
+ boolean mRestarting;
int mStartedCount;
public int mStartedState = STATE_NOTHING;
long mStartedStartTime;
@@ -2902,10 +2954,9 @@
// There was already an old owner, reset this object for its
// new owner.
mOwner = newOwner;
- if (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING
- || mExecState != STATE_NOTHING) {
+ if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) {
long now = SystemClock.uptimeMillis();
- if (mStartedState != STATE_NOTHING) {
+ if (mStarted) {
if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
+ " from " + mOwner + " while started: pkg="
+ mPackage + " service=" + mName + " proc=" + mProc);
@@ -2931,10 +2982,9 @@
public void clearCurrentOwner(Object owner, boolean silently) {
if (mOwner == owner) {
mProc.decActiveServices(mName);
- if (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING
- || mExecState != STATE_NOTHING) {
+ if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) {
long now = SystemClock.uptimeMillis();
- if (mStartedState != STATE_NOTHING) {
+ if (mStarted) {
if (!silently) {
Slog.wtfStack(TAG, "Service owner " + owner
+ " cleared while started: pkg=" + mPackage + " service="
@@ -3042,7 +3092,18 @@
if (mOwner == null) {
Slog.wtf(TAG, "Starting service " + this + " without owner");
}
+ mStarted = started;
+ updateStartedState(memFactor, now);
+ }
+
+ public void setRestarting(boolean restarting, int memFactor, long now) {
+ mRestarting = restarting;
+ updateStartedState(memFactor, now);
+ }
+
+ void updateStartedState(int memFactor, long now) {
final boolean wasStarted = mStartedState != STATE_NOTHING;
+ final boolean started = mStarted || mRestarting;
final int state = started ? memFactor : STATE_NOTHING;
if (mStartedState != state) {
if (mStartedState != STATE_NOTHING) {
@@ -3056,9 +3117,9 @@
mProc = mProc.pullFixedProc(mPackage);
if (wasStarted != started) {
if (started) {
- mProc.incStartedServices(memFactor, now);
+ mProc.incStartedServices(memFactor, now, mName);
} else {
- mProc.decStartedServices(memFactor, now);
+ mProc.decStartedServices(memFactor, now, mName);
}
}
updateRunning(memFactor, now);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index a99aa33..0a702ff 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -84,7 +84,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 66 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 67 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -154,6 +154,8 @@
final ArrayList<StopwatchTimer> mFullWifiLockTimers = new ArrayList<StopwatchTimer>();
final ArrayList<StopwatchTimer> mWifiMulticastTimers = new ArrayList<StopwatchTimer>();
final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<StopwatchTimer>();
+ final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers =
+ new SparseArray<ArrayList<StopwatchTimer>>();
// Last partial timers we use for distributing CPU usage.
final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<StopwatchTimer>();
@@ -2404,6 +2406,14 @@
getUidStatsLocked(uid).noteWifiScanStoppedLocked();
}
+ public void noteWifiBatchedScanStartedLocked(int uid, int csph) {
+ getUidStatsLocked(uid).noteWifiBatchedScanStartedLocked(csph);
+ }
+
+ public void noteWifiBatchedScanStoppedLocked(int uid) {
+ getUidStatsLocked(uid).noteWifiBatchedScanStoppedLocked();
+ }
+
int mWifiMulticastNesting = 0;
public void noteWifiMulticastEnabledLocked(int uid) {
@@ -2456,6 +2466,20 @@
}
}
+ public void noteWifiBatchedScanStartedFromSourceLocked(WorkSource ws, int csph) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteWifiBatchedScanStartedLocked(ws.get(i), csph);
+ }
+ }
+
+ public void noteWifiBatchedScanStoppedFromSourceLocked(WorkSource ws) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteWifiBatchedScanStoppedLocked(ws.get(i));
+ }
+ }
+
public void noteWifiMulticastEnabledFromSourceLocked(WorkSource ws) {
int N = ws.size();
for (int i=0; i<N; i++) {
@@ -2579,6 +2603,10 @@
boolean mWifiScanStarted;
StopwatchTimer mWifiScanTimer;
+ private static final int NO_BATCHED_SCAN_STARTED = -1;
+ int mWifiBatchedScanBinStarted = NO_BATCHED_SCAN_STARTED;
+ StopwatchTimer[] mWifiBatchedScanTimer;
+
boolean mWifiMulticastEnabled;
StopwatchTimer mWifiMulticastTimer;
@@ -2629,6 +2657,7 @@
mFullWifiLockTimers, mUnpluggables);
mWifiScanTimer = new StopwatchTimer(Uid.this, WIFI_SCAN,
mWifiScanTimers, mUnpluggables);
+ mWifiBatchedScanTimer = new StopwatchTimer[NUM_WIFI_BATCHED_SCAN_BINS];
mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
mWifiMulticastTimers, mUnpluggables);
}
@@ -2719,6 +2748,36 @@
}
@Override
+ public void noteWifiBatchedScanStartedLocked(int csph) {
+ int bin = 0;
+ while (csph > 8 && bin < NUM_WIFI_BATCHED_SCAN_BINS) {
+ csph = csph >> 3;
+ bin++;
+ }
+
+ if (mWifiBatchedScanBinStarted == bin) return;
+
+ if (mWifiBatchedScanBinStarted != NO_BATCHED_SCAN_STARTED) {
+ mWifiBatchedScanTimer[mWifiBatchedScanBinStarted].
+ stopRunningLocked(BatteryStatsImpl.this);
+ }
+ mWifiBatchedScanBinStarted = bin;
+ if (mWifiBatchedScanTimer[bin] == null) {
+ makeWifiBatchedScanBin(bin, null);
+ }
+ mWifiBatchedScanTimer[bin].startRunningLocked(BatteryStatsImpl.this);
+ }
+
+ @Override
+ public void noteWifiBatchedScanStoppedLocked() {
+ if (mWifiBatchedScanBinStarted != NO_BATCHED_SCAN_STARTED) {
+ mWifiBatchedScanTimer[mWifiBatchedScanBinStarted].
+ stopRunningLocked(BatteryStatsImpl.this);
+ mWifiBatchedScanBinStarted = NO_BATCHED_SCAN_STARTED;
+ }
+ }
+
+ @Override
public void noteWifiMulticastEnabledLocked() {
if (!mWifiMulticastEnabled) {
mWifiMulticastEnabled = true;
@@ -2854,6 +2913,15 @@
}
@Override
+ public long getWifiBatchedScanTime(int csphBin, long batteryRealtime, int which) {
+ if (csphBin < 0 || csphBin >= NUM_WIFI_BATCHED_SCAN_BINS) return 0;
+ if (mWifiBatchedScanTimer[csphBin] == null) {
+ return 0;
+ }
+ return mWifiBatchedScanTimer[csphBin].getTotalTimeLocked(batteryRealtime, which);
+ }
+
+ @Override
public long getWifiMulticastTime(long batteryRealtime, int which) {
if (mWifiMulticastTimer == null) {
return 0;
@@ -2914,6 +2982,24 @@
return mUserActivityCounters[type].getCountLocked(which);
}
+ void makeWifiBatchedScanBin(int i, Parcel in) {
+ if (i < 0 || i >= NUM_WIFI_BATCHED_SCAN_BINS) return;
+
+ ArrayList<StopwatchTimer> collected = mWifiBatchedScanTimers.get(i);
+ if (collected == null) {
+ collected = new ArrayList<StopwatchTimer>();
+ mWifiBatchedScanTimers.put(i, collected);
+ }
+ if (in == null) {
+ mWifiBatchedScanTimer[i] = new StopwatchTimer(this, WIFI_BATCHED_SCAN, collected,
+ mUnpluggables);
+ } else {
+ mWifiBatchedScanTimer[i] = new StopwatchTimer(this, WIFI_BATCHED_SCAN, collected,
+ mUnpluggables, in);
+ }
+ }
+
+
void initUserActivityLocked() {
mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES];
for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
@@ -2974,6 +3060,14 @@
active |= !mWifiScanTimer.reset(BatteryStatsImpl.this, false);
active |= mWifiScanStarted;
}
+ if (mWifiBatchedScanTimer != null) {
+ for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) {
+ if (mWifiBatchedScanTimer[i] != null) {
+ active |= !mWifiBatchedScanTimer[i].reset(BatteryStatsImpl.this, false);
+ }
+ }
+ active |= (mWifiBatchedScanBinStarted != NO_BATCHED_SCAN_STARTED);
+ }
if (mWifiMulticastTimer != null) {
active |= !mWifiMulticastTimer.reset(BatteryStatsImpl.this, false);
active |= mWifiMulticastEnabled;
@@ -3080,6 +3174,11 @@
if (mWifiScanTimer != null) {
mWifiScanTimer.detach();
}
+ for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) {
+ if (mWifiBatchedScanTimer[i] != null) {
+ mWifiBatchedScanTimer[i].detach();
+ }
+ }
if (mWifiMulticastTimer != null) {
mWifiMulticastTimer.detach();
}
@@ -3157,6 +3256,14 @@
} else {
out.writeInt(0);
}
+ for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) {
+ if (mWifiBatchedScanTimer[i] != null) {
+ out.writeInt(1);
+ mWifiBatchedScanTimer[i].writeToParcel(out, batteryRealtime);
+ } else {
+ out.writeInt(0);
+ }
+ }
if (mWifiMulticastTimer != null) {
out.writeInt(1);
mWifiMulticastTimer.writeToParcel(out, batteryRealtime);
@@ -3266,6 +3373,14 @@
} else {
mWifiScanTimer = null;
}
+ mWifiBatchedScanBinStarted = NO_BATCHED_SCAN_STARTED;
+ for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) {
+ if (in.readInt() != 0) {
+ makeWifiBatchedScanBin(i, in);
+ } else {
+ mWifiBatchedScanTimer[i] = null;
+ }
+ }
mWifiMulticastEnabled = false;
if (in.readInt() != 0) {
mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
@@ -5463,6 +5578,13 @@
if (in.readInt() != 0) {
u.mWifiScanTimer.readSummaryFromParcelLocked(in);
}
+ u.mWifiBatchedScanBinStarted = Uid.NO_BATCHED_SCAN_STARTED;
+ for (int i = 0; i < Uid.NUM_WIFI_BATCHED_SCAN_BINS; i++) {
+ if (in.readInt() != 0) {
+ u.makeWifiBatchedScanBin(i, null);
+ u.mWifiBatchedScanTimer[i].readSummaryFromParcelLocked(in);
+ }
+ }
u.mWifiMulticastEnabled = false;
if (in.readInt() != 0) {
u.mWifiMulticastTimer.readSummaryFromParcelLocked(in);
@@ -5674,6 +5796,14 @@
} else {
out.writeInt(0);
}
+ for (int i = 0; i < Uid.NUM_WIFI_BATCHED_SCAN_BINS; i++) {
+ if (u.mWifiBatchedScanTimer[i] != null) {
+ out.writeInt(1);
+ u.mWifiBatchedScanTimer[i].writeSummaryFromParcelLocked(out, NOWREAL);
+ } else {
+ out.writeInt(0);
+ }
+ }
if (u.mWifiMulticastTimer != null) {
out.writeInt(1);
u.mWifiMulticastTimer.writeSummaryFromParcelLocked(out, NOWREAL);
@@ -5909,6 +6039,7 @@
mWifiRunningTimers.clear();
mFullWifiLockTimers.clear();
mWifiScanTimers.clear();
+ mWifiBatchedScanTimers.clear();
mWifiMulticastTimers.clear();
sNumSpeedSteps = in.readInt();
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 99a6843..94750d3 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -136,6 +136,13 @@
public static final String POWER_CPU_SPEEDS = "cpu.speeds";
/**
+ * Power consumed by wif batched scaning. Broken down into bins by
+ * Channels Scanned per Hour. May do 1-720 scans per hour of 1-100 channels
+ * for a range of 1-72,000. Going logrithmic (1-8, 9-64, 65-512, 513-4096, 4097-)!
+ */
+ public static final String POWER_WIFI_BATCHED_SCAN = "wifi.batchedscan";
+
+ /**
* Battery capacity in milliAmpHour (mAh).
*/
public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
@@ -171,7 +178,7 @@
String element = parser.getName();
if (element == null) break;
-
+
if (parsingArray && !element.equals(TAG_ARRAYITEM)) {
// Finish array
sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
diff --git a/core/java/com/android/internal/view/CheckableLinearLayout.java b/core/java/com/android/internal/view/CheckableLinearLayout.java
deleted file mode 100644
index 3fb7cec..0000000
--- a/core/java/com/android/internal/view/CheckableLinearLayout.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.view;
-
-import com.android.internal.R;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.Checkable;
-import android.widget.CheckBox;
-import android.widget.LinearLayout;
-
-public class CheckableLinearLayout extends LinearLayout implements Checkable {
- private CheckBox mCheckBox;
-
- public CheckableLinearLayout(Context context) {
- super(context);
- // TODO Auto-generated constructor stub
- }
-
- public CheckableLinearLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- // TODO Auto-generated constructor stub
- }
-
- public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- // TODO Auto-generated constructor stub
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mCheckBox = (CheckBox) findViewById(R.id.check);
- }
-
- @Override
- public void setChecked(boolean checked) {
- mCheckBox.setChecked(checked);
- }
-
- @Override
- public boolean isChecked() {
- return mCheckBox.isChecked();
- }
-
- @Override
- public void toggle() {
- mCheckBox.toggle();
- }
-}
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index b57a0fe..6175a8f 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -17,62 +17,138 @@
#include "jni.h"
#include "GraphicsJNI.h"
#include <android_runtime/AndroidRuntime.h>
+#include <vector>
+
+#include "CreateJavaOutputStreamAdaptor.h"
#include "SkCanvas.h"
-#include "SkPDFDevice.h"
-#include "SkPDFDocument.h"
+#include "SkDocument.h"
+#include "SkPicture.h"
+#include "SkStream.h"
#include "SkRect.h"
-#include "SkSize.h"
-#include "CreateJavaOutputStreamAdaptor.h"
-#include "JNIHelp.h"
namespace android {
-#define LOGD(x...) do { Log::Instance()->printf(Log::ELogD, x); } while(0)
+struct PageRecord {
-static jint nativeCreateDocument(JNIEnv* env, jobject clazz) {
- return reinterpret_cast<jint>(new SkPDFDocument());
+ PageRecord(int width, int height, const SkRect& contentRect)
+ : mPicture(new SkPicture()), mWidth(width), mHeight(height) {
+ mContentRect = contentRect;
+ }
+
+ ~PageRecord() {
+ mPicture->unref();
+ }
+
+ SkPicture* const mPicture;
+ const int mWidth;
+ const int mHeight;
+ SkRect mContentRect;
+};
+
+class PdfDocument {
+public:
+ PdfDocument() {
+ mCurrentPage = NULL;
+ }
+
+ SkCanvas* startPage(int width, int height,
+ int contentLeft, int contentTop, int contentRight, int contentBottom) {
+ assert(mCurrentPage == NULL);
+
+ SkRect contentRect = SkRect::MakeLTRB(
+ contentLeft, contentTop, contentRight, contentBottom);
+ PageRecord* page = new PageRecord(width, height, contentRect);
+ mPages.push_back(page);
+ mCurrentPage = page;
+
+ SkCanvas* canvas = page->mPicture->beginRecording(
+ contentRect.width(), contentRect.height(), 0);
+
+ // We pass this canvas to Java where it is used to construct
+ // a Java Canvas object which dereferences the pointer when it
+ // is destroyed, so we have to bump up the reference count.
+ canvas->ref();
+
+ return canvas;
+ }
+
+ void finishPage() {
+ assert(mCurrentPage != NULL);
+ mCurrentPage->mPicture->endRecording();
+ mCurrentPage = NULL;
+ }
+
+ void write(SkWStream* stream) {
+ SkDocument* document = SkDocument::CreatePDF(stream);
+ for (unsigned i = 0; i < mPages.size(); i++) {
+ PageRecord* page = mPages[i];
+
+ SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight,
+ &(page->mContentRect));
+
+ canvas->clipRect(page->mContentRect);
+ canvas->translate(page->mContentRect.left(), page->mContentRect.top());
+ canvas->drawPicture(*page->mPicture);
+
+ document->endPage();
+ }
+ document->close();
+ }
+
+ void close() {
+ for (unsigned i = 0; i < mPages.size(); i++) {
+ delete mPages[i];
+ }
+ delete mCurrentPage;
+ mCurrentPage = NULL;
+ }
+
+private:
+ ~PdfDocument() {
+ close();
+ }
+
+ std::vector<PageRecord*> mPages;
+ PageRecord* mCurrentPage;
+};
+
+static jint nativeCreateDocument(JNIEnv* env, jobject thiz) {
+ return reinterpret_cast<jint>(new PdfDocument());
}
-static void nativeFinalize(JNIEnv* env, jobject thiz, jint documentPtr) {
- delete reinterpret_cast<SkPDFDocument*>(documentPtr);
-}
-
-static jint nativeCreatePage(JNIEnv* env, jobject thiz, jint pageWidth, jint pageHeight,
+static jint nativeStartPage(JNIEnv* env, jobject thiz, jint documentPtr,
+ jint pageWidth, jint pageHeight,
jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) {
-
- SkMatrix transformation;
- transformation.setTranslate(contentLeft, contentTop);
-
- SkISize skPageSize = SkISize::Make(pageWidth, pageHeight);
- SkISize skContentSize = SkISize::Make(contentRight - contentLeft, contentBottom - contentTop);
-
- SkPDFDevice* skPdfDevice = new SkPDFDevice(skPageSize, skContentSize, transformation);
- return reinterpret_cast<jint>(new SkCanvas(skPdfDevice));
+ PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
+ return reinterpret_cast<jint>(document->startPage(pageWidth, pageHeight,
+ contentLeft, contentTop, contentRight, contentBottom));
}
-static void nativeAppendPage(JNIEnv* env, jobject thiz, jint documentPtr, jint pagePtr) {
- SkCanvas* page = reinterpret_cast<SkCanvas*>(pagePtr);
- SkPDFDocument* document = reinterpret_cast<SkPDFDocument*>(documentPtr);
- SkPDFDevice* device = static_cast<SkPDFDevice*>(page->getDevice());
- document->appendPage(device);
+static void nativeFinishPage(JNIEnv* env, jobject thiz, jint documentPtr) {
+ PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
+ document->finishPage();
}
-static void nativeWriteTo(JNIEnv* env, jobject clazz, jint documentPtr,
- jobject out, jbyteArray chunk) {
+static void nativeWriteTo(JNIEnv* env, jobject thiz, jint documentPtr, jobject out,
+ jbyteArray chunk) {
+ PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
SkWStream* skWStream = CreateJavaOutputStreamAdaptor(env, out, chunk);
- SkPDFDocument* document = reinterpret_cast<SkPDFDocument*>(documentPtr);
- document->emitPDF(skWStream);
+ document->write(skWStream);
delete skWStream;
}
+static void nativeClose(JNIEnv* env, jobject thiz, jint documentPtr) {
+ PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
+ document->close();
+}
+
static JNINativeMethod gPdfDocument_Methods[] = {
{"nativeCreateDocument", "()I", (void*) nativeCreateDocument},
- {"nativeFinalize", "(I)V", (void*) nativeFinalize},
- {"nativeCreatePage", "(IIIIII)I",
- (void*) nativeCreatePage},
- {"nativeAppendPage", "(II)V", (void*) nativeAppendPage},
- {"nativeWriteTo", "(ILjava/io/OutputStream;[B)V", (void*) nativeWriteTo}
+ {"nativeStartPage", "(IIIIIII)I", (void*) nativeStartPage},
+ {"nativeFinishPage", "(I)V", (void*) nativeFinishPage},
+ {"nativeWriteTo", "(ILjava/io/OutputStream;[B)V", (void*) nativeWriteTo},
+ {"nativeClose", "(I)V", (void*) nativeClose}
};
int register_android_graphics_pdf_PdfDocument(JNIEnv* env) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6ddd3fe..2c5c6b5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1992,6 +1992,14 @@
android:description="@string/permdesc_bindWallpaper"
android:protectionLevel="signature|system" />
+ <!-- Must be required by a {@link com.android.media.remotedisplay.RemoteDisplayProvider},
+ to ensure that only the system can bind to it.
+ @hide -->
+ <permission android:name="android.permission.BIND_REMOTE_DISPLAY"
+ android:label="@string/permlab_bindRemoteDisplay"
+ android:description="@string/permdesc_bindRemoteDisplay"
+ android:protectionLevel="signature" />
+
<!-- Must be required by device administration receiver, to ensure that only the
system can interact with it. -->
<permission android:name="android.permission.BIND_DEVICE_ADMIN"
diff --git a/core/res/res/drawable-hdpi/ic_media_group_collapse.png b/core/res/res/drawable-hdpi/ic_media_group_collapse.png
deleted file mode 100644
index 89abf2c..0000000
--- a/core/res/res/drawable-hdpi/ic_media_group_collapse.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_group_expand.png b/core/res/res/drawable-hdpi/ic_media_group_expand.png
deleted file mode 100644
index d9470b2..0000000
--- a/core/res/res/drawable-hdpi/ic_media_group_expand.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png
deleted file mode 100644
index 35f27df..0000000
--- a/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_group_collapse.png b/core/res/res/drawable-mdpi/ic_media_group_collapse.png
deleted file mode 100644
index 34454ac..0000000
--- a/core/res/res/drawable-mdpi/ic_media_group_collapse.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_group_expand.png b/core/res/res/drawable-mdpi/ic_media_group_expand.png
deleted file mode 100644
index 8ce5a44..0000000
--- a/core/res/res/drawable-mdpi/ic_media_group_expand.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png
deleted file mode 100644
index f9c8678..0000000
--- a/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_group_collapse.png b/core/res/res/drawable-xhdpi/ic_media_group_collapse.png
deleted file mode 100644
index 2fb7428..0000000
--- a/core/res/res/drawable-xhdpi/ic_media_group_collapse.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_group_expand.png b/core/res/res/drawable-xhdpi/ic_media_group_expand.png
deleted file mode 100644
index 5755b9d..0000000
--- a/core/res/res/drawable-xhdpi/ic_media_group_expand.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png
deleted file mode 100644
index 4cc0ee8..0000000
--- a/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-xxhdpi/ic_notify_wifidisplay.png
deleted file mode 100644
index fea4774..0000000
--- a/core/res/res/drawable-xxhdpi/ic_notify_wifidisplay.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/layout/immersive_mode_cling.xml b/core/res/res/layout/immersive_mode_cling.xml
index f97225e..c0cd93d 100644
--- a/core/res/res/layout/immersive_mode_cling.xml
+++ b/core/res/res/layout/immersive_mode_cling.xml
@@ -37,8 +37,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/cling_bg"
- android:paddingLeft="14dp"
- android:paddingRight="14dp"
+ android:paddingStart="14dp"
+ android:paddingEnd="14dp"
android:paddingTop="24dp"
android:paddingBottom="24dp">
<TextView
diff --git a/core/res/res/layout/media_route_chooser_dialog.xml b/core/res/res/layout/media_route_chooser_dialog.xml
new file mode 100644
index 0000000..d1c6267
--- /dev/null
+++ b/core/res/res/layout/media_route_chooser_dialog.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:divider="?android:attr/dividerHorizontal"
+ android:showDividers="middle">
+ <!-- List of routes. -->
+ <ListView android:id="@+id/media_route_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ <!-- Content to show when list is empty. -->
+ <LinearLayout android:id="@android:id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:orientation="horizontal"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:visibility="gone">
+ <ProgressBar android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center" />
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:paddingLeft="16dp"
+ android:text="@string/media_route_chooser_searching" />
+ </LinearLayout>
+
+ <!-- Settings button. -->
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="?attr/buttonBarStyle">
+ <Button android:id="@+id/media_route_extended_settings_button"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ style="?attr/buttonBarButtonStyle"
+ android:gravity="center"
+ android:text="@string/media_route_chooser_extended_settings"
+ android:visibility="gone" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/media_route_chooser_layout.xml b/core/res/res/layout/media_route_chooser_layout.xml
deleted file mode 100644
index 5fcb8c8..0000000
--- a/core/res/res/layout/media_route_chooser_layout.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:showDividers="middle"
- android:divider="?android:attr/dividerHorizontal">
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:gravity="center_vertical"
- android:padding="8dp">
- <ImageView android:id="@+id/volume_icon"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:src="@android:drawable/ic_audio_vol"
- android:gravity="center"
- android:scaleType="center" />
- <SeekBar android:id="@+id/volume_slider"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp" />
- <ImageButton android:id="@+id/extended_settings"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:background="?android:attr/selectableItemBackground"
- android:src="@android:drawable/ic_sysbar_quicksettings"
- android:visibility="gone" />
- </LinearLayout>
- <ListView android:id="@id/list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-</LinearLayout>
diff --git a/core/res/res/layout/media_route_controller_dialog.xml b/core/res/res/layout/media_route_controller_dialog.xml
new file mode 100644
index 0000000..78287e0
--- /dev/null
+++ b/core/res/res/layout/media_route_controller_dialog.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:divider="?android:attr/dividerHorizontal"
+ android:showDividers="middle">
+ <!-- Optional volume slider section. -->
+ <LinearLayout android:id="@+id/media_route_volume_layout"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:gravity="center_vertical"
+ android:padding="8dp"
+ android:visibility="gone">
+ <ImageView android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:src="@drawable/ic_audio_vol"
+ android:gravity="center"
+ android:scaleType="center" />
+ <SeekBar android:id="@+id/media_route_volume_slider"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp" />
+ </LinearLayout>
+
+ <!-- Optional content view section. -->
+ <FrameLayout android:id="@+id/media_route_control_frame"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
+ <!-- Disconnect button. -->
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="?attr/buttonBarStyle">
+ <Button android:id="@+id/media_route_disconnect_button"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ style="?attr/buttonBarButtonStyle"
+ android:gravity="center"
+ android:text="@string/media_route_controller_disconnect" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/media_route_list_item.xml b/core/res/res/layout/media_route_list_item.xml
index 423d544..bdca433 100644
--- a/core/res/res/layout/media_route_list_item.xml
+++ b/core/res/res/layout/media_route_list_item.xml
@@ -20,13 +20,6 @@
android:background="@drawable/item_background_activated_holo_dark"
android:gravity="center_vertical">
- <ImageView android:layout_width="56dp"
- android:layout_height="56dp"
- android:scaleType="center"
- android:id="@+id/icon"
- android:visibility="gone"
- android:duplicateParentState="true" />
-
<LinearLayout android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
@@ -53,14 +46,4 @@
android:duplicateParentState="true" />
</LinearLayout>
- <ImageButton
- android:layout_width="56dp"
- android:layout_height="56dp"
- android:id="@+id/expand_button"
- android:background="?android:attr/selectableItemBackground"
- android:src="@drawable/ic_media_group_expand"
- android:scaleType="center"
- android:visibility="gone"
- android:duplicateParentState="true" />
-
</LinearLayout>
diff --git a/core/res/res/layout/media_route_list_item_checkable.xml b/core/res/res/layout/media_route_list_item_checkable.xml
deleted file mode 100644
index 5fb82f7..0000000
--- a/core/res/res/layout/media_route_list_item_checkable.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<com.android.internal.view.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:background="?android:attr/selectableItemBackground"
- android:gravity="center_vertical">
-
- <ImageView android:layout_width="56dp"
- android:layout_height="56dp"
- android:scaleType="center"
- android:id="@+id/icon"
- android:visibility="gone" />
-
- <LinearLayout android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="vertical"
- android:gravity="start|center_vertical"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
-
- <TextView android:id="@android:id/text1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <TextView android:id="@android:id/text2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </LinearLayout>
-
- <CheckBox
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="16dp"
- android:id="@+id/check"
- android:focusable="false"
- android:clickable="false" />
-
-</com.android.internal.view.CheckableLinearLayout>
diff --git a/core/res/res/layout/media_route_list_item_collapse_group.xml b/core/res/res/layout/media_route_list_item_collapse_group.xml
deleted file mode 100644
index 323e24d..0000000
--- a/core/res/res/layout/media_route_list_item_collapse_group.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?android:attr/selectableItemBackground">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeightSmall"
- android:background="#19ffffff"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:gravity="center_vertical">
-
- <TextView android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:text="@string/media_route_chooser_grouping_done"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_media_group_collapse"
- android:scaleType="center" />
-
- </LinearLayout>
-</FrameLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/media_route_list_item_section_header.xml b/core/res/res/layout/media_route_list_item_section_header.xml
deleted file mode 100644
index 949635f..0000000
--- a/core/res/res/layout/media_route_list_item_section_header.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="16dp">
- <TextView
- android:id="@android:id/text1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:background="#19ffffff"
- android:textStyle="bold"
- android:textAllCaps="true"
- android:gravity="center_vertical"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:minHeight="24dp"
- />
-</FrameLayout>
diff --git a/core/res/res/layout/media_route_list_item_top_header.xml b/core/res/res/layout/media_route_list_item_top_header.xml
deleted file mode 100644
index 0c49b24..0000000
--- a/core/res/res/layout/media_route_list_item_top_header.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/text1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:background="#19ffffff"
- android:textStyle="bold"
- android:textAllCaps="true"
- android:gravity="center_vertical"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:minHeight="24dp"
-/>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index b27ac62..d179b7a 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -382,6 +382,8 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Dit laat die houer toe om aan die topvlak-koppelvlak van \'n VPN-diens te bind. Dit moet nooit vir normale programme nodig wees nie."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"bind aan \'n muurpapier"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Dit laat die houer toe om aan die topvlak-koppelvlak van \'n muurpapier te bind. Dit moet nooit vir normale programme nodig wees nie."</string>
+ <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"koppel aan \'n afstandskerm"</string>
+ <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Dit laat die houer toe om aan die top-koppelvlak van \'n afstandskerm te koppel. Behoort nooit vir gewone programme nodig te wees nie."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"bind aan \'n legstukdiens"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Dit laat die houer toe om aan die topvlak-koppelvlak van \'n legstuk-diens te bind. Dit moet nooit vir normale programme nodig wees nie."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"skakel met \'n toestel-admin"</string>
@@ -1489,8 +1491,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Stelsel"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-oudio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Draadlose skerm"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Klaar"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Media-uitvoer"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Skandeer tans..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Koppel tans..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Beskikbaar"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 1f9599d..fe863a7 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"የVPN ግልጋሎትን ወደ ከፍተኛ-ደረጃ በየነ ገጽ ለማሳር ለመያዣው ይፈቅዳሉ፡፡ለተለመዱ መተግበሪያዎች አያስፈልግም፡፡"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"በልጣፍ ጠርዝ"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"ያዡ ግቤት ስልቱን ወደ ከፍተኛ-ደረጃ ልጣፍ ለመጠረዝ ይፈቅዳሉ። ለመደበኛ ትግበራዎች በፍፁም አያስፈልግም።"</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ወደ ፍርግም አገልግሎት አያይዝ"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ያዡ ግቤት ስልቱን ወደ ከፍተኛ-ደረጃ ፍርግም አገልግሎት ለመጠረዝ ይፈቅዳሉ። ለመደበኛ ትግበራዎች በፍፁም አያስፈልግም።"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"ከመሣሪያ አስተዳደር ጋር ተገናኝ"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ስርዓት"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"የብሉቱዝ ድምጽ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ገመድ አልባ ማሳያ"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"ተከናውኗል"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"የሚዲያ ውጽዓት"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"በመቃኘት ላይ..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"በማገናኘት ላይ..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"የሚገኙ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index ab03375..1faa709 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"للسماح للمالك بالالتزام بواجهة المستوى العلوي لخدمة الشبكة الظاهرية الخاصة (VPN). لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"الالتزام بخلفية ما"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"للسماح للمالك بالالتزام بواجهة المستوى العلوي للخلفية. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"الالتزام بخدمة أداة"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"للسماح للمالك بالالتزام بواجهة المستوى العلوي لخدمة الأداة. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"التفاعل مع مشرف الجهاز"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"النظام"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"صوت بلوتوث"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"عرض شاشة لاسلكي"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"تم"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"المنفذ الإعلامي"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"جارٍ الفحص..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"جارٍ الاتصال..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"متاح"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 00a77a3..10a3ffc 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -396,6 +396,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Дазваляе ўладальніку звязвацца з інтэрфейсам службы VPN вышэйшага ўзроўню. Не патрэбна для звычайных прыкладанняў."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"прывязаць да шпалер"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Дазваляе ўладальніку ўсталёўваць прывязку да інтэрфейсу шпалер верхняга ўзроўню. Не патрабуецца для звычайных прыкладанняў."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"прывязаць да службы віджэту"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Дазваляе ўладальніку прывязвацца да інтэрфейсу верхняга ўзроўню службы віджэта. Не патрабуецца для звычайных прыкладанняў."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"узаемадзейнічаць з адміністратарам прылады"</string>
@@ -1536,8 +1540,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Сістэма"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-аўдыё"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Бесправадны дысплей"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Гатова"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Мультымедыйны выхад"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Сканiраванне..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Падключэнне..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Даступна"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index d16796e..0c679d1 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Разрешава на притежателя да се обвърже с интерфейса от най-високото ниво на услуга за VPN. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"обвързване с тапет"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Разрешава на притежателя да се обвърже с интерфейса от най-високото ниво на тапет. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"обвързване с услуга за приспособления"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Разрешава на притежателя да се обвърже с интерфейса от най-високото ниво на услуга за приспособления. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"взаимодействие с администратор на устройството"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Система"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Звук през Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Безжичен дисплей"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Готово"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Изходяща мултимедия"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Сканира се..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Установява се връзка..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Налице"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 65416f1..badc632 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permet que el titular vinculi a la interfície de nivell superior d\'un servei de VPN. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"enllaça amb un fons de pantalla"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permet que el titular vinculi a la interfície de nivell superior d\'un fons de pantalla. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"vincula a un servei de widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permet que el titular vinculi a la interfície de nivell superior d\'un servei de widget. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interactuar amb un administrador del dispositiu"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Àudio per Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Pantalla sense fil"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Fet"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Sortida de contingut multimèdia"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"S\'està cercant…"</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"S\'està connectant..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponible"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index f4cb4d0..aa2c0c6 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Umožňuje držiteli navázat se na nejvyšší úroveň služby VPN. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"vazba na tapetu"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Umožňuje držiteli navázat se na nejvyšší úroveň rozhraní tapety. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"navázat se na službu widgetu"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Umožňuje držiteli navázat se na nejvyšší úroveň služby widgetu. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"komunikovat se správcem zařízení"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Systém"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth Audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Bezdrátový displej"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Hotovo"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Výstup médií"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Vyhledávání…"</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Připojování…"</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Dostupná"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 4a16765..da4f0e8 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Tillader, at brugeren forpligter sig til en VPN-tjenestes grænseflade på øverste niveau. Bør aldrig være nødvendigt i almindelige apps."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"forpligt til et tapet"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Tillader, at indehaveren kan binde en baggrunds grænseflade på øverste niveau. Dette bør aldrig være nødvendigt for almindelige apps."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"forpligt til en widgettjeneste"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Tillader, at brugeren kan forpligte sig til en grænseflade for en widgettjeneste på øverste niveau. Bør aldrig være nødvendigt til almindelige apps."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"kommunikere med en enhedsadministrator"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-lyd"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Trådløs skærm"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Udfør"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Medieudgang"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Søger..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Opretter forbindelse..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Tilgængelig"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 4f40d72..0fb31d8 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Ermöglicht dem Halter, sich an die Oberfläche eines VPN-Dienstes auf oberster Ebene zu binden. Sollte nie für normale Apps benötigt werden."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"An einen Hintergrund binden"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Ermöglicht dem Halter, sich an die Oberfläche eines Hintergrunds auf oberster Ebene zu binden. Sollte nie für normale Apps benötigt werden."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"An einen Widget-Dienst binden"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Ermöglicht dem Halter, sich an die Oberfläche eines Widget-Dienstes auf oberster Ebene zu binden. Sollte nie für normale Apps benötigt werden."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"Interaktion mit einem Geräteadministrator"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-Audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Kabellose Übertragung (WiDi)"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Fertig"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Medienausgabe"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Wird gescannt..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Verbindung wird hergestellt..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Verfügbar"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 6a34a6a..7e19a481 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας υπηρεσίας Vpn. Δεν απαιτείται για κανονικές εφαρμογές."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"δέσμευση σε ταπετσαρία"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας ταπετσαρίας. Δεν απαιτείται για συνήθεις εφαρμογές."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"δέσμευση σε υπηρεσία γραφικών στοιχείων"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας υπηρεσίας γραφικών στοιχείων. Δεν απαιτείται για κανονικές εφαρμογές."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"επικοινωνία με έναν διαχειριστή συσκευής"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Σύστημα"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Ήχος Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Ασύρματη οθόνη"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Τέλος"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Έξοδος μέσων"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Σάρωση…"</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Σύνδεση…"</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Διαθέσιμη"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index b706ee8..bf9084d 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -382,6 +382,8 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Allows the holder to bind to the top-level interface of a Vpn service. Should never be needed for normal apps."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"bind to wallpaper"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Allows the holder to bind to the top-level interface of wallpaper. Should never be needed for normal applications."</string>
+ <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"bind to a remote display"</string>
+ <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Allows the holder to bind to the top-level interface of a remote display. Should never be needed for normal apps."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"bind to a widget service"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Allows the holder to bind to the top-level interface of a widget service. Should never be needed for normal apps."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interact with device admin"</string>
@@ -1489,8 +1491,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wireless display"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Done"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Media output"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Scanning..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Connecting..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Available"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index b706ee8..bf9084d 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -382,6 +382,8 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Allows the holder to bind to the top-level interface of a Vpn service. Should never be needed for normal apps."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"bind to wallpaper"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Allows the holder to bind to the top-level interface of wallpaper. Should never be needed for normal applications."</string>
+ <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"bind to a remote display"</string>
+ <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Allows the holder to bind to the top-level interface of a remote display. Should never be needed for normal apps."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"bind to a widget service"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Allows the holder to bind to the top-level interface of a widget service. Should never be needed for normal apps."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interact with device admin"</string>
@@ -1489,8 +1491,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wireless display"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Done"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Media output"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Scanning..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Connecting..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Available"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 165b6a6..859ccc6 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permite al titular vincularse a la interfaz de nivel superior de un servicio de VPN. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"vincular a un fondo de pantalla"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permite al propietario vincularse a la interfaz de nivel superior de un fondo de pantalla. Las aplicaciones normales no deben utilizar este permiso."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"vincular a un servicio de widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permite al propietario vincularse a la interfaz de nivel superior del servicio de widget. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interactuar con un administrador de dispositivos"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Pantalla inalámbrica"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Listo"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Salida multimedia"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Examinando..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Conectando..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponible"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index c38a075..aba60f0 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permite enlazar con la interfaz de nivel superior de un servicio de VPN. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"enlazar con un fondo de pantalla"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permite enlazar con la interfaz de nivel superior de un fondo de pantalla. Las aplicaciones normales no deberían necesitar este permiso."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"enlazar con un servicio de widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permite enlazar con la interfaz de nivel superior de un servicio de widget. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interactuar con el administrador de un dispositivo"</string>
@@ -788,7 +792,7 @@
<string name="imProtocolYahoo" msgid="8271439408469021273">"Yahoo!"</string>
<string name="imProtocolSkype" msgid="9019296744622832951">"Skype"</string>
<string name="imProtocolQq" msgid="8887484379494111884">"QQ"</string>
- <string name="imProtocolGoogleTalk" msgid="493902321140277304">"Conversaciones"</string>
+ <string name="imProtocolGoogleTalk" msgid="493902321140277304">"Hangouts"</string>
<string name="imProtocolIcq" msgid="1574870433606517315">"ICQ"</string>
<string name="imProtocolJabber" msgid="2279917630875771722">"Jabber"</string>
<string name="imProtocolNetMeeting" msgid="8287625655986827971">"NetMeeting"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Pantalla inalámbrica"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Fin"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Salida multimedia"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Analizando..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Conectando..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponible"</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 5f05ac2..12033a5 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Võimaldab omanikul siduda VPN-teenuse ülataseme liidesega. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"taustapildiga sidumine"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Lubab omanikul siduda taustapildi ülataseme liidesega. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"vidinateenusega sidumine"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Lubab omanikul siduda vidina teenuse ülataseme liidesega. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"seadme administraatoriga suhtlemine"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Süsteem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-heli"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Juhtmeta ekraan"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Valmis"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Meediaväljund"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Skaneering ..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Ühendan..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Saadaval"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index baa11fa..80f3315 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -382,6 +382,8 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"به دارنده اجازه میدهد که به رابط سطح بالای سرویس Vpn متصل شود. هرگز برای برنامههای معمولی مورد نیاز نیست."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"پیوند شده به تصویر زمینه"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"به دارنده اجازه میدهد تا به رابط سطح بالای تصویر زمینه متصل شود. برنامههای معمولی هرگز به این ویژگی نیاز ندارند."</string>
+ <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"اتصال به نمایشگر راه دور"</string>
+ <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"به دارنده امکان میدهد تا به رابط سطح بالای نمایشگر راه دور وصل شود. نباید هرگز برای برنامههای عادی لازم باشد."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"اتصال به یک سرویس ابزارک"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"به دارنده اجازه میدهد که به رابط سطح بالای سرویس ابزارک متصل شود. هرگز برای برنامههای معمولی مورد نیاز نیست."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"تعامل با یک سرپرست دستگاه"</string>
@@ -1489,8 +1491,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"سیستم"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"بلوتوثهای صوتی"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"صفحه نمایش بیسیم"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"انجام شد"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"خروجی رسانه"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"در حال اسکن کردن…"</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"درحال اتصال…"</string>
<string name="media_route_status_available" msgid="6983258067194649391">"در دسترس"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 3718ccc..edf8729 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Antaa sovelluksen sitoutua VPN-palvelun ylemmän tason käyttöliittymään. Ei tavallisten sovellusten käyttöön."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"sido taustakuvaan"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Antaa sovelluksen sitoutua taustakuvan ylätason käyttöliittymään. Ei tavallisten sovellusten käyttöön."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"sitoudu widget-palveluun"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Antaa sovelluksen sitoutua widget-palvelun ylemmän tason käyttöliittymään. Ei tavallisten sovelluksien käyttöön."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"kommunikoi laitteen järjestelmänvalvojan kanssa"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Järjestelmä"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-ääni"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Langaton näyttö"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Valmis"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Median äänentoisto"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Etsitään..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Yhdistetään..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Käytettävissä"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index bbfaad7..196bb5f 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un service RPV. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"se fixer à un fond d\'écran"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un fond d\'écran. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"s\'associer à un service de widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un service de widget. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interagir avec l\'administrateur d\'un périphérique"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Système"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Affichage sans fil"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Terminé"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Sortie multimédia"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Analyse en cours..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Connexion en cours..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponible"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index a6d7687..e91c523 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un service VPN. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"Se fixer sur un fond d\'écran"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un fond d\'écran. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"associer à un service widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un service widget. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interagir avec l\'administrateur du périphérique"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Système"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Affichage sans fil"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"OK"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Sortie multimédia"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Analyse en cours..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Connexion en cours..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponible"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 59ac55c..12a09be 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"धारक को किसी Vpn सेवा के शीर्ष-स्तर इंटरफ़ेस से आबद्ध होने देता है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"वॉलपेपर से आबद्ध करें"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"धारक को किसी वॉलपेपर के शीर्ष-स्तर इंटरफ़ेस से आबद्ध होने देता है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"किसी विजेट सेवा से आबद्ध करें"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"धारक को किसी विजेट सेवा के शीर्ष-स्तर इंटरफ़ेस से आबद्ध होने देता है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"किसी उपकरण व्यवस्थापक के साथ सहभागिता करें"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"सिस्टम"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth ऑडियो"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"वायरलेस प्रदर्शन"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"पूर्ण"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"मीडिया आउटपुट"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"स्कैन कर रहा है..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"कनेक्ट हो रहा है..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"उपलब्ध"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 2b04e02..3525b8c 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Nositelju omogućuje vezanje uz sučelje najviše razine VPN usluge. Ne bi smjelo biti potrebno za normalne aplikacije."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"povezano s pozadinskom slikom"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Nositelju omogućuje povezivanje sa sučeljem pozadinske slike najviše razine. Ne bi smjelo biti potrebno za normalne aplikacije."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"vezanje na uslugu widgeta"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Nositelju omogućuje vezanje uz sučelje najviše razine usluge widgeta. Ne bi smjelo biti potrebno za normalne aplikacije."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interakcija s administratorom uređaja"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sustav"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth zvuk"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Bežični prikaz"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Gotovo"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Medijski izlaz"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Skeniranje..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Povezivanje..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Dostupno"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 71f6bdc..a74efe6 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Lehetővé teszi a használó számára, hogy csatlakozzon egy VPN-szolgáltatás legfelső szintű kezelőfelületéhez. A normál alkalmazásoknak erre soha nincs szüksége."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"összekapcsolás háttérképpel"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Lehetővé teszi, hogy a tulajdonos kötelezővé tegye egy háttérkép legfelső szintű felületét. A normál alkalmazásoknak erre soha nincs szüksége."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"csatlakozás modulszolgáltatáshoz"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Lehetővé teszi a használó számára, hogy csatlakozzon egy modulszolgáltatás legfelső szintű kezelőfelületéhez. A normál alkalmazásoknak erre soha nincs szüksége."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"az eszközkezelő használata"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Rendszer"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth hang"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Vezeték nélküli kijelző"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Kész"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Médiakimenet"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Keresés..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Kapcsolódás..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Elérhető"</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index f5ed539..91ab870 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -382,6 +382,8 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Թույլ է տալիս սեփականատիրոջը միանալ Vpn ծառայության վերին մակարդակի ինտերֆեյսին: Սովորական հավելվածների համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"միանալ պաստառին"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Թույլ է տալիս սեփականատիրոջը միանալ պաստառի վերին մակարդակի ինտերֆեյսին: Սովորական հավելվածների համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
+ <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"միանալ հեռակա էկրանին"</string>
+ <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Թույլ է տալիս սեփականատիրոջը միանալ հեռակա էկրանի վերին մակարդակի ինտերֆեյսին: Սովորական ծրագրերի համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"միանալ վիջեթ ծառայությանը"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Թույլ է տալիս սեփականատիրոջը միանալ վիջեթ ծառայության վերին մակարդակի ինտերֆեյսին: Սովորական հավելվածների համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"փոխգործակցել սարքի կառավարչի հետ"</string>
@@ -1489,8 +1491,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Համակարգ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-ի ձայնանյութ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Անլար էկրան"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Կատարված է"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Մեդիա արտածում"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Սկանավորում..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Միանում է..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Հասանելի է"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index d6d91fb..d8f41e9 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Mengizinkan pemegang mengikat antarmuka tingkat tinggi dari suatu layanan Vpn. Tidak pernah diperlukan oleh apl normal."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"mengikat ke wallpaper"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Mengizinkan pemegang mengikat antarmuka tingkat tinggi dari suatu wallpaper. Tidak pernah diperlukan oleh apl normal."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"mengikat ke layanan widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Mengizinkan pemegang mengikat antarmuka tingkat tinggi dari suatu layanan widget. Tidak pernah diperlukan oleh apl normal."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"berinteraksi dengan admin perangkat"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Layar nirkabel"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Selesai"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Keluaran media"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Memindai..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Menyambung..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Tersedia"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 88d69de..1a87c24 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Consente l\'associazione all\'interfaccia principale di un servizio VPN. Non dovrebbe mai essere necessario per le normali applicazioni."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"associazione a sfondo"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Consente l\'associazione di uno sfondo all\'interfaccia principale. Non dovrebbe mai essere necessaria per le normali applicazioni."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"associazione a un servizio widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Consente l\'associazione all\'interfaccia principale di un servizio widget. Non dovrebbe mai essere necessario per le normali applicazioni."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interazione con un amministratore dispositivo"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Visualizzazione wireless"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Fine"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Uscita media"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Ricerca in corso..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Connessione..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponibile"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 8dcdac5..8616220 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -382,6 +382,8 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"מאפשר למשתמש לבצע איגוד לממשק ברמה עליונה של שירות VPN. הרשאה זו לעולם אינה נחוצה לאפליקציות רגילים."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"קשור לטפט"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"מאפשר למשתמש לבצע איגוד לממשק הרמה העליונה של טפט. הרשאה זו לעולם אינה נחוצה לאפליקציות רגילים."</string>
+ <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"איגוד לצג מרוחק"</string>
+ <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"הרשאה זו מאפשרת למשתמש לבצע איגוד לממשק הרמה העליונה של צג רחוק. לעולם אינה אמורה להיות נחוצה לאפליקציות רגילות."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"הכפפה לשירות Widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"מאפשר למשתמש לבצע איגוד לממשק הרמה העליונה של שירות Widget. הרשאה זו לעולם אינה נחוצה לאפליקציות רגילים."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"קיים אינטראקציה עם מנהל המכשיר"</string>
@@ -1489,8 +1491,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"מערכת"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"אודיו Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"צג אלחוטי"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"סיום"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"פלט מדיה"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"סורק..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"מתחבר..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"זמין"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 0dc0a30..8587e149 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"VPNサービスのトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"壁紙にバインド"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"壁紙のトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ウィジェットサービスにバインド"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ウィジェットサービスのトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"デバイス管理者との通信"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"システム"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth音声"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ワイヤレスディスプレイ"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"完了"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"メディア出力"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"スキャン中..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"接続中..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"利用できます"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index efc85ba..09f19f4 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -382,6 +382,8 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"აპს შეეძლება Vpn სერვისის ზედა დონის ინტერფეისთან დაკავშირება. არასდროს გამოიყენება ჩვეულებრივ აპებში."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"ფონზე მიჭედება"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"მფლობელს შეეძლება ფონის ზედა დონის ინტერფეისთან დაკავშირება. არასდროს გამოიყენება ჩვეულებრივ აპებში."</string>
+ <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"დისტანციურ მონიტორზე მიბმა"</string>
+ <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"მფლობელს შეეძლება მიებას დისტანციურ მონიტორის ზედა დონის ინტერფეისს. ჩვეულებრივ აპს ეს წესით არასოდეს არ უნდა დაჭირდეს."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ვიჯეტ სერვისთან დაკავშირება"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"აპს შეეძლება ზედა დონის ინტერფეისის ვიჯეტთან დაკავშირება. არასდროს გამოიყენება ჩვეულებრივ აპებში."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"მოწყობილობის ადმინთან ინტერაქცია"</string>
@@ -1489,8 +1491,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"სისტემა"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth აუდიო"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"უსადენო ეკრანი"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"დასრულდა"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"მედია გამომავალი"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"სკანირება..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"დაკავშირება..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"ხელმისაწვდომი"</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 1014da4..3138efd 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្ម Vpn ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"ចងទៅផ្ទាំងរូបភាព"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃផ្ទាំងរូបភាព។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចងសេវាកម្មធាតុក្រាហ្វិក"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មធាតុក្រាហ្វិក។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"ទាក់ទងជាមួយអ្នកគ្រប់គ្រងឧបករណ៍"</string>
@@ -788,7 +792,7 @@
<string name="imProtocolYahoo" msgid="8271439408469021273">"Yahoo"</string>
<string name="imProtocolSkype" msgid="9019296744622832951">"Skype"</string>
<string name="imProtocolQq" msgid="8887484379494111884">"QQ"</string>
- <string name="imProtocolGoogleTalk" msgid="493902321140277304">"ការជជែក"</string>
+ <string name="imProtocolGoogleTalk" msgid="493902321140277304">"Hangouts"</string>
<string name="imProtocolIcq" msgid="1574870433606517315">"ICQ"</string>
<string name="imProtocolJabber" msgid="2279917630875771722">"Jabber"</string>
<string name="imProtocolNetMeeting" msgid="8287625655986827971">"NetMeeting"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ប្រព័ន្ធ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"សំឡេងប៊្លូធូស"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"បង្ហាញបណ្ដាញឥតខ្សែ"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"រួចរាល់"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"លទ្ធផលមេឌៀ"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"កំពុងវិភាគរក…"</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"កំពុងភ្ជាប់…"</string>
<string name="media_route_status_available" msgid="6983258067194649391">"ទំនេរ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 2240069..c4ac669 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"권한을 가진 프로그램이 VPN 서비스에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"배경화면 연결"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"권한을 가진 프로그램이 배경화면에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"위젯 서비스와 연결"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"권한을 가진 프로그램이 위젯 서비스에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"기기 관리자와 상호 작용"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"시스템"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"블루투스 오디오"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"무선 디스플레이"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"완료"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"미디어 출력"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"검색 중..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"연결 중..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"사용 가능"</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index b6de941..5531e88 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"ອະນຸຍາດໃຫ້ເຈົ້າຂອງເຊື່ອມໂຍງກັບສ່ວນຕິດຕໍ່ລະດັບເທິງສຸດ ຂອງບໍລິການ VPN. ແອັບຯທົ່ວໄປບໍ່ຄວນຈຳເປັນຕ້ອງໃຊ້."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"ເຊື່ອມໂຍງກັບພາບພື້ນຫຼັງ"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"ອະນຸຍາດໃຫ້ຜູ່ໃຊ້ເຊື່ອມໂຍງກັບສ່ວນຕິດຕໍ່ລະດັບສູງສຸດ ຂອງພາບພື້ນຫຼັງໃດນຶ່ງ. ແອັບຯທຳມະດາບໍ່ຄວນຈຳເປັນຕ້ອງໃຊ້."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ເຊື່ອມໂຍງໄປຫາບໍລິການວິດເຈັດ"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ອະນຸຍາດໃຫ້ຜູ່ຖືຜູກກັບອິນເຕີເຟດລະດັບສູງສຸດ ຂອງບໍລິການວິເຈັດ. ບໍ່ຈຳເປັນສຳລັບແອັບຯທົ່ວໄປ."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"ຕິດຕໍ່ກັບຜູ່ເບິ່ງແຍງອຸປະກອນ"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ລະບົບ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ສຽງ Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ການສະແດງຜົນໄຮ້ສາຍ"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"ແລ້ວໆ"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"ມີເດຍເອົ້າພຸດ"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"ກຳລັງສະແກນ..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"ກຳລັງເຊື່ອມຕໍ່..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"ສາມາດໃຊ້ໄດ້"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index c32bd9d..fc50d7b 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Leidžiama savininkui susisaistyti su aukščiausio lygio VPN paslaugos sąsaja. Įprastoms programoms to neturėtų prireikti."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"susaistyti su darbalaukio fonu"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Leidžiama savininką susaistyti su aukščiausio lygio darbalaukio fono sąsaja. Įprastoms programoms to neturėtų prireikti."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"susaistyti su valdiklio paslauga"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Leidžiama savininkui susisaistyti su aukščiausio lygio valdiklio paslaugos sąsaja. Įprastoms programoms to neturėtų prireikti."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"sąveikauti su įrenginio administratoriumi"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"„Bluetooth“ garsas"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Belaidis rodymas"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Atlikta"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Medijos išvestis"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Nuskaitoma..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Jungiama..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Pasiekiama"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 66a5d73..48aaec8 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Ļauj īpašniekam izveidot saiti ar VPN pakalpojuma augšējā līmeņa saskarni. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"saistīt ar tapeti"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Ļauj īpašniekam piesaistīt tapetes augstākā līmeņa lietotāja saskarni. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"saistīt ar logrīka pakalpojumu"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Ļauj īpašniekam izveidot saiti ar logrīka pakalpojuma augšējā līmeņa saskarni. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"mijiedarboties ar ierīces administratoru"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistēma"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Bezvadu attēlošana"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Gatavs"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Multivides izeja"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Notiek meklēšana..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Notiek savienojuma izveide..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Pieejams"</string>
diff --git a/core/res/res/values-mcc310-mnc260/config.xml b/core/res/res/values-mcc310-mnc260/config.xml
index 886ecbe..d602c9f 100644
--- a/core/res/res/values-mcc310-mnc260/config.xml
+++ b/core/res/res/values-mcc310-mnc260/config.xml
@@ -21,22 +21,6 @@
for different hardware and product builds. -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
- <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
- <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
- <integer-array translatable="false" name="config_tether_upstream_types">
- <item>1</item>
- <item>4</item>
- <item>7</item>
- <item>9</item>
- </integer-array>
-
- <!-- String containing the apn value for tethering. May be overriden by secure settings
- TETHER_DUN_APN. Value is a comma separated series of strings:
- "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
- note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
- <string translatable="false" name="config_tether_apndata">T-Mobile Tethering,pcweb.tmobile.com,,,,,,,,,310,260,,DUN</string>
-
<!-- Configure mobile network MTU. Carrier specific value is set here.
-->
<integer name="config_mobile_mtu">1440</integer>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 6ce299d..84bb9d5 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -382,6 +382,8 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Эзэмшигч нь VPN үйлчилгээний дээд-төвшиний интерфейстэй холбох боломжтой. Энгийн апп-д шаардлагагүй."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"ханын зурагтай холбох"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Эзэмшигч нь ханын зурагны дээд-төвшиний интерфейстэй холбох боломжтой. Энгийн апп-уудад шаардлагагүй."</string>
+ <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"алсын дэлгэцтэй холбогдох"</string>
+ <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Эзэмшигчид алсын дэлгэц дэх дээд давхаргын интерфэйстэй холбогдох боломж олгоно. Энгийн апп-д шаардагдахгүй."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"виджет үйлчилгээтэй холбох"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Эзэмшигч нь виджет үйлчилгээний дээд-төвшиний интерфейстэй холбох боломжтой. Энгийн апп-д шаардлагагүй."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"төхөөрөмжийн админтай харилцан үйлчлэх"</string>
@@ -788,7 +790,7 @@
<string name="imProtocolYahoo" msgid="8271439408469021273">"Yahoo"</string>
<string name="imProtocolSkype" msgid="9019296744622832951">"Skype"</string>
<string name="imProtocolQq" msgid="8887484379494111884">"QQ"</string>
- <string name="imProtocolGoogleTalk" msgid="493902321140277304">"Цугларалт"</string>
+ <string name="imProtocolGoogleTalk" msgid="493902321140277304">"Hangouts"</string>
<string name="imProtocolIcq" msgid="1574870433606517315">"ICQ"</string>
<string name="imProtocolJabber" msgid="2279917630875771722">"Jabber"</string>
<string name="imProtocolNetMeeting" msgid="8287625655986827971">"NetMeeting"</string>
@@ -1489,8 +1491,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Систем"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Блютүүт аудио"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Утасгүй дэлгэц"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Дууссан"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Медиа гаралт"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Скан хийж байна..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Холбогдож байна..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Боломжтой"</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 8203921..fe3bfa6 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi perkhidmatan Vpn. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"terikat pada kertas dinding"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi kertas dinding. Tidak sekali-kali diperlukan untuk apl biasa."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"terikat kepada perkhidmatan widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi perkhidmatan widget. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"berinteraksi dengan pentadbir peranti"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Paparan wayarles"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Selesai"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Output media"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Mengimbas…"</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Menyambung..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Tersedia"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 8aed6f1..b496ae5 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Lar innehaveren binde seg til det øverste nivået av grensesnittet for en VPN-tjeneste. Skal aldri være nødvendig for vanlige apper."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"binde til bakgrunnsbilde"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Lar innehaveren binde det øverste nivået av grensesnittet til en bakgrunn. Skal aldri være nødvendig for vanlige apper."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"binde til modultjenste"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Lar innehaveren binde seg til det øverste nivået av grensesnittet for en modultjeneste. Skal aldri være nødvendig for vanlige apper."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"kommunisere med enhetsadministrator"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-lyd"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Trådløs skjerm"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Fullført"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Medieutgang"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Skanner ..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Kobler til ..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Tilgjengelig"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 5838aef..1417b2c 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Staat de houder toe verbinding te maken met de hoofdinterface van een VPN-service. Nooit vereist voor normale apps."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"verbinden met een achtergrond"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Hiermee wordt de houder toegestaan zich te verbinden met de hoofdinterface van een achtergrond. Nooit vereist voor normale apps."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"verbinden met een widgetservice"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Hiermee wordt de houder toegestaan verbinding te maken met de hoofdinterface van een widgetservice. Nooit vereist voor normale apps."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interactie met apparaatbeheer"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Systeem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Draadloze weergave"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Gereed"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Media-uitvoer"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Scannen..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Verbinden..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Beschikbaar"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 847f9cc..5d2baaf 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Pozwala na tworzenie powiązania z interfejsem najwyższego poziomu usługi VPN. Nie powinno być nigdy potrzebne w przypadku zwykłych aplikacji."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"powiązanie z tapetą"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Pozwala na tworzenie powiązania z interfejsem najwyższego poziomu tapety. Nieprzeznaczone dla zwykłych aplikacji."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"powiązanie z usługą widżetów"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Zezwala na tworzenie powiązania z interfejsem najwyższego poziomu usługi widżetów. Nie powinno być nigdy potrzebne w przypadku zwykłych aplikacji."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interakcja z administratorem urządzenia"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Dźwięk Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wyświetlacz bezprzewodowy"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Gotowe"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Wyjście multimediów"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Skanuję..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Łączę..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Dostępne"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 2aa5e9a..dbbf93c 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permite que o titular se vincule à interface de nível superior de um serviço de VPN. Nunca deverá ser necessário para aplicações normais."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"vincular a uma imagem de fundo"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permite ao titular vincular-se à interface de nível superior de uma imagem de fundo. Nunca deverá ser necessário para aplicações normais."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"vincular a um serviço de widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permite que o titular vincule a interface de nível superior de um serviço de widget. Nunca deverá ser necessário para aplicações normais."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interagir com um administrador do dispositivo"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Áudio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Visualização sem fios"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Concluído"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Saída de som multimédia"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"A procurar..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"A ligar..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponível"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index b13044f..b0446e3 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permite que seu proprietário sujeite a interface de alto nível de um serviço de VPN. Nunca deve ser necessário para aplicativos normais."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"sujeitar-se a um plano de fundo"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permite que o proprietário utilize interface de nível superior de um plano de fundo. Nunca deve ser necessário para aplicativos normais."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"sujeitar-se a um serviço de widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permite que o proprietário utilize a interface de nível superior de um serviço de widget. Nunca deve ser necessário para aplicativos normais."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interagir com o administrador de um dispositivo"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Áudio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Display sem fio"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Concluído"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Saída de mídia"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Verificando..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Conectando..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponível"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index 5c56441..f33eed2 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -598,6 +598,10 @@
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"sa fixar vid in fund davos"</string>
<!-- no translation found for permdesc_bindWallpaper (7108428692595491668) -->
<skip />
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<!-- no translation found for permlab_bindRemoteViews (5697987759897367099) -->
<skip />
<!-- no translation found for permdesc_bindRemoteViews (4717987810137692572) -->
@@ -2411,10 +2415,16 @@
<skip />
<!-- no translation found for wireless_display_route_description (9070346425023979651) -->
<skip />
- <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
- <skip />
<!-- no translation found for media_route_button_content_description (5758553567065145276) -->
<skip />
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<!-- no translation found for media_route_status_scanning (7279908761758293783) -->
<skip />
<!-- no translation found for media_route_status_connecting (6422571716007825440) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index f29a7e6..f850b5a 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permite proprietarului să se conecteze la interfaţa de nivel superior a unui serviciu VPN. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"conectare la o imagine de fundal"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permite proprietarului să se conecteze la interfaţa de nivel superior a unei imagini de fundal. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"conectare la un serviciu widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permite proprietarului să se conecteze la interfaţa de nivel superior a unui serviciu widget. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interacţionare cu administratorul unui dispozitiv"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Ecran wireless"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Terminat"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Rezultate media"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Se scanează..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Se conectează..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Disponibilă"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 44db6b6..582b06c 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Приложение сможет подключаться к базовому интерфейсу службы VPN. Это разрешение не используется обычными приложениями."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"Привязка к фоновому рисунку"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Приложение сможет подключаться к базовому интерфейсу службы обоев. Это разрешение не используется обычными приложениями."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"Подключение к службе виджетов"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Приложение сможет подключаться к базовому интерфейсу службы виджетов. Это разрешение не используется обычными приложениями."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"Взаимодействие с администратором устройства"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Система"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Воспроизведение звука через Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Беспроводной монитор"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Готово"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Перенаправлять поток мультимедиа"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Сканирование..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Подключение..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Доступен"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index a4e57fb..838a53b 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Umožňuje držiteľovi viazať sa na najvyššiu úroveň rozhrania služby VPN. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"väzba na tapetu"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Umožňuje držiteľovi viazať sa na najvyššiu úroveň rozhrania tapety. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"viazať sa k službe miniaplikácie"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Umožňuje držiteľovi viazať sa na najvyššiu úroveň rozhrania služby miniaplikácií. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"komunikovať so správcom zariadenia"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Systém"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Bezdrôtový displej"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Hotovo"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Výstup médií"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Prebieha vyhľadávanie..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Prebieha pripájanie…"</string>
<string name="media_route_status_available" msgid="6983258067194649391">"K dispozícii"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index c364397..f4ffdab 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Lastniku omogoča povezovanje z vmesnikom storitve navideznega zasebnega omrežja najvišje ravni. Ne uporabljajte za navadne programe."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"povezovanje z ozadjem"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Imetniku omogoča povezavo z vmesnikom ozadja najvišje ravni. Tega nikoli ni treba uporabiti za navadne programe."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"poveži s storitvijo pripomočka"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Lastniku omogoča povezovanje z vmesnikom storitve pripomočka najvišje ravni. Tega ni treba nikoli uporabiti za navadne programe."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interakcija s skrbnikom naprave"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Zvok prek Bluetootha"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Brezžični prikaz"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Končano"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Izhod predstavnosti"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Pregledovanje ..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Vzpostavljanje povezave ..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Na voljo"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 605cde5..20913b0 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Дозвољава власнику да се повеже са интерфејсом VPN услуге највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"обавезивање на позадину"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Дозвољава власнику да се повеже са интерфејсом позадине највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"обавезивање на услугу виџета"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Дозвољава власнику да се обавеже на интерфејс услуге виџета највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"интеракција са администратором уређаја"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Систем"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth аудио"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Бежични екран"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Готово"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Излаз медија"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Скенирање..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Повезивање..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Доступна"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 86eaa11..e8fbaa7 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en VPN-tjänst. Ska inte behövas för vanliga appar."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"binda till en bakgrund"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Innehavaren kan binda till den översta nivåns gränssnitt för en bakgrund. Ska inte behövas för vanliga appar."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"bind till en widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en widget. Ska inte behövas för vanliga appar."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"arbeta med en enhetsadministratör"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-ljud"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Trådlös skärm"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Klar"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Medieuppspelning"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Skannar…"</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Ansluter ..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Tillgängliga"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index aa4b279..213e14a 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Inaruhusu kishikiliaji kushurutisha kusano ya kiwango cha juu cha huduma ya Vpn. Haipaswi kuhitajika kwa programu za kawaida."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"funga kwa mandhari"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Inaruhusu kishikiliaji kushurutisha kwa kusano ya kiwango cha juu cha mandhari. Haipaswi kamwe kuhitajika kwa programu za kawaida."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"funga kwenye huduma ya widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Inaruhusu mmiliki kushurutisha kusano ya kiwango cha juu ya huduma ya wijeti. Haipaswi kuhitajika kwa programu za kawaida."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"jiunge na msimamizi wa kifaa"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Mfumo"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Sauti ya Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Uonyeshaji usiotumia waya"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Nimemaliza"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Towe la midia"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Inatambaza..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Inaunganisha..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Inapatikana"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 4622005..c6c6d5b 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -382,6 +382,8 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"อนุญาตให้เจ้าของเชื่อมโยงกับส่วนติดต่อผู้ใช้ระดับสูงสุดของบริการ VPN ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"เชื่อมโยงกับวอลเปเปอร์"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"อนุญาตให้ผู้ใช้เชื่อมโยงกับส่วนติดต่อผู้ใช้ระดับสูงสุดของวอลเปเปอร์ ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
+ <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ผูกพันกับจอแสดงผลระยะไกล"</string>
+ <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"อนุญาตให้ผู้ใช้ผูกพันกับอินเทอร์เฟซระดับสูงสุดของจอแสดงผลระยะไกล ซึ่งแอปพลิเคชันทั่วไปไม่จำเป็นต้องใช้"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"เชื่อมโยงกับบริการวิดเจ็ต"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"อนุญาตให้ผู้ใช้เชื่อมโยงกับส่วนติดต่อผู้ใช้ระดับสูงสุดของบริการวิดเจ็ต ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"ติดต่อกับผู้ดูแลอุปกรณ์"</string>
@@ -1489,8 +1491,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ระบบ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"เสียงบลูทูธ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"การแสดงผลแบบไร้สาย"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"เสร็จสิ้น"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"เอาต์พุตสื่อ"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"กำลังสแกน..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"กำลังเชื่อมต่อ..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"พร้อมใช้งาน"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 897a111..5f8f18de 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Pinapayagan ang may-hawak na sumailalim sa nangungunang interface ng serbisyo ng Vpn. Hindi kailanman dapat na kailanganin para sa normal na apps."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"sumailalim sa wallpaper"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Pinapayagan ang may-hawak na sumailalim sa nangungunang interface ng isang wallpaper. Hindi kailanman dapat na kailanganin para sa normal na apps."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"itali sa serbisyo ng widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Pinapayagan ang may-hawak na sumailalim sa nangungunang interface ng serbisyo ng widget. Hindi kailanman dapat na kailanganin para sa normal na apps."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"makipag-ugnay sa tagapangasiwa ng device"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio sa Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wireless display"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Tapos na"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Output ng media"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Nagsa-scan..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Kumukonekta..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Available"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index ffb6ca1..b6e1e55 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Cihazın sahibine bir VPN hizmetinin en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"bir duvar kağıdına tabi kıl"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Cihazın sahibine, duvar kağıdının en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"bir widget hizmetine bağla"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Cihazın sahibine bir widget hizmetinin en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"bir cihaz yöneticisi ile etkileşimde bulun"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth ses"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Kablosuz ekran"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Tamamlandı"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Medya çıkışı"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Taranıyor..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Bağlanılıyor..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Kullanılabilir"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index f6bc23d..8de6ca8 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Дозволяє власникові прив’язуватися до інтерфейсу верхнього рівня служби VPN. Ніколи не застосовується для звичайних програм."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"прив’язати до фонового малюнка"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Дозволяє власнику прив’язуватися до інтерфейсу верхнього рівня фонового малюнка. Ніколи не застосовується для звичайних програм."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"прив\'язувати до служби віджетів"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Дозволяє власникові прив’язуватися до інтерфейсу верхнього рівня служби віджетів. Ніколи не застосовується для звичайних програм."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"взаємодіяти з адмін. пристрою"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Система"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Аудіо Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Бездротовий екран"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Готово"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Вивід медіа-даних"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Сканування..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"З’єднання..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Доступно"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index ec68f3a..9c64d45 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của dịch vụ Vpn. Không cần thiết cho các ứng dụng thông thường."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"liên kết với hình nền"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của hình nền. Không cần thiết cho các ứng dụng thông thường."</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"liên kết với dịch vụ tiện ích con"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của dịch vụ tiện ích con. Không cần thiết cho các ứng dụng thông thường."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"tương tác với quản trị viên thiết bị"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Hệ thống"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Âm thanh Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Hiển thị không dây"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Xong"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Đầu ra phương tiện"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Đang quét..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Đang kết nối..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Khả dụng"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 180accb..1bf1e50 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"允许用户绑定到 VPN 服务的顶级接口。普通应用绝不需要此权限。"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"绑定到壁纸"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"允许用户绑定到壁纸的顶级接口。普通应用绝不需要此权限。"</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"绑定到小部件服务"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"允许应用绑定到小部件服务的顶级接口。普通应用绝不需要此权限。"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"与设备管理器交互"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"系统"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"蓝牙音频"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"无线显示"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"完成"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"媒体输出线路"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"正在扫描..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"正在连接..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"可连接"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 0fd3fdb..98ebc5a 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -382,6 +382,8 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"允許應用程式繫結至 VPN 服務的頂層介面 (不建議一般應用程式使用)。"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"繫結至桌布"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"允許應用程式繫結至桌布的頂層介面 (不建議一般應用程式使用)。"</string>
+ <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"繫結至遠端屏幕"</string>
+ <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"允許應用程式繫結至遠端屏螢的頂層介面 (不建議一般應用程式使用)。"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"繫結至小工具服務"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"允許應用程式繫結至小工具服務的頂層介面 (不建議一般應用程式使用)。"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"與裝置管理員互動"</string>
@@ -1489,8 +1491,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"系統"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"藍牙音頻"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"無線螢幕分享"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"完成"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"媒體輸出"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"正在掃描…"</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"正在連線..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"可用"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 8f8ccdd..7c5f869 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -382,6 +382,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"允許應用程式聯繫至 VPN 服務的頂層介面 (一般應用程式不需使用)。"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"連結至桌布"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"允許應用程式繫結至桌布的頂層介面 (一般應用程式不需使用)。"</string>
+ <!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
+ <skip />
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"繫結至小工具服務"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"允許應用程式繫結至小工具服務的頂層介面 (一般應用程式不需使用)。"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"與裝置管理員互動"</string>
@@ -1489,8 +1493,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"系統"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"藍牙音訊"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"無線螢幕分享"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"完成"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"媒體輸出"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"掃描中..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"連線中…"</string>
<string name="media_route_status_available" msgid="6983258067194649391">"可以使用"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 9f47a5c..9a1108f 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -382,6 +382,8 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Ivumela umnini ukuthi abophele kwissekelo esingaphezulu sesevisi ye-Vpm. Ayidingakeli izinhlelo zokusebenza ezejwayelekile."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"hlanganisa kwiphephadonga"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Ivumela umbambi ukuhlanganisa uxhumano nomsebenzisi kwezinga eliphezulu lwephephadonga. Akusoze kwadingeka kwezinhlelo zokusebenza ezivamile."</string>
+ <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"bophezela kusibonisi sesilawuli kude"</string>
+ <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Ivumela umbambi ukuhlanganisa isixhumi esibonakalayo esisezingeni eliphezulu sesibonisi sesilawuli kude. Akumele idingelwe izinhlelo zokusebenza ezijwayelekile."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"bophezela kube isevisi yesinqunjana"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Ivumela umbambi ukuhlanganisa uxhumano nomsebenzisi kwezinga eliphezulu lensizakalo yesinqunjwana. Akusoze kwadingeka kwezinhlelo zokusebenza ezivamile."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"xhumana nomphathi wedivaysi"</string>
@@ -1489,8 +1491,15 @@
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Isistimu"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Umsindo we-Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Ukubonisa okungenazintambo"</string>
- <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Qedile"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Okukhiphayo kwemidiya"</string>
+ <!-- no translation found for media_route_chooser_title (1751618554539087622) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_searching (4776236202610828706) -->
+ <skip />
+ <!-- no translation found for media_route_chooser_extended_settings (87015534236701604) -->
+ <skip />
+ <!-- no translation found for media_route_controller_disconnect (8966120286374158649) -->
+ <skip />
<string name="media_route_status_scanning" msgid="7279908761758293783">"Iyaskena..."</string>
<string name="media_route_status_connecting" msgid="6422571716007825440">"Iyaxhuma..."</string>
<string name="media_route_status_available" msgid="6983258067194649391">"Kuyatholakala"</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 62f26c6..af5ace0 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1052,6 +1052,12 @@
interface of a wallpaper. Should never be needed for normal apps.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_bindRemoteDisplay">bind to a remote display</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_bindRemoteDisplay">Allows the holder to bind to the top-level
+ interface of a remote display. Should never be needed for normal apps.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_bindRemoteViews">bind to a widget service</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_bindRemoteViews">Allows the holder to bind to the top-level
@@ -4090,12 +4096,21 @@
<!-- Description of a wireless display route. [CHAR LIMIT=50] -->
<string name="wireless_display_route_description">Wireless display</string>
- <!-- "Done" button for MediaRouter chooser dialog when grouping routes. [CHAR LIMIT=NONE] -->
- <string name="media_route_chooser_grouping_done">Done</string>
-
<!-- Content description of a MediaRouteButton for accessibility support -->
<string name="media_route_button_content_description">Media output</string>
+ <!-- Title of the media route chooser dialog. [CHAR LIMIT=30] -->
+ <string name="media_route_chooser_title">Connect to device</string>
+
+ <!-- Placeholder text to show when no devices have been found. [CHAR LIMIT=50] -->
+ <string name="media_route_chooser_searching">Searching for devices\u2026</string>
+
+ <!-- Button to access extended settings. [CHAR LIMIT=30] -->
+ <string name="media_route_chooser_extended_settings">Settings</string>
+
+ <!-- Button to disconnect from a media route. [CHAR LIMIT=30] -->
+ <string name="media_route_controller_disconnect">Disconnect</string>
+
<!-- Status message for remote routes attempting to scan/determine availability -->
<string name="media_route_status_scanning">Scanning...</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b85bff4..ee64bf9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1096,6 +1096,8 @@
<java-symbol type="drawable" name="notification_template_icon_bg" />
<java-symbol type="drawable" name="notification_template_icon_low_bg" />
<java-symbol type="drawable" name="ic_media_route_on_holo_dark" />
+ <java-symbol type="drawable" name="ic_media_route_off_holo_dark" />
+ <java-symbol type="drawable" name="ic_media_route_connecting_holo_dark" />
<java-symbol type="drawable" name="ic_media_route_disabled_holo_dark" />
<java-symbol type="drawable" name="cling_button" />
<java-symbol type="drawable" name="cling_arrow_up" />
@@ -1253,17 +1255,16 @@
<java-symbol type="attr" name="mediaRouteButtonStyle" />
<java-symbol type="attr" name="externalRouteEnabledDrawable" />
- <java-symbol type="id" name="extended_settings" />
- <java-symbol type="id" name="check" />
- <java-symbol type="id" name="volume_slider" />
- <java-symbol type="id" name="volume_icon" />
- <java-symbol type="drawable" name="ic_media_route_on_holo_dark" />
- <java-symbol type="layout" name="media_route_chooser_layout" />
- <java-symbol type="layout" name="media_route_list_item_top_header" />
- <java-symbol type="layout" name="media_route_list_item_section_header" />
+ <java-symbol type="layout" name="media_route_chooser_dialog" />
+ <java-symbol type="layout" name="media_route_controller_dialog" />
<java-symbol type="layout" name="media_route_list_item" />
- <java-symbol type="layout" name="media_route_list_item_checkable" />
- <java-symbol type="layout" name="media_route_list_item_collapse_group" />
+ <java-symbol type="id" name="media_route_list" />
+ <java-symbol type="id" name="media_route_volume_layout" />
+ <java-symbol type="id" name="media_route_volume_slider" />
+ <java-symbol type="id" name="media_route_control_frame" />
+ <java-symbol type="id" name="media_route_disconnect_button" />
+ <java-symbol type="id" name="media_route_extended_settings_button" />
+ <java-symbol type="string" name="media_route_chooser_title" />
<java-symbol type="string" name="bluetooth_a2dp_audio_route_name" />
<java-symbol type="dimen" name="config_minScalingSpan" />
@@ -1451,7 +1452,6 @@
<java-symbol type="color" name="config_defaultNotificationColor" />
<java-symbol type="color" name="input_method_navigation_guard" />
<java-symbol type="drawable" name="ic_notification_ime_default" />
- <java-symbol type="drawable" name="ic_notify_wifidisplay" />
<java-symbol type="drawable" name="ic_menu_refresh" />
<java-symbol type="drawable" name="stat_notify_car_mode" />
<java-symbol type="drawable" name="stat_notify_disabled" />
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index 7af3b9c..3215e17 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -58,4 +58,12 @@
</array>
<!-- This is the battery capacity in mAh (measured at nominal voltage) -->
<item name="battery.capacity">1000</item>
+
+ <array name="wifi.batchedscan"> <!-- mA -->
+ <value>.0002</value> <!-- 1-8/hr -->
+ <value>.002</value> <!-- 9-64/hr -->
+ <value>.02</value> <!-- 65-512/hr -->
+ <value>.2</value> <!-- 513-4,096/hr -->
+ <value>2</value> <!-- 4097-/hr -->
+ </array>
</device>
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
index 76b702e..4a58f88 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
@@ -36,8 +36,6 @@
import com.android.bandwidthtest.util.ConnectionUtil;
import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
/**
* Test that downloads files from a test server and reports the bandwidth metrics collected.
@@ -131,8 +129,8 @@
results.putString("device_id", mDeviceId);
results.putString("timestamp", ts);
results.putInt("size", FILE_SIZE);
- AddStatsToResults(PROF_LABEL, prof_stats, results);
- AddStatsToResults(PROC_LABEL, proc_stats, results);
+ addStatsToResults(PROF_LABEL, prof_stats, results, mUid);
+ addStatsToResults(PROC_LABEL, proc_stats, results, mUid);
getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results);
// Clean up.
@@ -185,8 +183,8 @@
results.putString("device_id", mDeviceId);
results.putString("timestamp", ts);
results.putInt("size", FILE_SIZE);
- AddStatsToResults(PROF_LABEL, prof_stats, results);
- AddStatsToResults(PROC_LABEL, proc_stats, results);
+ addStatsToResults(PROF_LABEL, prof_stats, results, mUid);
+ addStatsToResults(PROC_LABEL, proc_stats, results, mUid);
getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results);
// Clean up.
@@ -242,8 +240,9 @@
results.putString("device_id", mDeviceId);
results.putString("timestamp", ts);
results.putInt("size", FILE_SIZE);
- AddStatsToResults(PROF_LABEL, prof_stats, results);
- AddStatsToResults(PROC_LABEL, proc_stats, results);
+ addStatsToResults(PROF_LABEL, prof_stats, results, mUid);
+ // remember to use download manager uid for proc stats
+ addStatsToResults(PROC_LABEL, proc_stats, results, downloadManagerUid);
getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results);
// Clean up.
@@ -302,46 +301,35 @@
* @param label to attach to this given stats.
* @param stats {@link NetworkStats} to add.
* @param results {@link Bundle} to be added to.
+ * @param uid for which to report the results.
*/
- public void AddStatsToResults(String label, NetworkStats stats, Bundle results){
+ public void addStatsToResults(String label, NetworkStats stats, Bundle results, int uid){
if (results == null || results.isEmpty()) {
Log.e(LOG_TAG, "Empty bundle provided.");
return;
}
- // Merge stats across all sets.
- Map<Integer, Entry> totalStats = new HashMap<Integer, Entry>();
+ Entry totalStats = null;
for (int i = 0; i < stats.size(); ++i) {
Entry statsEntry = stats.getValues(i, null);
// We are only interested in the all inclusive stats.
if (statsEntry.tag != 0) {
continue;
}
- Entry mapEntry = null;
- if (totalStats.containsKey(statsEntry.uid)) {
- mapEntry = totalStats.get(statsEntry.uid);
- switch (statsEntry.set) {
- case NetworkStats.SET_ALL:
- mapEntry.rxBytes = statsEntry.rxBytes;
- mapEntry.txBytes = statsEntry.txBytes;
- break;
- case NetworkStats.SET_DEFAULT:
- case NetworkStats.SET_FOREGROUND:
- mapEntry.rxBytes += statsEntry.rxBytes;
- mapEntry.txBytes += statsEntry.txBytes;
- break;
- default:
- Log.w(LOG_TAG, "Invalid state found in NetworkStats.");
- }
+ // skip stats for other uids
+ if (statsEntry.uid != uid) {
+ continue;
+ }
+ if (totalStats == null || statsEntry.set == NetworkStats.SET_ALL) {
+ totalStats = statsEntry;
} else {
- totalStats.put(statsEntry.uid, statsEntry);
+ totalStats.rxBytes += statsEntry.rxBytes;
+ totalStats.txBytes += statsEntry.txBytes;
}
}
- // Ouput merged stats to bundle.
- for (Entry entry : totalStats.values()) {
- results.putInt(label + "uid", entry.uid);
- results.putLong(label + "tx", entry.txBytes);
- results.putLong(label + "rx", entry.rxBytes);
- }
+ // Output merged stats to bundle.
+ results.putInt(label + "uid", totalStats.uid);
+ results.putLong(label + "tx", totalStats.txBytes);
+ results.putLong(label + "rx", totalStats.rxBytes);
}
/**
diff --git a/data/sounds/AudioPackage10.mk b/data/sounds/AudioPackage10.mk
index 783e1f8..ae4bc88 100644
--- a/data/sounds/AudioPackage10.mk
+++ b/data/sounds/AudioPackage10.mk
@@ -17,19 +17,19 @@
$(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
$(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
$(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:system/media/audio/ui/Effect_Tick.ogg \
- $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:system/media/audio/ui/KeypressStandard.ogg \
- $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
- $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
- $(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
- $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
+ $(LOCAL_PATH)/effects/ogg/KeypressStandard_48k.ogg:system/media/audio/ui/KeypressStandard.ogg \
+ $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
+ $(LOCAL_PATH)/effects/ogg/KeypressDelete_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
+ $(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
+ $(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \
$(LOCAL_PATH)/effects/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \
$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
- $(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
- $(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
+ $(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:system/media/audio/ui/Lock.ogg \
+ $(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:system/media/audio/ui/Unlock.ogg \
$(LOCAL_PATH)/effects/ogg/WirelessChargingStarted.ogg:system/media/audio/ui/WirelessChargingStarted.ogg \
$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
$(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
diff --git a/data/sounds/notifications/ogg/Tethys_48k.ogg b/data/sounds/notifications/ogg/Tethys_48k.ogg
index a9d8bbd..355d522 100644
--- a/data/sounds/notifications/ogg/Tethys_48k.ogg
+++ b/data/sounds/notifications/ogg/Tethys_48k.ogg
Binary files differ
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index af8441a..aac7876 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -88,9 +88,10 @@
@Override
public boolean getPadding(Rect padding) {
final Rect r = mDrawableContainerState.getConstantPadding();
- boolean result = true;
+ boolean result;
if (r != null) {
padding.set(r);
+ result = (r.left | r.top | r.bottom | r.right) != 0;
} else {
if (mCurrDrawable != null) {
result = mCurrDrawable.getPadding(padding);
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index ab34c0f..9c57a2c 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -244,7 +244,7 @@
} else {
padding.set(mPadding);
}
- return true;
+ return (padding.left | padding.top | padding.right | padding.bottom) != 0;
}
/**
diff --git a/graphics/java/android/graphics/pdf/PdfDocument.java b/graphics/java/android/graphics/pdf/PdfDocument.java
index 81e523d..29d14a2 100644
--- a/graphics/java/android/graphics/pdf/PdfDocument.java
+++ b/graphics/java/android/graphics/pdf/PdfDocument.java
@@ -18,6 +18,7 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Paint;
import android.graphics.Rect;
import dalvik.system.CloseGuard;
@@ -69,6 +70,12 @@
*/
public class PdfDocument {
+ // TODO: We need a constructor that will take an OutputStream to
+ // support online data serialization as opposed to the current
+ // on demand one. The current approach is fine until Skia starts
+ // to support online PDF generation at which point we need to
+ // handle this.
+
private final byte[] mChunk = new byte[4096];
private final CloseGuard mCloseGuard = CloseGuard.get();
@@ -111,7 +118,7 @@
if (pageInfo == null) {
throw new IllegalArgumentException("page cannot be null");
}
- Canvas canvas = new PdfCanvas(nativeCreatePage(pageInfo.mPageWidth,
+ Canvas canvas = new PdfCanvas(nativeStartPage(mNativeDocument, pageInfo.mPageWidth,
pageInfo.mPageHeight, pageInfo.mContentRect.left, pageInfo.mContentRect.top,
pageInfo.mContentRect.right, pageInfo.mContentRect.bottom));
mCurrentPage = new Page(canvas, pageInfo);
@@ -142,7 +149,7 @@
}
mPages.add(page.getInfo());
mCurrentPage = null;
- nativeAppendPage(mNativeDocument, page.mCanvas.mNativeCanvas);
+ nativeFinishPage(mNativeDocument);
page.finish();
}
@@ -204,7 +211,7 @@
private void dispose() {
if (mNativeDocument != 0) {
- nativeFinalize(mNativeDocument);
+ nativeClose(mNativeDocument);
mCloseGuard.close();
mNativeDocument = 0;
}
@@ -230,14 +237,14 @@
private native int nativeCreateDocument();
- private native void nativeFinalize(int document);
+ private native void nativeClose(int document);
- private native void nativeAppendPage(int document, int page);
+ private native void nativeFinishPage(int document);
private native void nativeWriteTo(int document, OutputStream out, byte[] chunk);
- private static native int nativeCreatePage(int pageWidth, int pageHeight, int contentLeft,
- int contentTop, int contentRight, int contentBottom);
+ private static native int nativeStartPage(int documentPtr, int pageWidth, int pageHeight,
+ int contentLeft, int contentTop, int contentRight, int contentBottom);
private final class PdfCanvas extends Canvas {
@@ -392,28 +399,28 @@
* Gets the {@link Canvas} of the page.
*
* <p>
- * <strong>Note: </strong> There are some draw operations that are
- * not yet supported by the canvas returned by this method. More
- * specifically:
+ * <strong>Note: </strong> There are some draw operations that are not yet
+ * supported by the canvas returned by this method. More specifically:
* <ul>
- * <li>{@link Canvas#clipPath(android.graphics.Path)
- * Canvas.clipPath(android.graphics.Path)}</li>
- * <li>All flavors of {@link Canvas#drawText(String, float, float,
- * android.graphics.Paint) Canvas.drawText(String, float, float,
- * android.graphics.Paint)}</li>
- * <li>All flavors of {@link Canvas#drawPosText(String, float[],
- * android.graphics.Paint) Canvas.drawPosText(String, float[],
- * android.graphics.Paint)}</li>
+ * <li>Inverse path clipping performed via {@link Canvas#clipPath(android.graphics.Path,
+ * android.graphics.Region.Op) Canvas.clipPath(android.graphics.Path,
+ * android.graphics.Region.Op)} for {@link
+ * android.graphics.Region.Op#REVERSE_DIFFERENCE
+ * Region.Op#REVERSE_DIFFERENCE} operations.</li>
* <li>{@link Canvas#drawVertices(android.graphics.Canvas.VertexMode, int,
* float[], int, float[], int, int[], int, short[], int, int,
* android.graphics.Paint) Canvas.drawVertices(
* android.graphics.Canvas.VertexMode, int, float[], int, float[],
* int, int[], int, short[], int, int, android.graphics.Paint)}</li>
- * <li>{@link android.graphics.PorterDuff.Mode#SRC_ATOP PorterDuff.Mode SRC},
+ * <li>Color filters set via {@link Paint#setColorFilter(
+ * android.graphics.ColorFilter)}</li>
+ * <li>Mask filters set via {@link Paint#setMaskFilter(
+ * android.graphics.MaskFilter)}</li>
+ * <li>Some XFER modes such as
+ * {@link android.graphics.PorterDuff.Mode#SRC_ATOP PorterDuff.Mode SRC},
* {@link android.graphics.PorterDuff.Mode#DST_ATOP PorterDuff.DST_ATOP},
* {@link android.graphics.PorterDuff.Mode#XOR PorterDuff.XOR},
* {@link android.graphics.PorterDuff.Mode#ADD PorterDuff.ADD}</li>
- * <li>Perspective transforms</li>
* </ul>
*
* @return The canvas if the page is not finished, null otherwise.
diff --git a/media/java/android/media/IMediaRouterClient.aidl b/media/java/android/media/IMediaRouterClient.aidl
new file mode 100644
index 0000000..9640dcb
--- /dev/null
+++ b/media/java/android/media/IMediaRouterClient.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+oneway interface IMediaRouterClient {
+ void onStateChanged();
+}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
new file mode 100644
index 0000000..f8f5fdf
--- /dev/null
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.IMediaRouterClient;
+import android.media.MediaRouterClientState;
+
+/**
+ * {@hide}
+ */
+interface IMediaRouterService {
+ void registerClientAsUser(IMediaRouterClient client, String packageName, int userId);
+ void unregisterClient(IMediaRouterClient client);
+
+ MediaRouterClientState getState(IMediaRouterClient client);
+
+ void setDiscoveryRequest(IMediaRouterClient client, int routeTypes, boolean activeScan);
+ void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit);
+ void requestSetVolume(IMediaRouterClient client, String routeId, int volume);
+ void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction);
+}
diff --git a/media/java/android/media/IRemoteDisplayCallback.aidl b/media/java/android/media/IRemoteDisplayCallback.aidl
new file mode 100644
index 0000000..19cf070
--- /dev/null
+++ b/media/java/android/media/IRemoteDisplayCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.RemoteDisplayState;
+
+/**
+ * {@hide}
+ */
+oneway interface IRemoteDisplayCallback {
+ void onStateChanged(in RemoteDisplayState state);
+}
diff --git a/media/java/android/media/IRemoteDisplayProvider.aidl b/media/java/android/media/IRemoteDisplayProvider.aidl
new file mode 100644
index 0000000..b0d7379
--- /dev/null
+++ b/media/java/android/media/IRemoteDisplayProvider.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.IRemoteDisplayCallback;
+
+/**
+ * {@hide}
+ */
+oneway interface IRemoteDisplayProvider {
+ void setCallback(in IRemoteDisplayCallback callback);
+ void setDiscoveryMode(int mode);
+ void connect(String id);
+ void disconnect(String id);
+ void setVolume(String id, int volume);
+ void adjustVolume(String id, int delta);
+}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 9a79c94..3d10158 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -16,6 +16,8 @@
package android.media;
+import com.android.internal.util.Objects;
+
import android.app.ActivityThread;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -30,6 +32,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
@@ -52,14 +55,17 @@
*/
public class MediaRouter {
private static final String TAG = "MediaRouter";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
static class Static implements DisplayManager.DisplayListener {
// Time between wifi display scans when actively scanning in milliseconds.
- private static final int WIFI_DISPLAY_SCAN_INTERVAL = 15000;
+ private static final int WIFI_DISPLAY_SCAN_INTERVAL = 10000;
+ final Context mAppContext;
final Resources mResources;
final IAudioService mAudioService;
final DisplayManager mDisplayService;
+ final IMediaRouterService mMediaRouterService;
final Handler mHandler;
final CopyOnWriteArrayList<CallbackInfo> mCallbacks =
new CopyOnWriteArrayList<CallbackInfo>();
@@ -76,9 +82,15 @@
RouteInfo mSelectedRoute;
- WifiDisplayStatus mLastKnownWifiDisplayStatus;
boolean mActivelyScanningWifiDisplays;
+ int mDiscoveryRequestRouteTypes;
+ boolean mDiscoverRequestActiveScan;
+
+ int mCurrentUserId = -1;
+ IMediaRouterClient mClient;
+ MediaRouterClientState mClientState;
+
final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
@Override
public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
@@ -101,6 +113,7 @@
};
Static(Context appContext) {
+ mAppContext = appContext;
mResources = Resources.getSystem();
mHandler = new Handler(appContext.getMainLooper());
@@ -109,6 +122,9 @@
mDisplayService = (DisplayManager) appContext.getSystemService(Context.DISPLAY_SERVICE);
+ mMediaRouterService = IMediaRouterService.Stub.asInterface(
+ ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
+
mSystemCategory = new RouteCategory(
com.android.internal.R.string.default_audio_route_category_name,
ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO, false);
@@ -120,8 +136,7 @@
mDefaultAudioVideo = new RouteInfo(mSystemCategory);
mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name;
mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
- mDefaultAudioVideo.mPresentationDisplay = choosePresentationDisplayForRoute(
- mDefaultAudioVideo, getAllPresentationDisplays());
+ mDefaultAudioVideo.updatePresentationDisplay();
addRouteStatic(mDefaultAudioVideo);
// This will select the active wifi display route if there is one.
@@ -146,10 +161,13 @@
updateAudioRoutes(newAudioRoutes);
}
+ // Bind to the media router service.
+ rebindAsUser(UserHandle.myUserId());
+
// Select the default route if the above didn't sync us up
// appropriately with relevant system state.
if (mSelectedRoute == null) {
- selectRouteStatic(mDefaultAudioVideo.getSupportedTypes(), mDefaultAudioVideo);
+ selectDefaultRouteStatic();
}
}
@@ -197,7 +215,7 @@
dispatchRouteChanged(sStatic.mBluetoothA2dpRoute);
}
} else if (sStatic.mBluetoothA2dpRoute != null) {
- removeRoute(sStatic.mBluetoothA2dpRoute);
+ removeRouteStatic(sStatic.mBluetoothA2dpRoute);
sStatic.mBluetoothA2dpRoute = null;
}
}
@@ -205,16 +223,52 @@
if (mBluetoothA2dpRoute != null) {
if (mainType != AudioRoutesInfo.MAIN_SPEAKER &&
mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) {
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo);
+ selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo, false);
} else if ((mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
a2dpEnabled) {
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute);
+ selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute, false);
}
}
}
- void updateActiveScan() {
- if (hasActiveScanCallbackOfType(ROUTE_TYPE_LIVE_VIDEO)) {
+ void updateDiscoveryRequest() {
+ // What are we looking for today?
+ int routeTypes = 0;
+ int passiveRouteTypes = 0;
+ boolean activeScan = false;
+ boolean activeScanWifiDisplay = false;
+ final int count = mCallbacks.size();
+ for (int i = 0; i < count; i++) {
+ CallbackInfo cbi = mCallbacks.get(i);
+ if ((cbi.flags & (CALLBACK_FLAG_PERFORM_ACTIVE_SCAN
+ | CALLBACK_FLAG_REQUEST_DISCOVERY)) != 0) {
+ // Discovery explicitly requested.
+ routeTypes |= cbi.type;
+ } else if ((cbi.flags & CALLBACK_FLAG_PASSIVE_DISCOVERY) != 0) {
+ // Discovery only passively requested.
+ passiveRouteTypes |= cbi.type;
+ } else {
+ // Legacy case since applications don't specify the discovery flag.
+ // Unfortunately we just have to assume they always need discovery
+ // whenever they have a callback registered.
+ routeTypes |= cbi.type;
+ }
+ if ((cbi.flags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
+ activeScan = true;
+ if ((cbi.type & (ROUTE_TYPE_LIVE_VIDEO | ROUTE_TYPE_REMOTE_DISPLAY)) != 0) {
+ activeScanWifiDisplay = true;
+ }
+ }
+ }
+ if (routeTypes != 0 || activeScan) {
+ // If someone else requests discovery then enable the passive listeners.
+ // This is used by the MediaRouteButton and MediaRouteActionProvider since
+ // they don't receive lifecycle callbacks from the Activity.
+ routeTypes |= passiveRouteTypes;
+ }
+
+ // Update wifi display scanning.
+ if (activeScanWifiDisplay) {
if (!mActivelyScanningWifiDisplays) {
mActivelyScanningWifiDisplays = true;
mHandler.post(mScanWifiDisplays);
@@ -225,18 +279,14 @@
mHandler.removeCallbacks(mScanWifiDisplays);
}
}
- }
- private boolean hasActiveScanCallbackOfType(int type) {
- final int count = mCallbacks.size();
- for (int i = 0; i < count; i++) {
- CallbackInfo cbi = mCallbacks.get(i);
- if ((cbi.flags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0
- && (cbi.type & type) != 0) {
- return true;
- }
+ // Tell the media router service all about it.
+ if (routeTypes != mDiscoveryRequestRouteTypes
+ || activeScan != mDiscoverRequestActiveScan) {
+ mDiscoveryRequestRouteTypes = routeTypes;
+ mDiscoverRequestActiveScan = activeScan;
+ publishClientDiscoveryRequest();
}
- return false;
}
@Override
@@ -259,18 +309,267 @@
}
private void updatePresentationDisplays(int changedDisplayId) {
- final Display[] displays = getAllPresentationDisplays();
final int count = mRoutes.size();
for (int i = 0; i < count; i++) {
- final RouteInfo info = mRoutes.get(i);
- Display display = choosePresentationDisplayForRoute(info, displays);
- if (display != info.mPresentationDisplay
- || (display != null && display.getDisplayId() == changedDisplayId)) {
- info.mPresentationDisplay = display;
- dispatchRoutePresentationDisplayChanged(info);
+ final RouteInfo route = mRoutes.get(i);
+ if (route.updatePresentationDisplay() || (route.mPresentationDisplay != null
+ && route.mPresentationDisplay.getDisplayId() == changedDisplayId)) {
+ dispatchRoutePresentationDisplayChanged(route);
}
}
}
+
+ void setSelectedRoute(RouteInfo info, boolean explicit) {
+ // Must be non-reentrant.
+ mSelectedRoute = info;
+ publishClientSelectedRoute(explicit);
+ }
+
+ void rebindAsUser(int userId) {
+ if (mCurrentUserId != userId || userId < 0 || mClient == null) {
+ if (mClient != null) {
+ try {
+ mMediaRouterService.unregisterClient(mClient);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to unregister media router client.", ex);
+ }
+ mClient = null;
+ }
+
+ mCurrentUserId = userId;
+
+ try {
+ Client client = new Client();
+ mMediaRouterService.registerClientAsUser(client,
+ mAppContext.getPackageName(), userId);
+ mClient = client;
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to register media router client.", ex);
+ }
+
+ publishClientDiscoveryRequest();
+ publishClientSelectedRoute(false);
+ updateClientState();
+ }
+ }
+
+ void publishClientDiscoveryRequest() {
+ if (mClient != null) {
+ try {
+ mMediaRouterService.setDiscoveryRequest(mClient,
+ mDiscoveryRequestRouteTypes, mDiscoverRequestActiveScan);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to publish media router client discovery request.", ex);
+ }
+ }
+ }
+
+ void publishClientSelectedRoute(boolean explicit) {
+ if (mClient != null) {
+ try {
+ mMediaRouterService.setSelectedRoute(mClient,
+ mSelectedRoute != null ? mSelectedRoute.mGlobalRouteId : null,
+ explicit);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to publish media router client selected route.", ex);
+ }
+ }
+ }
+
+ void updateClientState() {
+ // Update the client state.
+ mClientState = null;
+ if (mClient != null) {
+ try {
+ mClientState = mMediaRouterService.getState(mClient);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to retrieve media router client state.", ex);
+ }
+ }
+ final ArrayList<MediaRouterClientState.RouteInfo> globalRoutes =
+ mClientState != null ? mClientState.routes : null;
+ final String globallySelectedRouteId = mClientState != null ?
+ mClientState.globallySelectedRouteId : null;
+
+ // Add or update routes.
+ final int globalRouteCount = globalRoutes != null ? globalRoutes.size() : 0;
+ for (int i = 0; i < globalRouteCount; i++) {
+ final MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(i);
+ RouteInfo route = findGlobalRoute(globalRoute.id);
+ if (route == null) {
+ route = makeGlobalRoute(globalRoute);
+ addRouteStatic(route);
+ } else {
+ updateGlobalRoute(route, globalRoute);
+ }
+ }
+
+ // Synchronize state with the globally selected route.
+ if (globallySelectedRouteId != null) {
+ final RouteInfo route = findGlobalRoute(globallySelectedRouteId);
+ if (route == null) {
+ Log.w(TAG, "Could not find new globally selected route: "
+ + globallySelectedRouteId);
+ } else if (route != mSelectedRoute) {
+ if (DEBUG) {
+ Log.d(TAG, "Selecting new globally selected route: " + route);
+ }
+ selectRouteStatic(route.mSupportedTypes, route, false);
+ }
+ } else if (mSelectedRoute != null && mSelectedRoute.mGlobalRouteId != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Unselecting previous globally selected route: " + mSelectedRoute);
+ }
+ selectDefaultRouteStatic();
+ }
+
+ // Remove defunct routes.
+ outer: for (int i = mRoutes.size(); i-- > 0; ) {
+ final RouteInfo route = mRoutes.get(i);
+ final String globalRouteId = route.mGlobalRouteId;
+ if (globalRouteId != null) {
+ for (int j = 0; j < globalRouteCount; j++) {
+ MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(j);
+ if (globalRouteId.equals(globalRoute.id)) {
+ continue outer; // found
+ }
+ }
+ // not found
+ removeRouteStatic(route);
+ }
+ }
+ }
+
+ void requestSetVolume(RouteInfo route, int volume) {
+ if (route.mGlobalRouteId != null && mClient != null) {
+ try {
+ mMediaRouterService.requestSetVolume(mClient,
+ route.mGlobalRouteId, volume);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Unable to request volume change.", ex);
+ }
+ }
+ }
+
+ void requestUpdateVolume(RouteInfo route, int direction) {
+ if (route.mGlobalRouteId != null && mClient != null) {
+ try {
+ mMediaRouterService.requestUpdateVolume(mClient,
+ route.mGlobalRouteId, direction);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Unable to request volume change.", ex);
+ }
+ }
+ }
+
+ RouteInfo makeGlobalRoute(MediaRouterClientState.RouteInfo globalRoute) {
+ RouteInfo route = new RouteInfo(sStatic.mSystemCategory);
+ route.mGlobalRouteId = globalRoute.id;
+ route.mName = globalRoute.name;
+ route.mDescription = globalRoute.description;
+ route.mSupportedTypes = globalRoute.supportedTypes;
+ route.mEnabled = globalRoute.enabled;
+ route.setRealStatusCode(globalRoute.statusCode);
+ route.mPlaybackType = globalRoute.playbackType;
+ route.mPlaybackStream = globalRoute.playbackStream;
+ route.mVolume = globalRoute.volume;
+ route.mVolumeMax = globalRoute.volumeMax;
+ route.mVolumeHandling = globalRoute.volumeHandling;
+ route.mPresentationDisplayId = globalRoute.presentationDisplayId;
+ route.updatePresentationDisplay();
+ return route;
+ }
+
+ void updateGlobalRoute(RouteInfo route, MediaRouterClientState.RouteInfo globalRoute) {
+ boolean changed = false;
+ boolean volumeChanged = false;
+ boolean presentationDisplayChanged = false;
+
+ if (!Objects.equal(route.mName, globalRoute.name)) {
+ route.mName = globalRoute.name;
+ changed = true;
+ }
+ if (!Objects.equal(route.mDescription, globalRoute.description)) {
+ route.mDescription = globalRoute.description;
+ changed = true;
+ }
+ if (route.mSupportedTypes != globalRoute.supportedTypes) {
+ route.mSupportedTypes = globalRoute.supportedTypes;
+ changed = true;
+ }
+ if (route.mEnabled != globalRoute.enabled) {
+ route.mEnabled = globalRoute.enabled;
+ changed = true;
+ }
+ if (route.mRealStatusCode != globalRoute.statusCode) {
+ route.setRealStatusCode(globalRoute.statusCode);
+ changed = true;
+ }
+ if (route.mPlaybackType != globalRoute.playbackType) {
+ route.mPlaybackType = globalRoute.playbackType;
+ changed = true;
+ }
+ if (route.mPlaybackStream != globalRoute.playbackStream) {
+ route.mPlaybackStream = globalRoute.playbackStream;
+ changed = true;
+ }
+ if (route.mVolume != globalRoute.volume) {
+ route.mVolume = globalRoute.volume;
+ changed = true;
+ volumeChanged = true;
+ }
+ if (route.mVolumeMax != globalRoute.volumeMax) {
+ route.mVolumeMax = globalRoute.volumeMax;
+ changed = true;
+ volumeChanged = true;
+ }
+ if (route.mVolumeHandling != globalRoute.volumeHandling) {
+ route.mVolumeHandling = globalRoute.volumeHandling;
+ changed = true;
+ volumeChanged = true;
+ }
+ if (route.mPresentationDisplayId != globalRoute.presentationDisplayId) {
+ route.mPresentationDisplayId = globalRoute.presentationDisplayId;
+ route.updatePresentationDisplay();
+ changed = true;
+ presentationDisplayChanged = true;
+ }
+
+ if (changed) {
+ dispatchRouteChanged(route);
+ }
+ if (volumeChanged) {
+ dispatchRouteVolumeChanged(route);
+ }
+ if (presentationDisplayChanged) {
+ dispatchRoutePresentationDisplayChanged(route);
+ }
+ }
+
+ RouteInfo findGlobalRoute(String globalRouteId) {
+ final int count = mRoutes.size();
+ for (int i = 0; i < count; i++) {
+ final RouteInfo route = mRoutes.get(i);
+ if (globalRouteId.equals(route.mGlobalRouteId)) {
+ return route;
+ }
+ }
+ return null;
+ }
+
+ final class Client extends IMediaRouterClient.Stub {
+ @Override
+ public void onStateChanged() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (Client.this == mClient) {
+ updateClientState();
+ }
+ }
+ });
+ }
+ }
}
static Static sStatic;
@@ -285,7 +584,7 @@
* <p>Once initiated this routing is transparent to the application. All audio
* played on the media stream will be routed to the selected destination.</p>
*/
- public static final int ROUTE_TYPE_LIVE_AUDIO = 0x1;
+ public static final int ROUTE_TYPE_LIVE_AUDIO = 1 << 0;
/**
* Route type flag for live video.
@@ -302,7 +601,13 @@
* @see RouteInfo#getPresentationDisplay()
* @see android.app.Presentation
*/
- public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2;
+ public static final int ROUTE_TYPE_LIVE_VIDEO = 1 << 1;
+
+ /**
+ * Temporary interop constant to identify remote displays.
+ * @hide To be removed when media router API is updated.
+ */
+ public static final int ROUTE_TYPE_REMOTE_DISPLAY = 1 << 2;
/**
* Route type flag for application-specific usage.
@@ -312,7 +617,10 @@
* is expected to interpret the meaning of these events and perform the requested
* routing tasks.</p>
*/
- public static final int ROUTE_TYPE_USER = 0x00800000;
+ public static final int ROUTE_TYPE_USER = 1 << 23;
+
+ static final int ROUTE_TYPE_ANY = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO
+ | ROUTE_TYPE_REMOTE_DISPLAY | ROUTE_TYPE_USER;
/**
* Flag for {@link #addCallback}: Actively scan for routes while this callback
@@ -336,11 +644,40 @@
* Flag for {@link #addCallback}: Do not filter route events.
* <p>
* When this flag is specified, the callback will be invoked for event that affect any
- * route event if they do not match the callback's associated media route selector.
+ * route even if they do not match the callback's filter.
* </p>
*/
public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
+ /**
+ * Explicitly requests discovery.
+ *
+ * @hide Future API ported from support library. Revisit this later.
+ */
+ public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2;
+
+ /**
+ * Requests that discovery be performed but only if there is some other active
+ * callback already registered.
+ *
+ * @hide Compatibility workaround for the fact that applications do not currently
+ * request discovery explicitly (except when using the support library API).
+ */
+ public static final int CALLBACK_FLAG_PASSIVE_DISCOVERY = 1 << 3;
+
+ /**
+ * Flag for {@link #isRouteAvailable}: Ignore the default route.
+ * <p>
+ * This flag is used to determine whether a matching non-default route is available.
+ * This constraint may be used to decide whether to offer the route chooser dialog
+ * to the user. There is no point offering the chooser if there are no
+ * non-default choices.
+ * </p>
+ *
+ * @hide Future API ported from support library. Revisit this later.
+ */
+ public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0;
+
// Maps application contexts
static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
@@ -352,6 +689,9 @@
if ((types & ROUTE_TYPE_LIVE_VIDEO) != 0) {
result.append("ROUTE_TYPE_LIVE_VIDEO ");
}
+ if ((types & ROUTE_TYPE_REMOTE_DISPLAY) != 0) {
+ result.append("ROUTE_TYPE_REMOTE_DISPLAY ");
+ }
if ((types & ROUTE_TYPE_USER) != 0) {
result.append("ROUTE_TYPE_USER ");
}
@@ -388,6 +728,11 @@
return sStatic.mSystemCategory;
}
+ /** @hide */
+ public RouteInfo getSelectedRoute() {
+ return getSelectedRoute(ROUTE_TYPE_ANY);
+ }
+
/**
* Return the currently selected route for any of the given types
*
@@ -411,6 +756,38 @@
}
/**
+ * Returns true if there is a route that matches the specified types.
+ * <p>
+ * This method returns true if there are any available routes that match the types
+ * regardless of whether they are enabled or disabled. If the
+ * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then
+ * the method will only consider non-default routes.
+ * </p>
+ *
+ * @param types The types to match.
+ * @param flags Flags to control the determination of whether a route may be available.
+ * May be zero or {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE}.
+ * @return True if a matching route may be available.
+ *
+ * @hide Future API ported from support library. Revisit this later.
+ */
+ public boolean isRouteAvailable(int types, int flags) {
+ final int count = sStatic.mRoutes.size();
+ for (int i = 0; i < count; i++) {
+ RouteInfo route = sStatic.mRoutes.get(i);
+ if (route.matchesTypes(types)) {
+ if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) == 0
+ || route != sStatic.mDefaultAudioVideo) {
+ return true;
+ }
+ }
+ }
+
+ // It doesn't look like we can find a matching route right now.
+ return false;
+ }
+
+ /**
* Add a callback to listen to events about specific kinds of media routes.
* If the specified callback is already registered, its registration will be updated for any
* additional route types specified.
@@ -453,9 +830,7 @@
info = new CallbackInfo(cb, types, flags, this);
sStatic.mCallbacks.add(info);
}
- if ((info.flags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
- sStatic.updateActiveScan();
- }
+ sStatic.updateDiscoveryRequest();
}
/**
@@ -466,10 +841,8 @@
public void removeCallback(Callback cb) {
int index = findCallbackInfo(cb);
if (index >= 0) {
- CallbackInfo info = sStatic.mCallbacks.remove(index);
- if ((info.flags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
- sStatic.updateActiveScan();
- }
+ sStatic.mCallbacks.remove(index);
+ sStatic.updateDiscoveryRequest();
} else {
Log.w(TAG, "removeCallback(" + cb + "): callback not registered");
}
@@ -499,20 +872,20 @@
* @param route Route to select
*/
public void selectRoute(int types, RouteInfo route) {
- selectRouteStatic(types, route);
+ selectRouteStatic(types, route, true);
}
-
+
/**
* @hide internal use
*/
- public void selectRouteInt(int types, RouteInfo route) {
- selectRouteStatic(types, route);
+ public void selectRouteInt(int types, RouteInfo route, boolean explicit) {
+ selectRouteStatic(types, route, explicit);
}
- static void selectRouteStatic(int types, RouteInfo route) {
+ static void selectRouteStatic(int types, RouteInfo route, boolean explicit) {
final RouteInfo oldRoute = sStatic.mSelectedRoute;
if (oldRoute == route) return;
- if ((route.getSupportedTypes() & types) == 0) {
+ if (!route.matchesTypes(types)) {
Log.w(TAG, "selectRoute ignored; cannot select route with supported types " +
typesToString(route.getSupportedTypes()) + " into route types " +
typesToString(types));
@@ -541,15 +914,32 @@
}
}
+ sStatic.setSelectedRoute(route, explicit);
+
if (oldRoute != null) {
dispatchRouteUnselected(types & oldRoute.getSupportedTypes(), oldRoute);
+ if (oldRoute.resolveStatusCode()) {
+ dispatchRouteChanged(oldRoute);
+ }
}
- sStatic.mSelectedRoute = route;
if (route != null) {
+ if (route.resolveStatusCode()) {
+ dispatchRouteChanged(route);
+ }
dispatchRouteSelected(types & route.getSupportedTypes(), route);
}
}
+ static void selectDefaultRouteStatic() {
+ // TODO: Be smarter about the route types here; this selects for all valid.
+ if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute
+ && sStatic.mBluetoothA2dpRoute != null) {
+ selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mBluetoothA2dpRoute, false);
+ } else {
+ selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mDefaultAudioVideo, false);
+ }
+ }
+
/**
* Compare the device address of a display and a route.
* Nulls/no device address will match another null/no address.
@@ -612,7 +1002,7 @@
* @see #addUserRoute(UserRouteInfo)
*/
public void removeUserRoute(UserRouteInfo info) {
- removeRoute(info);
+ removeRouteStatic(info);
}
/**
@@ -626,7 +1016,7 @@
// TODO Right now, RouteGroups only ever contain user routes.
// The code below will need to change if this assumption does.
if (info instanceof UserRouteInfo || info instanceof RouteGroup) {
- removeRouteAt(i);
+ removeRouteStatic(info);
i--;
}
}
@@ -636,10 +1026,10 @@
* @hide internal use only
*/
public void removeRouteInt(RouteInfo info) {
- removeRoute(info);
+ removeRouteStatic(info);
}
- static void removeRoute(RouteInfo info) {
+ static void removeRouteStatic(RouteInfo info) {
if (sStatic.mRoutes.remove(info)) {
final RouteCategory removingCat = info.getCategory();
final int count = sStatic.mRoutes.size();
@@ -651,42 +1041,9 @@
break;
}
}
- if (info == sStatic.mSelectedRoute) {
+ if (info.isSelected()) {
// Removing the currently selected route? Select the default before we remove it.
- // TODO: Be smarter about the route types here; this selects for all valid.
- if (info != sStatic.mBluetoothA2dpRoute && sStatic.mBluetoothA2dpRoute != null) {
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_USER,
- sStatic.mBluetoothA2dpRoute);
- } else {
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_USER,
- sStatic.mDefaultAudioVideo);
- }
- }
- if (!found) {
- sStatic.mCategories.remove(removingCat);
- }
- dispatchRouteRemoved(info);
- }
- }
-
- void removeRouteAt(int routeIndex) {
- if (routeIndex >= 0 && routeIndex < sStatic.mRoutes.size()) {
- final RouteInfo info = sStatic.mRoutes.remove(routeIndex);
- final RouteCategory removingCat = info.getCategory();
- final int count = sStatic.mRoutes.size();
- boolean found = false;
- for (int i = 0; i < count; i++) {
- final RouteCategory cat = sStatic.mRoutes.get(i).getCategory();
- if (removingCat == cat) {
- found = true;
- break;
- }
- }
- if (info == sStatic.mSelectedRoute) {
- // Removing the currently selected route? Select the default before we remove it.
- // TODO: Be smarter about the route types here; this selects for all valid.
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO | ROUTE_TYPE_USER,
- sStatic.mDefaultAudioVideo);
+ selectDefaultRouteStatic();
}
if (!found) {
sStatic.mCategories.remove(removingCat);
@@ -752,7 +1109,7 @@
*
* @see #addUserRoute(UserRouteInfo)
* @see #removeUserRoute(UserRouteInfo)
- * @see #createRouteCategory(CharSequence)
+ * @see #createRouteCategory(CharSequence, boolean)
*/
public UserRouteInfo createUserRoute(RouteCategory category) {
return new UserRouteInfo(category);
@@ -780,6 +1137,23 @@
return new RouteCategory(nameResId, ROUTE_TYPE_USER, isGroupable);
}
+ /**
+ * Rebinds the media router to handle routes that belong to the specified user.
+ * Requires the interact across users permission to access the routes of another user.
+ * <p>
+ * This method is a complete hack to work around the singleton nature of the
+ * media router when running inside of singleton processes like QuickSettings.
+ * This mechanism should be burned to the ground when MediaRouter is redesigned.
+ * Ideally the current user would be pulled from the Context but we need to break
+ * down MediaRouter.Static before we can get there.
+ * </p>
+ *
+ * @hide
+ */
+ public void rebindAsUser(int userId) {
+ sStatic.rebindAsUser(userId);
+ }
+
static void updateRoute(final RouteInfo info) {
dispatchRouteChanged(info);
}
@@ -875,65 +1249,61 @@
}
}
- static void updateWifiDisplayStatus(WifiDisplayStatus newStatus) {
- final WifiDisplayStatus oldStatus = sStatic.mLastKnownWifiDisplayStatus;
-
- // TODO Naive implementation. Make this smarter later.
+ static void updateWifiDisplayStatus(WifiDisplayStatus status) {
boolean wantScan = false;
- boolean blockScan = false;
- WifiDisplay[] oldDisplays = oldStatus != null ?
- oldStatus.getDisplays() : WifiDisplay.EMPTY_ARRAY;
- WifiDisplay[] newDisplays;
+ WifiDisplay[] displays;
WifiDisplay activeDisplay;
- if (newStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
- newDisplays = newStatus.getDisplays();
- activeDisplay = newStatus.getActiveDisplay();
+ if (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
+ displays = status.getDisplays();
+ activeDisplay = status.getActiveDisplay();
} else {
- newDisplays = WifiDisplay.EMPTY_ARRAY;
+ displays = WifiDisplay.EMPTY_ARRAY;
activeDisplay = null;
}
- for (int i = 0; i < newDisplays.length; i++) {
- final WifiDisplay d = newDisplays[i];
- if (d.isRemembered()) {
+ // Add or update routes.
+ for (int i = 0; i < displays.length; i++) {
+ final WifiDisplay d = displays[i];
+ if (shouldShowWifiDisplay(d, activeDisplay)) {
RouteInfo route = findWifiDisplayRoute(d);
if (route == null) {
- route = makeWifiDisplayRoute(d, newStatus);
+ route = makeWifiDisplayRoute(d, status);
addRouteStatic(route);
wantScan = true;
} else {
- updateWifiDisplayRoute(route, d, newStatus);
+ updateWifiDisplayRoute(route, d, status);
}
if (d.equals(activeDisplay)) {
- selectRouteStatic(route.getSupportedTypes(), route);
-
- // Don't scan if we're already connected to a wifi display,
- // the scanning process can cause a hiccup with some configurations.
- blockScan = true;
- }
- }
- }
- for (int i = 0; i < oldDisplays.length; i++) {
- final WifiDisplay d = oldDisplays[i];
- if (d.isRemembered()) {
- final WifiDisplay newDisplay = findMatchingDisplay(d, newDisplays);
- if (newDisplay == null || !newDisplay.isRemembered()) {
- removeRoute(findWifiDisplayRoute(d));
+ selectRouteStatic(route.getSupportedTypes(), route, false);
}
}
}
- if (wantScan && !blockScan) {
+ // Remove stale routes.
+ for (int i = sStatic.mRoutes.size(); i-- > 0; ) {
+ RouteInfo route = sStatic.mRoutes.get(i);
+ if (route.mDeviceAddress != null) {
+ WifiDisplay d = findWifiDisplay(displays, route.mDeviceAddress);
+ if (d == null || !shouldShowWifiDisplay(d, activeDisplay)) {
+ removeRouteStatic(route);
+ }
+ }
+ }
+
+ // Don't scan if we're already connected to a wifi display,
+ // the scanning process can cause a hiccup with some configurations.
+ if (wantScan && activeDisplay != null) {
sStatic.mDisplayService.scanWifiDisplays();
}
+ }
- sStatic.mLastKnownWifiDisplayStatus = newStatus;
+ private static boolean shouldShowWifiDisplay(WifiDisplay d, WifiDisplay activeDisplay) {
+ return d.isRemembered() || d.equals(activeDisplay);
}
static int getWifiDisplayStatusCode(WifiDisplay d, WifiDisplayStatus wfdStatus) {
- int newStatus = RouteInfo.STATUS_NONE;
-
+ int newStatus;
if (wfdStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING) {
newStatus = RouteInfo.STATUS_SCANNING;
} else if (d.isAvailable()) {
@@ -947,7 +1317,7 @@
final int activeState = wfdStatus.getActiveDisplayState();
switch (activeState) {
case WifiDisplayStatus.DISPLAY_STATE_CONNECTED:
- newStatus = RouteInfo.STATUS_NONE;
+ newStatus = RouteInfo.STATUS_CONNECTED;
break;
case WifiDisplayStatus.DISPLAY_STATE_CONNECTING:
newStatus = RouteInfo.STATUS_CONNECTING;
@@ -968,18 +1338,17 @@
static RouteInfo makeWifiDisplayRoute(WifiDisplay display, WifiDisplayStatus wfdStatus) {
final RouteInfo newRoute = new RouteInfo(sStatic.mSystemCategory);
newRoute.mDeviceAddress = display.getDeviceAddress();
- newRoute.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
+ newRoute.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO
+ | ROUTE_TYPE_REMOTE_DISPLAY;
newRoute.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED;
newRoute.mPlaybackType = RouteInfo.PLAYBACK_TYPE_REMOTE;
- newRoute.setStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
+ newRoute.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
newRoute.mEnabled = isWifiDisplayEnabled(display, wfdStatus);
newRoute.mName = display.getFriendlyDisplayName();
newRoute.mDescription = sStatic.mResources.getText(
com.android.internal.R.string.wireless_display_route_description);
-
- newRoute.mPresentationDisplay = choosePresentationDisplayForRoute(newRoute,
- sStatic.getAllPresentationDisplays());
+ newRoute.updatePresentationDisplay();
return newRoute;
}
@@ -996,24 +1365,23 @@
changed |= route.mEnabled != enabled;
route.mEnabled = enabled;
- changed |= route.setStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
+ changed |= route.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus));
if (changed) {
dispatchRouteChanged(route);
}
- if (!enabled && route == sStatic.mSelectedRoute) {
+ if (!enabled && route.isSelected()) {
// Oops, no longer available. Reselect the default.
- final RouteInfo defaultRoute = sStatic.mDefaultAudioVideo;
- selectRouteStatic(defaultRoute.getSupportedTypes(), defaultRoute);
+ selectDefaultRouteStatic();
}
}
- private static WifiDisplay findMatchingDisplay(WifiDisplay d, WifiDisplay[] displays) {
+ private static WifiDisplay findWifiDisplay(WifiDisplay[] displays, String deviceAddress) {
for (int i = 0; i < displays.length; i++) {
- final WifiDisplay other = displays[i];
- if (d.hasSameAddress(other)) {
- return other;
+ final WifiDisplay d = displays[i];
+ if (d.getDeviceAddress().equals(deviceAddress)) {
+ return d;
}
}
return null;
@@ -1030,27 +1398,6 @@
return null;
}
- private static Display choosePresentationDisplayForRoute(RouteInfo route, Display[] displays) {
- if ((route.mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
- if (route.mDeviceAddress != null) {
- // Find the indicated Wifi display by its address.
- for (Display display : displays) {
- if (display.getType() == Display.TYPE_WIFI
- && route.mDeviceAddress.equals(display.getAddress())) {
- return display;
- }
- }
- return null;
- }
-
- if (route == sStatic.mDefaultAudioVideo && displays.length > 0) {
- // Choose the first presentation display from the list.
- return displays[0];
- }
- }
- return null;
- }
-
/**
* Information about a media route.
*/
@@ -1071,12 +1418,18 @@
int mPlaybackStream = AudioManager.STREAM_MUSIC;
VolumeCallbackInfo mVcb;
Display mPresentationDisplay;
+ int mPresentationDisplayId = -1;
String mDeviceAddress;
boolean mEnabled = true;
+ // An id by which the route is known to the media router service.
+ // Null if this route only exists as an artifact within this process.
+ String mGlobalRouteId;
+
// A predetermined connection status that can override mStatus
- private int mStatusCode;
+ private int mRealStatusCode;
+ private int mResolvedStatusCode;
/** @hide */ public static final int STATUS_NONE = 0;
/** @hide */ public static final int STATUS_SCANNING = 1;
@@ -1084,19 +1437,20 @@
/** @hide */ public static final int STATUS_AVAILABLE = 3;
/** @hide */ public static final int STATUS_NOT_AVAILABLE = 4;
/** @hide */ public static final int STATUS_IN_USE = 5;
+ /** @hide */ public static final int STATUS_CONNECTED = 6;
private Object mTag;
/**
* The default playback type, "local", indicating the presentation of the media is happening
* on the same device (e.g. a phone, a tablet) as where it is controlled from.
- * @see #setPlaybackType(int)
+ * @see #getPlaybackType()
*/
public final static int PLAYBACK_TYPE_LOCAL = 0;
/**
* A playback type indicating the presentation of the media is happening on
* a different device (i.e. the remote device) than where it is controlled from.
- * @see #setPlaybackType(int)
+ * @see #getPlaybackType()
*/
public final static int PLAYBACK_TYPE_REMOTE = 1;
/**
@@ -1104,12 +1458,13 @@
* controlled from this object. An example of fixed playback volume is a remote player,
* playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
* than attenuate at the source.
- * @see #setVolumeHandling(int)
+ * @see #getVolumeHandling()
*/
public final static int PLAYBACK_VOLUME_FIXED = 0;
/**
* Playback information indicating the playback volume is variable and can be controlled
* from this object.
+ * @see #getVolumeHandling()
*/
public final static int PLAYBACK_VOLUME_VARIABLE = 1;
@@ -1178,38 +1533,71 @@
* Set this route's status by predetermined status code. If the caller
* should dispatch a route changed event this call will return true;
*/
- boolean setStatusCode(int statusCode) {
- if (statusCode != mStatusCode) {
- mStatusCode = statusCode;
- int resId = 0;
- switch (statusCode) {
- case STATUS_SCANNING:
- resId = com.android.internal.R.string.media_route_status_scanning;
- break;
- case STATUS_CONNECTING:
- resId = com.android.internal.R.string.media_route_status_connecting;
- break;
- case STATUS_AVAILABLE:
- resId = com.android.internal.R.string.media_route_status_available;
- break;
- case STATUS_NOT_AVAILABLE:
- resId = com.android.internal.R.string.media_route_status_not_available;
- break;
- case STATUS_IN_USE:
- resId = com.android.internal.R.string.media_route_status_in_use;
- break;
- }
- mStatus = resId != 0 ? sStatic.mResources.getText(resId) : null;
- return true;
+ boolean setRealStatusCode(int statusCode) {
+ if (mRealStatusCode != statusCode) {
+ mRealStatusCode = statusCode;
+ return resolveStatusCode();
}
return false;
}
/**
+ * Resolves the status code whenever the real status code or selection state
+ * changes.
+ */
+ boolean resolveStatusCode() {
+ int statusCode = mRealStatusCode;
+ if (isSelected()) {
+ switch (statusCode) {
+ // If the route is selected and its status appears to be between states
+ // then report it as connecting even though it has not yet had a chance
+ // to officially move into the CONNECTING state. Note that routes in
+ // the NONE state are assumed to not require an explicit connection
+ // lifecycle whereas those that are AVAILABLE are assumed to have
+ // to eventually proceed to CONNECTED.
+ case STATUS_AVAILABLE:
+ case STATUS_SCANNING:
+ statusCode = STATUS_CONNECTING;
+ break;
+ }
+ }
+ if (mResolvedStatusCode == statusCode) {
+ return false;
+ }
+
+ mResolvedStatusCode = statusCode;
+ int resId;
+ switch (statusCode) {
+ case STATUS_SCANNING:
+ resId = com.android.internal.R.string.media_route_status_scanning;
+ break;
+ case STATUS_CONNECTING:
+ resId = com.android.internal.R.string.media_route_status_connecting;
+ break;
+ case STATUS_AVAILABLE:
+ resId = com.android.internal.R.string.media_route_status_available;
+ break;
+ case STATUS_NOT_AVAILABLE:
+ resId = com.android.internal.R.string.media_route_status_not_available;
+ break;
+ case STATUS_IN_USE:
+ resId = com.android.internal.R.string.media_route_status_in_use;
+ break;
+ case STATUS_CONNECTED:
+ case STATUS_NONE:
+ default:
+ resId = 0;
+ break;
+ }
+ mStatus = resId != 0 ? sStatic.mResources.getText(resId) : null;
+ return true;
+ }
+
+ /**
* @hide
*/
public int getStatusCode() {
- return mStatusCode;
+ return mResolvedStatusCode;
}
/**
@@ -1219,6 +1607,11 @@
return mSupportedTypes;
}
+ /** @hide */
+ public boolean matchesTypes(int types) {
+ return (mSupportedTypes & types) != 0;
+ }
+
/**
* @return The group that this route belongs to.
*/
@@ -1317,9 +1710,7 @@
Log.e(TAG, "Error setting local stream volume", e);
}
} else {
- Log.e(TAG, getClass().getSimpleName() + ".requestSetVolume(): " +
- "Non-local volume playback on system route? " +
- "Could not request volume change.");
+ sStatic.requestSetVolume(this, volume);
}
}
@@ -1338,9 +1729,7 @@
Log.e(TAG, "Error setting local stream volume", e);
}
} else {
- Log.e(TAG, getClass().getSimpleName() + ".requestChangeVolume(): " +
- "Non-local volume playback on system route? " +
- "Could not request volume change.");
+ sStatic.requestUpdateVolume(this, direction);
}
}
@@ -1402,6 +1791,55 @@
return mPresentationDisplay;
}
+ boolean updatePresentationDisplay() {
+ Display display = choosePresentationDisplay();
+ if (mPresentationDisplay != display) {
+ mPresentationDisplay = display;
+ return true;
+ }
+ return false;
+ }
+
+ private Display choosePresentationDisplay() {
+ if ((mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
+ Display[] displays = sStatic.getAllPresentationDisplays();
+
+ // Ensure that the specified display is valid for presentations.
+ // This check will normally disallow the default display unless it was
+ // configured as a presentation display for some reason.
+ if (mPresentationDisplayId >= 0) {
+ for (Display display : displays) {
+ if (display.getDisplayId() == mPresentationDisplayId) {
+ return display;
+ }
+ }
+ return null;
+ }
+
+ // Find the indicated Wifi display by its address.
+ if (mDeviceAddress != null) {
+ for (Display display : displays) {
+ if (display.getType() == Display.TYPE_WIFI
+ && mDeviceAddress.equals(display.getAddress())) {
+ return display;
+ }
+ }
+ return null;
+ }
+
+ // For the default route, choose the first presentation display from the list.
+ if (this == sStatic.mDefaultAudioVideo && displays.length > 0) {
+ return displays[0];
+ }
+ }
+ return null;
+ }
+
+ /** @hide */
+ public String getDeviceAddress() {
+ return mDeviceAddress;
+ }
+
/**
* Returns true if this route is enabled and may be selected.
*
@@ -1418,7 +1856,22 @@
* @return True if this route is in the process of connecting.
*/
public boolean isConnecting() {
- return mStatusCode == STATUS_CONNECTING;
+ return mResolvedStatusCode == STATUS_CONNECTING;
+ }
+
+ /** @hide */
+ public boolean isSelected() {
+ return this == sStatic.mSelectedRoute;
+ }
+
+ /** @hide */
+ public boolean isDefault() {
+ return this == sStatic.mDefaultAudioVideo;
+ }
+
+ /** @hide */
+ public void select() {
+ selectRouteStatic(mSupportedTypes, this, true);
}
void setStatusInt(CharSequence status) {
@@ -1432,6 +1885,7 @@
}
final IRemoteVolumeObserver.Stub mRemoteVolObserver = new IRemoteVolumeObserver.Stub() {
+ @Override
public void dispatchRemoteVolumeUpdate(final int direction, final int value) {
sStatic.mHandler.post(new Runnable() {
@Override
@@ -1460,7 +1914,7 @@
", status=" + getStatus() +
", category=" + getCategory() +
", supportedTypes=" + supportedTypes +
- ", presentationDisplay=" + mPresentationDisplay + "}";
+ ", presentationDisplay=" + mPresentationDisplay + " }";
}
}
@@ -1716,6 +2170,7 @@
mVolumeHandling = PLAYBACK_VOLUME_FIXED;
}
+ @Override
CharSequence getName(Resources res) {
if (mUpdateName) updateName();
return super.getName(res);
@@ -1916,7 +2371,7 @@
final int count = mRoutes.size();
if (count == 0) {
// Don't keep empty groups in the router.
- MediaRouter.removeRoute(this);
+ MediaRouter.removeRouteStatic(this);
return;
}
@@ -2071,6 +2526,7 @@
return mIsSystem;
}
+ @Override
public String toString() {
return "RouteCategory{ name=" + mName + " types=" + typesToString(mTypes) +
" groupable=" + mGroupable + " }";
diff --git a/media/java/android/media/MediaRouterClientState.aidl b/media/java/android/media/MediaRouterClientState.aidl
new file mode 100644
index 0000000..70077119
--- /dev/null
+++ b/media/java/android/media/MediaRouterClientState.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media;
+
+parcelable MediaRouterClientState;
diff --git a/media/java/android/media/MediaRouterClientState.java b/media/java/android/media/MediaRouterClientState.java
new file mode 100644
index 0000000..0847503
--- /dev/null
+++ b/media/java/android/media/MediaRouterClientState.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+
+/**
+ * Information available from MediaRouterService about the state perceived by
+ * a particular client and the routes that are available to it.
+ *
+ * Clients must not modify the contents of this object.
+ * @hide
+ */
+public final class MediaRouterClientState implements Parcelable {
+ /**
+ * A list of all known routes.
+ */
+ public final ArrayList<RouteInfo> routes;
+
+ /**
+ * The id of the current globally selected route, or null if none.
+ * Globally selected routes override any other route selections that applications
+ * may have made. Used for remote displays.
+ */
+ public String globallySelectedRouteId;
+
+ public MediaRouterClientState() {
+ routes = new ArrayList<RouteInfo>();
+ }
+
+ MediaRouterClientState(Parcel src) {
+ routes = src.createTypedArrayList(RouteInfo.CREATOR);
+ globallySelectedRouteId = src.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedList(routes);
+ dest.writeString(globallySelectedRouteId);
+ }
+
+ public static final Parcelable.Creator<MediaRouterClientState> CREATOR =
+ new Parcelable.Creator<MediaRouterClientState>() {
+ @Override
+ public MediaRouterClientState createFromParcel(Parcel in) {
+ return new MediaRouterClientState(in);
+ }
+
+ @Override
+ public MediaRouterClientState[] newArray(int size) {
+ return new MediaRouterClientState[size];
+ }
+ };
+
+ public static final class RouteInfo implements Parcelable {
+ public String id;
+ public String name;
+ public String description;
+ public int supportedTypes;
+ public boolean enabled;
+ public int statusCode;
+ public int playbackType;
+ public int playbackStream;
+ public int volume;
+ public int volumeMax;
+ public int volumeHandling;
+ public int presentationDisplayId;
+
+ public RouteInfo(String id) {
+ this.id = id;
+ enabled = true;
+ statusCode = MediaRouter.RouteInfo.STATUS_NONE;
+ playbackType = MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
+ playbackStream = -1;
+ volumeHandling = MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED;
+ presentationDisplayId = -1;
+ }
+
+ public RouteInfo(RouteInfo other) {
+ id = other.id;
+ name = other.name;
+ description = other.description;
+ supportedTypes = other.supportedTypes;
+ enabled = other.enabled;
+ statusCode = other.statusCode;
+ playbackType = other.playbackType;
+ playbackStream = other.playbackStream;
+ volume = other.volume;
+ volumeMax = other.volumeMax;
+ volumeHandling = other.volumeHandling;
+ presentationDisplayId = other.presentationDisplayId;
+ }
+
+ RouteInfo(Parcel in) {
+ id = in.readString();
+ name = in.readString();
+ description = in.readString();
+ supportedTypes = in.readInt();
+ enabled = in.readInt() != 0;
+ statusCode = in.readInt();
+ playbackType = in.readInt();
+ playbackStream = in.readInt();
+ volume = in.readInt();
+ volumeMax = in.readInt();
+ volumeHandling = in.readInt();
+ presentationDisplayId = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(id);
+ dest.writeString(name);
+ dest.writeString(description);
+ dest.writeInt(supportedTypes);
+ dest.writeInt(enabled ? 1 : 0);
+ dest.writeInt(statusCode);
+ dest.writeInt(playbackType);
+ dest.writeInt(playbackStream);
+ dest.writeInt(volume);
+ dest.writeInt(volumeMax);
+ dest.writeInt(volumeHandling);
+ dest.writeInt(presentationDisplayId);
+ }
+
+ @Override
+ public String toString() {
+ return "RouteInfo{ id=" + id
+ + ", name=" + name
+ + ", description=" + description
+ + ", supportedTypes=0x" + Integer.toHexString(supportedTypes)
+ + ", enabled=" + enabled
+ + ", statusCode=" + statusCode
+ + ", playbackType=" + playbackType
+ + ", playbackStream=" + playbackStream
+ + ", volume=" + volume
+ + ", volumeMax=" + volumeMax
+ + ", volumeHandling=" + volumeHandling
+ + ", presentationDisplayId=" + presentationDisplayId
+ + " }";
+ }
+
+ @SuppressWarnings("hiding")
+ public static final Parcelable.Creator<RouteInfo> CREATOR =
+ new Parcelable.Creator<RouteInfo>() {
+ @Override
+ public RouteInfo createFromParcel(Parcel in) {
+ return new RouteInfo(in);
+ }
+
+ @Override
+ public RouteInfo[] newArray(int size) {
+ return new RouteInfo[size];
+ }
+ };
+ }
+}
diff --git a/media/java/android/media/RemoteDisplayState.aidl b/media/java/android/media/RemoteDisplayState.aidl
new file mode 100644
index 0000000..b3262fc
--- /dev/null
+++ b/media/java/android/media/RemoteDisplayState.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media;
+
+parcelable RemoteDisplayState;
diff --git a/media/java/android/media/RemoteDisplayState.java b/media/java/android/media/RemoteDisplayState.java
new file mode 100644
index 0000000..1197f65
--- /dev/null
+++ b/media/java/android/media/RemoteDisplayState.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+
+/**
+ * Information available from IRemoteDisplayProvider about available remote displays.
+ *
+ * Clients must not modify the contents of this object.
+ * @hide
+ */
+public final class RemoteDisplayState implements Parcelable {
+ // Note: These constants are used by the remote display provider API.
+ // Do not change them!
+ public static final String SERVICE_INTERFACE =
+ "com.android.media.remotedisplay.RemoteDisplayProvider";
+ public static final int DISCOVERY_MODE_NONE = 0;
+ public static final int DISCOVERY_MODE_PASSIVE = 1;
+ public static final int DISCOVERY_MODE_ACTIVE = 2;
+
+ /**
+ * A list of all remote displays.
+ */
+ public final ArrayList<RemoteDisplayInfo> displays;
+
+ public RemoteDisplayState() {
+ displays = new ArrayList<RemoteDisplayInfo>();
+ }
+
+ RemoteDisplayState(Parcel src) {
+ displays = src.createTypedArrayList(RemoteDisplayInfo.CREATOR);
+ }
+
+ public boolean isValid() {
+ if (displays == null) {
+ return false;
+ }
+ final int count = displays.size();
+ for (int i = 0; i < count; i++) {
+ if (!displays.get(i).isValid()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedList(displays);
+ }
+
+ public static final Parcelable.Creator<RemoteDisplayState> CREATOR =
+ new Parcelable.Creator<RemoteDisplayState>() {
+ @Override
+ public RemoteDisplayState createFromParcel(Parcel in) {
+ return new RemoteDisplayState(in);
+ }
+
+ @Override
+ public RemoteDisplayState[] newArray(int size) {
+ return new RemoteDisplayState[size];
+ }
+ };
+
+ public static final class RemoteDisplayInfo implements Parcelable {
+ // Note: These constants are used by the remote display provider API.
+ // Do not change them!
+ public static final int STATUS_NOT_AVAILABLE = 0;
+ public static final int STATUS_IN_USE = 1;
+ public static final int STATUS_AVAILABLE = 2;
+ public static final int STATUS_CONNECTING = 3;
+ public static final int STATUS_CONNECTED = 4;
+
+ public static final int PLAYBACK_VOLUME_VARIABLE =
+ MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
+ public static final int PLAYBACK_VOLUME_FIXED =
+ MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED;
+
+ public String id;
+ public String name;
+ public String description;
+ public int status;
+ public int volume;
+ public int volumeMax;
+ public int volumeHandling;
+ public int presentationDisplayId;
+
+ public RemoteDisplayInfo(String id) {
+ this.id = id;
+ status = STATUS_NOT_AVAILABLE;
+ volumeHandling = MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED;
+ presentationDisplayId = -1;
+ }
+
+ public RemoteDisplayInfo(RemoteDisplayInfo other) {
+ id = other.id;
+ name = other.name;
+ description = other.description;
+ status = other.status;
+ volume = other.volume;
+ volumeMax = other.volumeMax;
+ volumeHandling = other.volumeHandling;
+ presentationDisplayId = other.presentationDisplayId;
+ }
+
+ RemoteDisplayInfo(Parcel in) {
+ id = in.readString();
+ name = in.readString();
+ description = in.readString();
+ status = in.readInt();
+ volume = in.readInt();
+ volumeMax = in.readInt();
+ volumeHandling = in.readInt();
+ presentationDisplayId = in.readInt();
+ }
+
+ public boolean isValid() {
+ return !TextUtils.isEmpty(id) && !TextUtils.isEmpty(name);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(id);
+ dest.writeString(name);
+ dest.writeString(description);
+ dest.writeInt(status);
+ dest.writeInt(volume);
+ dest.writeInt(volumeMax);
+ dest.writeInt(volumeHandling);
+ dest.writeInt(presentationDisplayId);
+ }
+
+ @Override
+ public String toString() {
+ return "RemoteDisplayInfo{ id=" + id
+ + ", name=" + name
+ + ", description=" + description
+ + ", status=" + status
+ + ", volume=" + volume
+ + ", volumeMax=" + volumeMax
+ + ", volumeHandling=" + volumeHandling
+ + ", presentationDisplayId=" + presentationDisplayId
+ + " }";
+ }
+
+ @SuppressWarnings("hiding")
+ public static final Parcelable.Creator<RemoteDisplayInfo> CREATOR =
+ new Parcelable.Creator<RemoteDisplayInfo>() {
+ @Override
+ public RemoteDisplayInfo createFromParcel(Parcel in) {
+ return new RemoteDisplayInfo(in);
+ }
+
+ @Override
+ public RemoteDisplayInfo[] newArray(int size) {
+ return new RemoteDisplayInfo[size];
+ }
+ };
+ }
+}
diff --git a/media/lib/Android.mk b/media/lib/Android.mk
new file mode 100644
index 0000000..50799a6
--- /dev/null
+++ b/media/lib/Android.mk
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+# the library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= com.android.media.remotedisplay
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ $(call all-subdir-java-files) \
+ $(call all-aidl-files-under, java)
+
+include $(BUILD_JAVA_LIBRARY)
+
+
+# ==== com.android.media.remotedisplay.xml lib def ========================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := com.android.media.remotedisplay.xml
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_CLASS := ETC
+
+# This will install the file in /system/etc/permissions
+#
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
+
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+
+include $(BUILD_PREBUILT)
diff --git a/media/lib/README.txt b/media/lib/README.txt
new file mode 100644
index 0000000..cade3df
--- /dev/null
+++ b/media/lib/README.txt
@@ -0,0 +1,28 @@
+This library (com.android.media.remotedisplay.jar) is a shared java library
+containing classes required by unbundled remote display providers.
+
+--- Rules of this library ---
+o This library is effectively a PUBLIC API for unbundled remote display providers
+ that may be distributed outside the system image. So it MUST BE API STABLE.
+ You can add but not remove. The rules are the same as for the
+ public platform SDK API.
+o This library can see and instantiate internal platform classes, but it must not
+ expose them in any public method (or by extending them via inheritance). This would
+ break clients of the library because they cannot see the internal platform classes.
+
+This library is distributed in the system image, and loaded as
+a shared library. So you can change the implementation, but not
+the interface. In this way it is like framework.jar.
+
+--- Why does this library exists? ---
+
+Unbundled remote display providers (such as Cast) cannot use internal
+platform classes.
+
+This library will eventually be replaced when the media route provider
+infrastructure that is currently defined in the support library is reintegrated
+with the framework in a new API. That API isn't ready yet so this
+library is a compromise to make new capabilities available to the system
+without exposing the full surface area of the support library media
+route provider protocol.
+
diff --git a/media/lib/com.android.media.remotedisplay.xml b/media/lib/com.android.media.remotedisplay.xml
new file mode 100644
index 0000000..77a91d2
--- /dev/null
+++ b/media/lib/com.android.media.remotedisplay.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<permissions>
+ <library name="com.android.media.remotedisplay"
+ file="/system/framework/com.android.media.remotedisplay.jar" />
+</permissions>
diff --git a/media/lib/java/com/android/media/remotedisplay/RemoteDisplay.java b/media/lib/java/com/android/media/remotedisplay/RemoteDisplay.java
new file mode 100644
index 0000000..5e15702
--- /dev/null
+++ b/media/lib/java/com/android/media/remotedisplay/RemoteDisplay.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media.remotedisplay;
+
+import com.android.internal.util.Objects;
+
+import android.media.MediaRouter;
+import android.media.RemoteDisplayState.RemoteDisplayInfo;
+import android.text.TextUtils;
+
+/**
+ * Represents a remote display that has been discovered.
+ */
+public class RemoteDisplay {
+ private final RemoteDisplayInfo mMutableInfo;
+ private RemoteDisplayInfo mImmutableInfo;
+
+ /**
+ * Status code: Indicates that the remote display is not available.
+ */
+ public static final int STATUS_NOT_AVAILABLE = RemoteDisplayInfo.STATUS_NOT_AVAILABLE;
+
+ /**
+ * Status code: Indicates that the remote display is in use by someone else.
+ */
+ public static final int STATUS_IN_USE = RemoteDisplayInfo.STATUS_IN_USE;
+
+ /**
+ * Status code: Indicates that the remote display is available for new connections.
+ */
+ public static final int STATUS_AVAILABLE = RemoteDisplayInfo.STATUS_AVAILABLE;
+
+ /**
+ * Status code: Indicates that the remote display is current connecting.
+ */
+ public static final int STATUS_CONNECTING = RemoteDisplayInfo.STATUS_CONNECTING;
+
+ /**
+ * Status code: Indicates that the remote display is connected and is mirroring
+ * display contents.
+ */
+ public static final int STATUS_CONNECTED = RemoteDisplayInfo.STATUS_CONNECTED;
+
+ /**
+ * Volume handling: Output volume can be changed.
+ */
+ public static final int PLAYBACK_VOLUME_VARIABLE =
+ RemoteDisplayInfo.PLAYBACK_VOLUME_VARIABLE;
+
+ /**
+ * Volume handling: Output volume is fixed.
+ */
+ public static final int PLAYBACK_VOLUME_FIXED =
+ RemoteDisplayInfo.PLAYBACK_VOLUME_FIXED;
+
+ /**
+ * Creates a remote display with the specified name and id.
+ */
+ public RemoteDisplay(String id, String name) {
+ if (TextUtils.isEmpty(id)) {
+ throw new IllegalArgumentException("id must not be null or empty");
+ }
+ mMutableInfo = new RemoteDisplayInfo(id);
+ setName(name);
+ }
+
+ public String getId() {
+ return mMutableInfo.id;
+ }
+
+ public String getName() {
+ return mMutableInfo.name;
+ }
+
+ public void setName(String name) {
+ if (!Objects.equal(mMutableInfo.name, name)) {
+ mMutableInfo.name = name;
+ mImmutableInfo = null;
+ }
+ }
+
+ public String getDescription() {
+ return mMutableInfo.description;
+ }
+
+ public void setDescription(String description) {
+ if (!Objects.equal(mMutableInfo.description, description)) {
+ mMutableInfo.description = description;
+ mImmutableInfo = null;
+ }
+ }
+
+ public int getStatus() {
+ return mMutableInfo.status;
+ }
+
+ public void setStatus(int status) {
+ if (mMutableInfo.status != status) {
+ mMutableInfo.status = status;
+ mImmutableInfo = null;
+ }
+ }
+
+ public int getVolume() {
+ return mMutableInfo.volume;
+ }
+
+ public void setVolume(int volume) {
+ if (mMutableInfo.volume != volume) {
+ mMutableInfo.volume = volume;
+ mImmutableInfo = null;
+ }
+ }
+
+ public int getVolumeMax() {
+ return mMutableInfo.volumeMax;
+ }
+
+ public void setVolumeMax(int volumeMax) {
+ if (mMutableInfo.volumeMax != volumeMax) {
+ mMutableInfo.volumeMax = volumeMax;
+ mImmutableInfo = null;
+ }
+ }
+
+ public int getVolumeHandling() {
+ return mMutableInfo.volumeHandling;
+ }
+
+ public void setVolumeHandling(int volumeHandling) {
+ if (mMutableInfo.volumeHandling != volumeHandling) {
+ mMutableInfo.volumeHandling = volumeHandling;
+ mImmutableInfo = null;
+ }
+ }
+
+ public int getPresentationDisplayId() {
+ return mMutableInfo.presentationDisplayId;
+ }
+
+ public void setPresentationDisplayId(int presentationDisplayId) {
+ if (mMutableInfo.presentationDisplayId != presentationDisplayId) {
+ mMutableInfo.presentationDisplayId = presentationDisplayId;
+ mImmutableInfo = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "RemoteDisplay{" + mMutableInfo.toString() + "}";
+ }
+
+ RemoteDisplayInfo getInfo() {
+ if (mImmutableInfo == null) {
+ mImmutableInfo = new RemoteDisplayInfo(mMutableInfo);
+ }
+ return mImmutableInfo;
+ }
+}
diff --git a/media/lib/java/com/android/media/remotedisplay/RemoteDisplayProvider.java b/media/lib/java/com/android/media/remotedisplay/RemoteDisplayProvider.java
new file mode 100644
index 0000000..e2df77c
--- /dev/null
+++ b/media/lib/java/com/android/media/remotedisplay/RemoteDisplayProvider.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media.remotedisplay;
+
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.media.IRemoteDisplayCallback;
+import android.media.IRemoteDisplayProvider;
+import android.media.RemoteDisplayState;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.util.ArrayMap;
+
+import java.util.Collection;
+
+/**
+ * Base class for remote display providers implemented as unbundled services.
+ * <p>
+ * To implement your remote display provider service, create a subclass of
+ * {@link Service} and override the {@link Service#onBind Service.onBind()} method
+ * to return the provider's binder when the {@link #SERVICE_INTERFACE} is requested.
+ * </p>
+ * <pre>
+ * public class SampleRemoteDisplayProviderService extends Service {
+ * private SampleProvider mProvider;
+ *
+ * public IBinder onBind(Intent intent) {
+ * if (intent.getAction().equals(RemoteDisplayProvider.SERVICE_INTERFACE)) {
+ * if (mProvider == null) {
+ * mProvider = new SampleProvider(this);
+ * }
+ * return mProvider.getBinder();
+ * }
+ * return null;
+ * }
+ *
+ * class SampleProvider extends RemoteDisplayProvider {
+ * public SampleProvider() {
+ * super(SampleRemoteDisplayProviderService.this);
+ * }
+ *
+ * // --- Implementation goes here ---
+ * }
+ * }
+ * </pre>
+ * <p>
+ * Declare your remote display provider service in your application manifest
+ * like this:
+ * </p>
+ * <pre>
+ * <application>
+ * <uses-library android:name="com.android.media.remotedisplay" />
+ *
+ * <service android:name=".SampleRemoteDisplayProviderService"
+ * android:label="@string/sample_remote_display_provider_service"
+ * android:exported="true"
+ * android:permission="android.permission.BIND_REMOTE_DISPLAY">
+ * <intent-filter>
+ * <action android:name="com.android.media.remotedisplay.RemoteDisplayProvider" />
+ * </intent-filter>
+ * </service>
+ * </application>
+ * </pre>
+ * <p>
+ * This object is not thread safe. It is only intended to be accessed on the
+ * {@link Context#getMainLooper main looper thread} of an application.
+ * </p><p>
+ * IMPORTANT: This class is effectively a public API for unbundled applications, and
+ * must remain API stable. See README.txt in the root of this package for more information.
+ * </p>
+ */
+public abstract class RemoteDisplayProvider {
+ private static final int MSG_SET_CALLBACK = 1;
+ private static final int MSG_SET_DISCOVERY_MODE = 2;
+ private static final int MSG_CONNECT = 3;
+ private static final int MSG_DISCONNECT = 4;
+ private static final int MSG_SET_VOLUME = 5;
+ private static final int MSG_ADJUST_VOLUME = 6;
+
+ private final Context mContext;
+ private final ProviderStub mStub;
+ private final ProviderHandler mHandler;
+ private final ArrayMap<String, RemoteDisplay> mDisplays =
+ new ArrayMap<String, RemoteDisplay>();
+ private IRemoteDisplayCallback mCallback;
+ private int mDiscoveryMode = DISCOVERY_MODE_NONE;
+
+ private PendingIntent mSettingsPendingIntent;
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ * Put this in your manifest.
+ */
+ public static final String SERVICE_INTERFACE = RemoteDisplayState.SERVICE_INTERFACE;
+
+ /**
+ * Discovery mode: Do not perform any discovery.
+ */
+ public static final int DISCOVERY_MODE_NONE = RemoteDisplayState.DISCOVERY_MODE_NONE;
+
+ /**
+ * Discovery mode: Passive or low-power periodic discovery.
+ * <p>
+ * This mode indicates that an application is interested in knowing whether there
+ * are any remote displays paired or available but doesn't need the latest or
+ * most detailed information. The provider may scan at a lower rate or rely on
+ * knowledge of previously paired devices.
+ * </p>
+ */
+ public static final int DISCOVERY_MODE_PASSIVE = RemoteDisplayState.DISCOVERY_MODE_PASSIVE;
+
+ /**
+ * Discovery mode: Active discovery.
+ * <p>
+ * This mode indicates that the user is actively trying to connect to a route
+ * and we should perform continuous scans. This mode may use significantly more
+ * power but is intended to be short-lived.
+ * </p>
+ */
+ public static final int DISCOVERY_MODE_ACTIVE = RemoteDisplayState.DISCOVERY_MODE_ACTIVE;
+
+ /**
+ * Creates a remote display provider.
+ *
+ * @param context The application context for the remote display provider.
+ */
+ public RemoteDisplayProvider(Context context) {
+ mContext = context;
+ mStub = new ProviderStub();
+ mHandler = new ProviderHandler(context.getMainLooper());
+ }
+
+ /**
+ * Gets the context of the remote display provider.
+ */
+ public final Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * Gets the Binder associated with the provider.
+ * <p>
+ * This is intended to be used for the onBind() method of a service that implements
+ * a remote display provider service.
+ * </p>
+ *
+ * @return The IBinder instance associated with the provider.
+ */
+ public IBinder getBinder() {
+ return mStub;
+ }
+
+ /**
+ * Called when the current discovery mode changes.
+ *
+ * @param mode The new discovery mode.
+ */
+ public void onDiscoveryModeChanged(int mode) {
+ }
+
+ /**
+ * Called when the system would like to connect to a display.
+ *
+ * @param display The remote display.
+ */
+ public void onConnect(RemoteDisplay display) {
+ }
+
+ /**
+ * Called when the system would like to disconnect from a display.
+ *
+ * @param display The remote display.
+ */
+ public void onDisconnect(RemoteDisplay display) {
+ }
+
+ /**
+ * Called when the system would like to set the volume of a display.
+ *
+ * @param display The remote display.
+ * @param volume The desired volume.
+ */
+ public void onSetVolume(RemoteDisplay display, int volume) {
+ }
+
+ /**
+ * Called when the system would like to adjust the volume of a display.
+ *
+ * @param display The remote display.
+ * @param delta An increment to add to the current volume, such as +1 or -1.
+ */
+ public void onAdjustVolume(RemoteDisplay display, int delta) {
+ }
+
+ /**
+ * Gets the current discovery mode.
+ *
+ * @return The current discovery mode.
+ */
+ public int getDiscoveryMode() {
+ return mDiscoveryMode;
+ }
+
+ /**
+ * Gets the current collection of displays.
+ *
+ * @return The current collection of displays, which must not be modified.
+ */
+ public Collection<RemoteDisplay> getDisplays() {
+ return mDisplays.values();
+ }
+
+ /**
+ * Adds the specified remote display and notifies the system.
+ *
+ * @param display The remote display that was added.
+ * @throws IllegalStateException if there is already a display with the same id.
+ */
+ public void addDisplay(RemoteDisplay display) {
+ if (display == null || mDisplays.containsKey(display.getId())) {
+ throw new IllegalArgumentException("display");
+ }
+ mDisplays.put(display.getId(), display);
+ publishState();
+ }
+
+ /**
+ * Updates information about the specified remote display and notifies the system.
+ *
+ * @param display The remote display that was added.
+ * @throws IllegalStateException if the display was n
+ */
+ public void updateDisplay(RemoteDisplay display) {
+ if (display == null || mDisplays.get(display.getId()) != display) {
+ throw new IllegalArgumentException("display");
+ }
+ publishState();
+ }
+
+ /**
+ * Removes the specified remote display and tells the system about it.
+ *
+ * @param display The remote display that was removed.
+ */
+ public void removeDisplay(RemoteDisplay display) {
+ if (display == null || mDisplays.get(display.getId()) != display) {
+ throw new IllegalArgumentException("display");
+ }
+ mDisplays.remove(display.getId());
+ publishState();
+ }
+
+ /**
+ * Finds the remote display with the specified id, returns null if not found.
+ *
+ * @param id Id of the remote display.
+ * @return The display, or null if none.
+ */
+ public RemoteDisplay findRemoteDisplay(String id) {
+ return mDisplays.get(id);
+ }
+
+ /**
+ * Gets a pending intent to launch the remote display settings activity.
+ *
+ * @return A pending intent to launch the settings activity.
+ */
+ public PendingIntent getSettingsPendingIntent() {
+ if (mSettingsPendingIntent == null) {
+ Intent settingsIntent = new Intent(Settings.ACTION_WIFI_DISPLAY_SETTINGS);
+ settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mSettingsPendingIntent = PendingIntent.getActivity(
+ mContext, 0, settingsIntent, 0, null);
+ }
+ return mSettingsPendingIntent;
+ }
+
+ void setCallback(IRemoteDisplayCallback callback) {
+ mCallback = callback;
+ publishState();
+ }
+
+ void setDiscoveryMode(int mode) {
+ if (mDiscoveryMode != mode) {
+ mDiscoveryMode = mode;
+ onDiscoveryModeChanged(mode);
+ }
+ }
+
+ void publishState() {
+ if (mCallback != null) {
+ RemoteDisplayState state = new RemoteDisplayState();
+ final int count = mDisplays.size();
+ for (int i = 0; i < count; i++) {
+ final RemoteDisplay display = mDisplays.valueAt(i);
+ state.displays.add(display.getInfo());
+ }
+ try {
+ mCallback.onStateChanged(state);
+ } catch (RemoteException ex) {
+ // system server died?
+ }
+ }
+ }
+
+ final class ProviderStub extends IRemoteDisplayProvider.Stub {
+ @Override
+ public void setCallback(IRemoteDisplayCallback callback) {
+ mHandler.obtainMessage(MSG_SET_CALLBACK, callback).sendToTarget();
+ }
+
+ @Override
+ public void setDiscoveryMode(int mode) {
+ mHandler.obtainMessage(MSG_SET_DISCOVERY_MODE, mode, 0).sendToTarget();
+ }
+
+ @Override
+ public void connect(String id) {
+ mHandler.obtainMessage(MSG_CONNECT, id).sendToTarget();
+ }
+
+ @Override
+ public void disconnect(String id) {
+ mHandler.obtainMessage(MSG_DISCONNECT, id).sendToTarget();
+ }
+
+ @Override
+ public void setVolume(String id, int volume) {
+ mHandler.obtainMessage(MSG_SET_VOLUME, volume, 0, id).sendToTarget();
+ }
+
+ @Override
+ public void adjustVolume(String id, int delta) {
+ mHandler.obtainMessage(MSG_ADJUST_VOLUME, delta, 0, id).sendToTarget();
+ }
+ }
+
+ final class ProviderHandler extends Handler {
+ public ProviderHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_CALLBACK: {
+ setCallback((IRemoteDisplayCallback)msg.obj);
+ break;
+ }
+ case MSG_SET_DISCOVERY_MODE: {
+ setDiscoveryMode(msg.arg1);
+ break;
+ }
+ case MSG_CONNECT: {
+ RemoteDisplay display = findRemoteDisplay((String)msg.obj);
+ if (display != null) {
+ onConnect(display);
+ }
+ break;
+ }
+ case MSG_DISCONNECT: {
+ RemoteDisplay display = findRemoteDisplay((String)msg.obj);
+ if (display != null) {
+ onDisconnect(display);
+ }
+ break;
+ }
+ case MSG_SET_VOLUME: {
+ RemoteDisplay display = findRemoteDisplay((String)msg.obj);
+ if (display != null) {
+ onSetVolume(display, msg.arg1);
+ }
+ break;
+ }
+ case MSG_ADJUST_VOLUME: {
+ RemoteDisplay display = findRemoteDisplay((String)msg.obj);
+ if (display != null) {
+ onAdjustVolume(display, msg.arg1);
+ }
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/packages/ExternalStorageProvider/AndroidManifest.xml b/packages/ExternalStorageProvider/AndroidManifest.xml
index 5169fef..edd6255 100644
--- a/packages/ExternalStorageProvider/AndroidManifest.xml
+++ b/packages/ExternalStorageProvider/AndroidManifest.xml
@@ -3,6 +3,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
<application android:label="@string/app_label">
<provider
diff --git a/packages/Keyguard/AndroidManifest.xml b/packages/Keyguard/AndroidManifest.xml
index 9e296e2..66d1e75 100644
--- a/packages/Keyguard/AndroidManifest.xml
+++ b/packages/Keyguard/AndroidManifest.xml
@@ -38,6 +38,7 @@
<uses-permission android:name="android.permission.BIND_DEVICE_ADMIN" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
+ <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
<application android:label="@string/app_name"
android:process="com.android.systemui"
diff --git a/packages/Keyguard/res/layout/keyguard_presentation.xml b/packages/Keyguard/res/layout/keyguard_presentation.xml
new file mode 100644
index 0000000..7df0b70
--- /dev/null
+++ b/packages/Keyguard/res/layout/keyguard_presentation.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is a view that shows general status information in Keyguard. -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
+ android:id="@+id/presentation"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.android.keyguard.KeyguardStatusView
+ android:id="@+id/clock"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/keyguard_accessibility_status">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|top"
+ android:orientation="vertical"
+ android:focusable="true">
+ <TextClock
+ android:id="@+id/clock_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|top"
+ android:textColor="@color/clock_white"
+ android:singleLine="true"
+ style="@style/widget_big_thin"
+ android:format12Hour="@string/keyguard_widget_12_hours_format"
+ android:format24Hour="@string/keyguard_widget_24_hours_format"
+ android:baselineAligned="true"
+ android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
+
+ <include layout="@layout/keyguard_status_area" />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:layout_gravity="center_horizontal"
+ android:src="@drawable/kg_security_lock_normal" />
+ </LinearLayout>
+ </com.android.keyguard.KeyguardStatusView>
+
+</FrameLayout>
diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml
index 5857fc2..a4d298a 100644
--- a/packages/Keyguard/res/layout/keyguard_status_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_status_view.xml
@@ -26,7 +26,7 @@
android:layout_height="match_parent"
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
androidprv:layout_maxHeight="@dimen/keyguard_security_height"
- android:gravity="center_horizontal">
+ android:gravity="center">
<com.android.keyguard.KeyguardStatusView
android:id="@+id/keyguard_status_view_face_palm"
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardDisplayManager.java
new file mode 100644
index 0000000..6bcbd6c
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard;
+
+import android.app.Presentation;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.graphics.Point;
+import android.media.MediaRouter;
+import android.media.MediaRouter.RouteInfo;
+import android.os.Bundle;
+import android.util.Slog;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowManager;
+
+public class KeyguardDisplayManager {
+ protected static final String TAG = "KeyguardDisplayManager";
+ private static boolean DEBUG = KeyguardViewMediator.DEBUG;
+ Presentation mPresentation;
+ private MediaRouter mMediaRouter;
+ private Context mContext;
+ private boolean mShowing;
+
+ KeyguardDisplayManager(Context context) {
+ mContext = context;
+ mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+ }
+
+ void show() {
+ if (!mShowing) {
+ if (DEBUG) Slog.v(TAG, "show");
+ mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
+ mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
+ updateDisplays(true);
+ }
+ mShowing = true;
+ }
+
+ void hide() {
+ if (mShowing) {
+ if (DEBUG) Slog.v(TAG, "hide");
+ mMediaRouter.removeCallback(mMediaRouterCallback);
+ updateDisplays(false);
+ }
+ mShowing = false;
+ }
+
+ private final MediaRouter.SimpleCallback mMediaRouterCallback =
+ new MediaRouter.SimpleCallback() {
+ @Override
+ public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
+ if (DEBUG) Slog.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
+ updateDisplays(mShowing);
+ }
+
+ @Override
+ public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
+ if (DEBUG) Slog.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
+ updateDisplays(mShowing);
+ }
+
+ @Override
+ public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
+ if (DEBUG) Slog.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
+ updateDisplays(mShowing);
+ }
+ };
+
+ private OnDismissListener mOnDismissListener = new OnDismissListener() {
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mPresentation = null;
+ }
+ };
+
+ protected void updateDisplays(boolean showing) {
+ if (showing) {
+ MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(
+ MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
+ boolean useDisplay = route != null
+ && route.getPlaybackType() == MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
+ Display presentationDisplay = useDisplay ? route.getPresentationDisplay() : null;
+
+ if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) {
+ if (DEBUG) Slog.v(TAG, "Display gone: " + mPresentation.getDisplay());
+ mPresentation.dismiss();
+ mPresentation = null;
+ }
+
+ if (mPresentation == null && presentationDisplay != null) {
+ if (DEBUG) Slog.i(TAG, "Keyguard enabled on display: " + presentationDisplay);
+ mPresentation = new KeyguardPresentation(mContext, presentationDisplay);
+ mPresentation.setOnDismissListener(mOnDismissListener);
+ try {
+ mPresentation.show();
+ } catch (WindowManager.InvalidDisplayException ex) {
+ Slog.w(TAG, "Invalid display:", ex);
+ mPresentation = null;
+ }
+ }
+ } else {
+ if (mPresentation != null) {
+ mPresentation.dismiss();
+ mPresentation = null;
+ }
+ }
+ }
+
+ private final static class KeyguardPresentation extends Presentation {
+ private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
+ private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
+ private View mClock;
+ private int mUsableWidth;
+ private int mUsableHeight;
+ private int mMarginTop;
+ private int mMarginLeft;
+ Runnable mMoveTextRunnable = new Runnable() {
+ @Override
+ public void run() {
+ int x = mMarginLeft + (int) (Math.random() * (mUsableWidth - mClock.getWidth()));
+ int y = mMarginTop + (int) (Math.random() * (mUsableHeight - mClock.getHeight()));
+ mClock.setTranslationX(x);
+ mClock.setTranslationY(y);
+ mClock.postDelayed(mMoveTextRunnable, MOVE_CLOCK_TIMEOUT);
+ }
+ };
+
+ public KeyguardPresentation(Context context, Display display) {
+ super(context, display);
+ getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+
+ public void onDetachedFromWindow() {
+ mClock.removeCallbacks(mMoveTextRunnable);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Point p = new Point();
+ getDisplay().getSize(p);
+ mUsableWidth = VIDEO_SAFE_REGION * p.x/100;
+ mUsableHeight = VIDEO_SAFE_REGION * p.y/100;
+ mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200;
+ mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200;
+
+ setContentView(R.layout.keyguard_presentation);
+ mClock = findViewById(R.id.clock);
+
+ // Avoid screen burn in
+ mClock.post(mMoveTextRunnable);
+ }
+ }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
index 2719c5c..349078f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
@@ -60,12 +60,12 @@
*/
public class KeyguardTransportControlView extends FrameLayout {
- private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s
private static final int RESET_TO_METADATA_DELAY = 5000;
protected static final boolean DEBUG = false;
protected static final String TAG = "TransportControlView";
private static final boolean ANIMATE_TRANSITIONS = true;
+ protected static final long QUIESCENT_PLAYBACK_FACTOR = 1000;
private ViewGroup mMetadataContainer;
private ViewGroup mInfoContainer;
@@ -89,11 +89,9 @@
private ImageView mBadge;
private boolean mSeekEnabled;
- private boolean mUserSeeking;
private java.text.DateFormat mFormat;
- private Date mTimeElapsed;
- private Date mTrackDuration;
+ private Date mTempDate = new Date();
/**
* The metadata which should be populated into the view once we've been attached
@@ -111,18 +109,25 @@
@Override
public void onClientPlaybackStateUpdate(int state) {
- setSeekBarsEnabled(false);
updatePlayPauseState(state);
}
@Override
public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs,
long currentPosMs, float speed) {
- setSeekBarsEnabled(mMetadata != null && mMetadata.duration > 0);
updatePlayPauseState(state);
if (DEBUG) Log.d(TAG, "onClientPlaybackStateUpdate(state=" + state +
", stateChangeTimeMs=" + stateChangeTimeMs + ", currentPosMs=" + currentPosMs +
", speed=" + speed + ")");
+
+ removeCallbacks(mUpdateSeekBars);
+ // Since the music client may be responding to historical events that cause the
+ // playback state to change dramatically, wait until things become quiescent before
+ // resuming automatic scrub position update.
+ if (mTransientSeek.getVisibility() == View.VISIBLE
+ && playbackPositionShouldMove(mCurrentPlayState)) {
+ postDelayed(mUpdateSeekBars, QUIESCENT_PLAYBACK_FACTOR);
+ }
}
@Override
@@ -136,15 +141,21 @@
}
};
- private final Runnable mUpdateSeekBars = new Runnable() {
+ private class UpdateSeekBarRunnable implements Runnable {
public void run() {
- if (updateSeekBars()) {
+ boolean seekAble = updateOnce();
+ if (seekAble) {
removeCallbacks(this);
postDelayed(this, 1000);
}
}
+ public boolean updateOnce() {
+ return updateSeekBars();
+ }
};
+ private final UpdateSeekBarRunnable mUpdateSeekBars = new UpdateSeekBarRunnable();
+
private final Runnable mResetToMetadata = new Runnable() {
public void run() {
resetToMetadata();
@@ -163,6 +174,7 @@
}
if (keyCode != -1) {
sendMediaButtonClick(keyCode);
+ delayResetToMetadata(); // if the scrub bar is showing, keep showing it.
}
}
};
@@ -177,25 +189,67 @@
}
};
+ // This class is here to throttle scrub position updates to the music client
+ class FutureSeekRunnable implements Runnable {
+ private int mProgress;
+ private boolean mPending;
+
+ public void run() {
+ scrubTo(mProgress);
+ mPending = false;
+ }
+
+ void setProgress(int progress) {
+ mProgress = progress;
+ if (!mPending) {
+ mPending = true;
+ postDelayed(this, 30);
+ }
+ }
+ };
+
+ // This is here because RemoteControlClient's method isn't visible :/
+ private final static boolean playbackPositionShouldMove(int playstate) {
+ switch(playstate) {
+ case RemoteControlClient.PLAYSTATE_STOPPED:
+ case RemoteControlClient.PLAYSTATE_PAUSED:
+ case RemoteControlClient.PLAYSTATE_BUFFERING:
+ case RemoteControlClient.PLAYSTATE_ERROR:
+ case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+ case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
+ return false;
+ case RemoteControlClient.PLAYSTATE_PLAYING:
+ case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
+ case RemoteControlClient.PLAYSTATE_REWINDING:
+ default:
+ return true;
+ }
+ }
+
+ private final FutureSeekRunnable mFutureSeekRunnable = new FutureSeekRunnable();
+
private final SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener =
new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
- scrubTo(progress);
+ mFutureSeekRunnable.setProgress(progress);
delayResetToMetadata();
+ mTempDate.setTime(progress);
+ mTransientSeekTimeElapsed.setText(mFormat.format(mTempDate));
+ } else {
+ updateSeekDisplay();
}
- updateSeekDisplay();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
- mUserSeeking = true;
+ delayResetToMetadata();
+ removeCallbacks(mUpdateSeekBars); // don't update during user interaction
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
- mUserSeeking = false;
}
};
@@ -247,17 +301,11 @@
if (enabled == mSeekEnabled) return;
mSeekEnabled = enabled;
- if (mTransientSeek.getVisibility() == VISIBLE) {
+ if (mTransientSeek.getVisibility() == VISIBLE && !enabled) {
mTransientSeek.setVisibility(INVISIBLE);
mMetadataContainer.setVisibility(VISIBLE);
- mUserSeeking = false;
cancelResetToMetadata();
}
- if (enabled) {
- mUpdateSeekBars.run();
- } else {
- removeCallbacks(mUpdateSeekBars);
- }
}
public void setTransportControlCallback(KeyguardHostView.TransportControlCallback
@@ -294,6 +342,8 @@
}
final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
setEnableMarquee(screenOn);
+ // Allow long-press anywhere else in this view to show the seek bar
+ setOnLongClickListener(mTransportShowSeekBarListener);
}
@Override
@@ -326,7 +376,6 @@
mAudioManager.unregisterRemoteController(mRemoteController);
KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitor);
mMetadata.clear();
- mUserSeeking = false;
removeCallbacks(mUpdateSeekBars);
}
@@ -484,18 +533,12 @@
void updateSeekDisplay() {
if (mMetadata != null && mRemoteController != null && mFormat != null) {
- if (mTimeElapsed == null) {
- mTimeElapsed = new Date();
- }
- if (mTrackDuration == null) {
- mTrackDuration = new Date();
- }
- mTimeElapsed.setTime(mRemoteController.getEstimatedMediaPosition());
- mTrackDuration.setTime(mMetadata.duration);
- mTransientSeekTimeElapsed.setText(mFormat.format(mTimeElapsed));
- mTransientSeekTimeTotal.setText(mFormat.format(mTrackDuration));
+ mTempDate.setTime(mRemoteController.getEstimatedMediaPosition());
+ mTransientSeekTimeElapsed.setText(mFormat.format(mTempDate));
+ mTempDate.setTime(mMetadata.duration);
+ mTransientSeekTimeTotal.setText(mFormat.format(mTempDate));
- if (DEBUG) Log.d(TAG, "updateSeekDisplay timeElapsed=" + mTimeElapsed +
+ if (DEBUG) Log.d(TAG, "updateSeekDisplay timeElapsed=" + mTempDate +
" duration=" + mMetadata.duration);
}
}
@@ -508,10 +551,16 @@
mTransientSeek.setVisibility(INVISIBLE);
mMetadataContainer.setVisibility(VISIBLE);
cancelResetToMetadata();
+ removeCallbacks(mUpdateSeekBars); // don't update if scrubber isn't visible
} else {
mTransientSeek.setVisibility(VISIBLE);
mMetadataContainer.setVisibility(INVISIBLE);
delayResetToMetadata();
+ if (playbackPositionShouldMove(mCurrentPlayState)) {
+ mUpdateSeekBars.run();
+ } else {
+ mUpdateSeekBars.updateOnce();
+ }
}
mTransportControlCallback.userActivity();
return true;
@@ -573,9 +622,6 @@
case RemoteControlClient.PLAYSTATE_PLAYING:
imageResId = R.drawable.ic_media_pause;
imageDescId = R.string.keyguard_transport_pause_description;
- if (mSeekEnabled) {
- mUpdateSeekBars.run();
- }
break;
case RemoteControlClient.PLAYSTATE_BUFFERING:
@@ -590,10 +636,9 @@
break;
}
- if (state != RemoteControlClient.PLAYSTATE_PLAYING) {
- removeCallbacks(mUpdateSeekBars);
- updateSeekBars();
- }
+ boolean clientSupportsSeek = mMetadata != null && mMetadata.duration > 0;
+ setSeekBarsEnabled(clientSupportsSeek);
+
mBtnPlay.setImageResource(imageResId);
mBtnPlay.setContentDescription(getResources().getString(imageDescId));
mCurrentPlayState = state;
@@ -601,11 +646,9 @@
boolean updateSeekBars() {
final int position = (int) mRemoteController.getEstimatedMediaPosition();
+ if (DEBUG) Log.v(TAG, "Estimated time:" + position);
if (position >= 0) {
- if (DEBUG) Log.v(TAG, "Seek to " + position);
- if (!mUserSeeking) {
- mTransientSeekBar.setProgress(position);
- }
+ mTransientSeekBar.setProgress(position);
return true;
}
Log.w(TAG, "Updating seek bars; received invalid estimated media position (" +
@@ -671,34 +714,4 @@
public boolean providesClock() {
return false;
}
-
- private boolean wasPlayingRecently(int state, long stateChangeTimeMs) {
- switch (state) {
- case RemoteControlClient.PLAYSTATE_PLAYING:
- case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
- case RemoteControlClient.PLAYSTATE_REWINDING:
- case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
- case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- // actively playing or about to play
- return true;
- case RemoteControlClient.PLAYSTATE_NONE:
- return false;
- case RemoteControlClient.PLAYSTATE_STOPPED:
- case RemoteControlClient.PLAYSTATE_PAUSED:
- case RemoteControlClient.PLAYSTATE_ERROR:
- // we have stopped playing, check how long ago
- if (DEBUG) {
- if ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS) {
- Log.v(TAG, "wasPlayingRecently: time < TIMEOUT was playing recently");
- } else {
- Log.v(TAG, "wasPlayingRecently: time > TIMEOUT");
- }
- }
- return ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS);
- default:
- Log.e(TAG, "Unknown playback state " + state + " in wasPlayingRecently()");
- return false;
- }
- }
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
index fd7cae6..6aa0a4b 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
@@ -61,9 +61,11 @@
public class KeyguardViewManager {
private final static boolean DEBUG = KeyguardViewMediator.DEBUG;
private static String TAG = "KeyguardViewManager";
- public static boolean USE_UPPER_CASE = true;
public final static String IS_SWITCHING_USER = "is_switching_user";
+ // Delay dismissing keyguard to allow animations to complete.
+ private static final int HIDE_KEYGUARD_DELAY = 500;
+
// Timeout used for keypresses
static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
@@ -509,9 +511,10 @@
mKeyguardHost.setCustomBackground(null);
updateShowWallpaper(true);
mKeyguardHost.removeView(lastView);
+ mViewMediatorCallback.keyguardGone();
}
}
- }, 500);
+ }, HIDE_KEYGUARD_DELAY);
}
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
index b92ae90..49982ea 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
@@ -253,6 +253,11 @@
private final float mLockSoundVolume;
/**
+ * For managing external displays
+ */
+ private KeyguardDisplayManager mKeyguardDisplayManager;
+
+ /**
* Cache of avatar drawables, for use by KeyguardMultiUserAvatar.
*/
private static MultiUserAvatarCache sMultiUserAvatarCache = new MultiUserAvatarCache();
@@ -304,6 +309,11 @@
* Report that the keyguard is dismissable, pending the next keyguardDone call.
*/
void keyguardDonePending();
+
+ /**
+ * Report when keyguard is actually gone
+ */
+ void keyguardGone();
}
KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
@@ -457,6 +467,11 @@
public void keyguardDonePending() {
mKeyguardDonePending = true;
}
+
+ @Override
+ public void keyguardGone() {
+ mKeyguardDisplayManager.hide();
+ }
};
private void userActivity() {
@@ -483,6 +498,8 @@
mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(DELAYED_KEYGUARD_ACTION));
+ mKeyguardDisplayManager = new KeyguardDisplayManager(context);
+
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
@@ -597,6 +614,7 @@
}
}
KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurndOff(why);
+ mKeyguardDisplayManager.show();
}
private void doKeyguardLaterLocked() {
@@ -1218,6 +1236,7 @@
mShowKeyguardWakeLock.release();
}
+ mKeyguardDisplayManager.show();
}
/**
diff --git a/packages/PrintSpooler/res/values-am/strings.xml b/packages/PrintSpooler/res/values-am/strings.xml
index ae3d1f3..3b94d6d 100644
--- a/packages/PrintSpooler/res/values-am/strings.xml
+++ b/packages/PrintSpooler/res/values-am/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"የፍለጋ ሳጥን ይታያል"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"የፍለጋ ሳጥን ተደብቋል"</string>
<string name="print_add_printer" msgid="1088656468360653455">"አታሚ አክል"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"አታሚ ምረጥ"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"አታሚ እርሳ"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> አታሚ ተገኝቷል"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> አታሚዎች ተገኝተዋል"</item>
diff --git a/packages/PrintSpooler/res/values-bg/strings.xml b/packages/PrintSpooler/res/values-bg/strings.xml
index 85b96f32..4009aa2 100644
--- a/packages/PrintSpooler/res/values-bg/strings.xml
+++ b/packages/PrintSpooler/res/values-bg/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Полето за търсене е показано"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Полето за търсене е скрито"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Добавяне на принтер"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Избиране на принтер"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Забравяне на принтера"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"Намерен е <xliff:g id="COUNT">%1$s</xliff:g> принтер"</item>
<item quantity="other" msgid="6533817036607128241">"Намерени са <xliff:g id="COUNT">%1$s</xliff:g> принтера"</item>
diff --git a/packages/PrintSpooler/res/values-cs/strings.xml b/packages/PrintSpooler/res/values-cs/strings.xml
index e85ea9b..3ddc701 100644
--- a/packages/PrintSpooler/res/values-cs/strings.xml
+++ b/packages/PrintSpooler/res/values-cs/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Vyhledávací pole se zobrazuje"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Vyhledávací pole je skryto"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Přidat tiskárnu"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Vybrat tiskárnu"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Odstranit tiskárnu"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"Počet nalezených tiskáren: <xliff:g id="COUNT">%1$s</xliff:g>"</item>
<item quantity="other" msgid="6533817036607128241">"Počet nalezených tiskáren: <xliff:g id="COUNT">%1$s</xliff:g>"</item>
diff --git a/packages/PrintSpooler/res/values-de/strings.xml b/packages/PrintSpooler/res/values-de/strings.xml
index 21289e0..6b83ac3 100644
--- a/packages/PrintSpooler/res/values-de/strings.xml
+++ b/packages/PrintSpooler/res/values-de/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Suchfeld angezeigt"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Suchfeld ausgeblendet"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Drucker hinzufügen"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Drucker auswählen"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Drucker wieder vergessen"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> Drucker gefunden"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> Drucker gefunden"</item>
diff --git a/packages/PrintSpooler/res/values-es-rUS/strings.xml b/packages/PrintSpooler/res/values-es-rUS/strings.xml
index 1fd174d..c7d12af 100644
--- a/packages/PrintSpooler/res/values-es-rUS/strings.xml
+++ b/packages/PrintSpooler/res/values-es-rUS/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Cuadro de búsqueda visible"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Cuadro de búsqueda oculto"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Agregar impresora"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Seleccionar impresora"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"No recordar impresora"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"Se encontró <xliff:g id="COUNT">%1$s</xliff:g> impresora."</item>
<item quantity="other" msgid="6533817036607128241">"Se encontraron <xliff:g id="COUNT">%1$s</xliff:g> impresoras."</item>
diff --git a/packages/PrintSpooler/res/values-et-rEE/strings.xml b/packages/PrintSpooler/res/values-et-rEE/strings.xml
index f4391c8..2b3b352 100644
--- a/packages/PrintSpooler/res/values-et-rEE/strings.xml
+++ b/packages/PrintSpooler/res/values-et-rEE/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Otsingukast on kuvatud"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Otsingukast on peidetud"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Lisa printer"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Printeri valimine"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Printeri unustamine"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"Leiti <xliff:g id="COUNT">%1$s</xliff:g> printer"</item>
<item quantity="other" msgid="6533817036607128241">"Leiti <xliff:g id="COUNT">%1$s</xliff:g> printerit"</item>
diff --git a/packages/PrintSpooler/res/values-fa/strings.xml b/packages/PrintSpooler/res/values-fa/strings.xml
index 9db965f..49bae323 100644
--- a/packages/PrintSpooler/res/values-fa/strings.xml
+++ b/packages/PrintSpooler/res/values-fa/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"کادر جستجو نمایان شد"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"کادر جستجو پنهان شد"</string>
<string name="print_add_printer" msgid="1088656468360653455">"افزودن چاپگر"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"انتخاب چاپگر"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"فراموش کردن چاپگر"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> چاپگر یافت شد"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> چاپگر یافت شد"</item>
diff --git a/packages/PrintSpooler/res/values-fi/strings.xml b/packages/PrintSpooler/res/values-fi/strings.xml
index bb43b6c..8658e04 100644
--- a/packages/PrintSpooler/res/values-fi/strings.xml
+++ b/packages/PrintSpooler/res/values-fi/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Hakukenttä näkyvissä"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Hakukenttä piilotettu"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Lisää tulostin"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Valitse tulostin"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Unohda tulostin"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"Löytyi <xliff:g id="COUNT">%1$s</xliff:g> tulostin"</item>
<item quantity="other" msgid="6533817036607128241">"Löytyi <xliff:g id="COUNT">%1$s</xliff:g> tulostinta"</item>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index f42d85f6..9a3352c 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Champ de recherche affiché"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Champ de recherche masqué"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Ajouter une imprimante"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Sélectionner une imprimante"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Supprimer l\'imprimante"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> imprimante trouvée"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> imprimantes trouvées"</item>
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index 39e7493..cfb557e 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Champ de recherche affiché."</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Champ de recherche masqué."</string>
<string name="print_add_printer" msgid="1088656468360653455">"Ajouter une imprimante"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Sélectionner une imprimante"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Supprimer l\'imprimante"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> imprimante trouvée."</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> imprimantes trouvées."</item>
diff --git a/packages/PrintSpooler/res/values-hi/strings.xml b/packages/PrintSpooler/res/values-hi/strings.xml
index af5745f..60406b7 100644
--- a/packages/PrintSpooler/res/values-hi/strings.xml
+++ b/packages/PrintSpooler/res/values-hi/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"खोज बॉक्स प्रदर्शित है"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"खोज बॉक्स छिपा हुआ है"</string>
<string name="print_add_printer" msgid="1088656468360653455">"प्रिंटर जोड़ें"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"प्रिंटर चुनें"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"प्रिंटर को भूल जाएं"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> प्रिंटर मिला"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> प्रिंटर मिले"</item>
diff --git a/packages/PrintSpooler/res/values-hr/strings.xml b/packages/PrintSpooler/res/values-hr/strings.xml
index ccc4664..7182cf6 100644
--- a/packages/PrintSpooler/res/values-hr/strings.xml
+++ b/packages/PrintSpooler/res/values-hr/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Okvir za pretraživanje prikazan je"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Okvir za pretraživanje skriven je"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Dodaj pisač"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Odaberite pisač"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Zaboravite pisač"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"Pronađen je <xliff:g id="COUNT">%1$s</xliff:g> pisač"</item>
<item quantity="other" msgid="6533817036607128241">"Pronađen je sljedeći broj pisača: <xliff:g id="COUNT">%1$s</xliff:g>"</item>
diff --git a/packages/PrintSpooler/res/values-hy-rAM/strings.xml b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
index 1fe3e2c..1423b82 100644
--- a/packages/PrintSpooler/res/values-hy-rAM/strings.xml
+++ b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Որոնման վանդակը ցուցադրված է"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Որոնման վանդակը թաքցվել է"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Ավելացնել տպիչ"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Ընտրել տպիչ"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Մոռանալ տպիչը"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> տպիչ է գտնվել"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> տպիչ է գտնվել"</item>
diff --git a/packages/PrintSpooler/res/values-in/strings.xml b/packages/PrintSpooler/res/values-in/strings.xml
index 26c9c33..2b80d07 100644
--- a/packages/PrintSpooler/res/values-in/strings.xml
+++ b/packages/PrintSpooler/res/values-in/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Kotak telusur ditampilkan"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Kotak telusur disembunyikan"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Tambahkan printer"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Pilih printer"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Lupakan printer"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> printer ditemukan"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> printer ditemukan"</item>
diff --git a/packages/PrintSpooler/res/values-ja/strings.xml b/packages/PrintSpooler/res/values-ja/strings.xml
index 6df1f2b1..94e8441 100644
--- a/packages/PrintSpooler/res/values-ja/strings.xml
+++ b/packages/PrintSpooler/res/values-ja/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"検索ボックスは表示されています"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"検索ボックスは表示されていません"</string>
<string name="print_add_printer" msgid="1088656468360653455">"プリンタを追加"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"プリンタを選択"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"プリンタを切断"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g>台のプリンタが見つかりました"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g>台のプリンタが見つかりました"</item>
diff --git a/packages/PrintSpooler/res/values-ka-rGE/strings.xml b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
index 5ffa81e..d36b7c9 100644
--- a/packages/PrintSpooler/res/values-ka-rGE/strings.xml
+++ b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"საძიებო ველი ნაჩვენებია"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"საძიებო ველი დამალულია"</string>
<string name="print_add_printer" msgid="1088656468360653455">"პრინტერის დამატება"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"პრინტერის არჩევა"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"პრინტერის დავიწყება"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"ნაპოვნია <xliff:g id="COUNT">%1$s</xliff:g> პრინტერი"</item>
<item quantity="other" msgid="6533817036607128241">"ნაპოვნია <xliff:g id="COUNT">%1$s</xliff:g> პრინტერი"</item>
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index c00dee5..c89f9bf 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"បានបង្ហាញប្រអប់ស្វែងរក"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"បានលាក់ប្រអប់ស្វែងរក"</string>
<string name="print_add_printer" msgid="1088656468360653455">"បន្ថែមម៉ាស៊ីនបោះពុម្ព"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"ជ្រើសម៉ាស៊ីនបោះពុម្ព"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"ភ្លេចម៉ាស៊ីនបោះពុម្ព"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"រកឃើញម៉ាស៊ីនបោះពុម្ព <xliff:g id="COUNT">%1$s</xliff:g>"</item>
<item quantity="other" msgid="6533817036607128241">"រកឃើញម៉ាស៊ីនបោះពុម្ព <xliff:g id="COUNT">%1$s</xliff:g>"</item>
diff --git a/packages/PrintSpooler/res/values-ko/strings.xml b/packages/PrintSpooler/res/values-ko/strings.xml
index 64c7ca6..3b2fef7 100644
--- a/packages/PrintSpooler/res/values-ko/strings.xml
+++ b/packages/PrintSpooler/res/values-ko/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"검색창 표시됨"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"검색창 숨겨짐"</string>
<string name="print_add_printer" msgid="1088656468360653455">"프린터 추가"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"프린터 선택"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"프린터 삭제"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"프린터 <xliff:g id="COUNT">%1$s</xliff:g>대 검색됨"</item>
<item quantity="other" msgid="6533817036607128241">"프린터 <xliff:g id="COUNT">%1$s</xliff:g>대 검색됨"</item>
diff --git a/packages/PrintSpooler/res/values-ms-rMY/strings.xml b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
index 24059b1..cca5d2c 100644
--- a/packages/PrintSpooler/res/values-ms-rMY/strings.xml
+++ b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Kotak carian ditunjukkan"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Kotak carian tersembunyi"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Tambah pencetak"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Pilih pencetak"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Lupakan pencetak"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> pencetak ditemui"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> pencetak ditemui"</item>
diff --git a/packages/PrintSpooler/res/values-nb/strings.xml b/packages/PrintSpooler/res/values-nb/strings.xml
index 1344e11..f6a60c6 100644
--- a/packages/PrintSpooler/res/values-nb/strings.xml
+++ b/packages/PrintSpooler/res/values-nb/strings.xml
@@ -41,7 +41,7 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Søkefeltet vises"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Søkefeltet er skjult"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Legg til skriver"</string>
- <string name="print_select_printer" msgid="7388760939873368698">"Velg skriveren"</string>
+ <string name="print_select_printer" msgid="7388760939873368698">"Velg skriver"</string>
<string name="print_forget_printer" msgid="5035287497291910766">"Glem skriveren"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> skriver ble funnet"</item>
diff --git a/packages/PrintSpooler/res/values-pl/strings.xml b/packages/PrintSpooler/res/values-pl/strings.xml
index fda4c14..81acc76 100644
--- a/packages/PrintSpooler/res/values-pl/strings.xml
+++ b/packages/PrintSpooler/res/values-pl/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Pole wyszukiwania jest widoczne"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Pole wyszukiwania jest ukryte"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Dodaj drukarkę"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Wybierz drukarkę"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Nie zapamiętuj drukarki"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"Znaleziono <xliff:g id="COUNT">%1$s</xliff:g> drukarkę"</item>
<item quantity="other" msgid="6533817036607128241">"Znalezione drukarki: <xliff:g id="COUNT">%1$s</xliff:g>"</item>
diff --git a/packages/PrintSpooler/res/values-pt/strings.xml b/packages/PrintSpooler/res/values-pt/strings.xml
index ae2603e..82c157d 100644
--- a/packages/PrintSpooler/res/values-pt/strings.xml
+++ b/packages/PrintSpooler/res/values-pt/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Caixa de pesquisa exibida"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Caixa de pesquisa oculta"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Adicionar impressora"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Selecionar impressora"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Esquecer impressora"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> impressora encontrada"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> impressoras encontradas"</item>
diff --git a/packages/PrintSpooler/res/values-ro/strings.xml b/packages/PrintSpooler/res/values-ro/strings.xml
index 94d389a..79c3bdc 100644
--- a/packages/PrintSpooler/res/values-ro/strings.xml
+++ b/packages/PrintSpooler/res/values-ro/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Caseta de căutare este afișată"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Caseta de căutare este ascunsă"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Adăugați o imprimantă"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Selectați imprimanta"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Omiteți imprimanta"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> imprimantă găsită"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> (de) imprimante găsite"</item>
diff --git a/packages/PrintSpooler/res/values-ru/strings.xml b/packages/PrintSpooler/res/values-ru/strings.xml
index 5eb2d57..7541bbb 100644
--- a/packages/PrintSpooler/res/values-ru/strings.xml
+++ b/packages/PrintSpooler/res/values-ru/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Окно поиска показано"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Окно поиска скрыто"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Добавить принтер"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Выбрать принтер"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Удалить принтер"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"Найден <xliff:g id="COUNT">%1$s</xliff:g> принтер"</item>
<item quantity="other" msgid="6533817036607128241">"Найдено принтеров: <xliff:g id="COUNT">%1$s</xliff:g>"</item>
diff --git a/packages/PrintSpooler/res/values-sk/strings.xml b/packages/PrintSpooler/res/values-sk/strings.xml
index e868078..39a022d 100644
--- a/packages/PrintSpooler/res/values-sk/strings.xml
+++ b/packages/PrintSpooler/res/values-sk/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Vyhľadávacie pole sa zobrazuje"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Vyhľadávacie pole je skryté"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Pridať tlačiareň"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Vybrať tlačiareň"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Odstrániť tlačiareň"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"Našla sa <xliff:g id="COUNT">%1$s</xliff:g> tlačiareň"</item>
<item quantity="other" msgid="6533817036607128241">"Počet nájdených tlačiarní: <xliff:g id="COUNT">%1$s</xliff:g>"</item>
diff --git a/packages/PrintSpooler/res/values-sl/strings.xml b/packages/PrintSpooler/res/values-sl/strings.xml
index 018f56d..5d4fe94 100644
--- a/packages/PrintSpooler/res/values-sl/strings.xml
+++ b/packages/PrintSpooler/res/values-sl/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Iskalno polje je prikazano"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Iskalno polje je skrito"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Dodajanje tiskalnika"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Izbira tiskalnika"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Odstranitev tiskalnika"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"Najden <xliff:g id="COUNT">%1$s</xliff:g> tiskalnik"</item>
<item quantity="other" msgid="6533817036607128241">"Število najdenih tiskalnikov: <xliff:g id="COUNT">%1$s</xliff:g>"</item>
diff --git a/packages/PrintSpooler/res/values-sv/strings.xml b/packages/PrintSpooler/res/values-sv/strings.xml
index 9eb9930..2286fce 100644
--- a/packages/PrintSpooler/res/values-sv/strings.xml
+++ b/packages/PrintSpooler/res/values-sv/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Sökrutan visas"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Sökrutan är dold"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Lägg till skrivare"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Välj en skrivare"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Ta bort en skrivare"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> skrivare hittades"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> skrivare hittades"</item>
diff --git a/packages/PrintSpooler/res/values-sw/strings.xml b/packages/PrintSpooler/res/values-sw/strings.xml
index c99ce0d..55c8687 100644
--- a/packages/PrintSpooler/res/values-sw/strings.xml
+++ b/packages/PrintSpooler/res/values-sw/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Kisanduku cha kutafutia kimeonyeshwa"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Kisanduku cha kutafutia kimefichwa"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Ongeza printa"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Chagua printa"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Sahau printa"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"Printa <xliff:g id="COUNT">%1$s</xliff:g> imepatikana"</item>
<item quantity="other" msgid="6533817036607128241">"Printa <xliff:g id="COUNT">%1$s</xliff:g> zimepatikana"</item>
diff --git a/packages/PrintSpooler/res/values-th/strings.xml b/packages/PrintSpooler/res/values-th/strings.xml
index 2f3ae1f..aa01d6f 100644
--- a/packages/PrintSpooler/res/values-th/strings.xml
+++ b/packages/PrintSpooler/res/values-th/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"แสดงช่องค้นหาอยู่"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"ซ่อนช่องค้นหาอยู่"</string>
<string name="print_add_printer" msgid="1088656468360653455">"เพิ่มเครื่องพิมพ์"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"เลือกเครื่องพิมพ์"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"ไม่ต้องจำเครื่องพิมพ์"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"พบเครื่องพิมพ์ <xliff:g id="COUNT">%1$s</xliff:g> เครื่อง"</item>
<item quantity="other" msgid="6533817036607128241">"พบเครื่องพิมพ์ <xliff:g id="COUNT">%1$s</xliff:g> เครื่อง"</item>
diff --git a/packages/PrintSpooler/res/values-tl/strings.xml b/packages/PrintSpooler/res/values-tl/strings.xml
index 7fc1071..a72c937 100644
--- a/packages/PrintSpooler/res/values-tl/strings.xml
+++ b/packages/PrintSpooler/res/values-tl/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Ipinapakita ang box para sa paghahanap"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Nakatago ang box para sa paghahanap"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Magdagdag ng printer"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Piliin ang printer"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Kalimutan ang printer"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> printer ang nakita"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> (na) printer ang nakita"</item>
diff --git a/packages/PrintSpooler/res/values-tr/strings.xml b/packages/PrintSpooler/res/values-tr/strings.xml
index 1be7a04..c6e302d 100644
--- a/packages/PrintSpooler/res/values-tr/strings.xml
+++ b/packages/PrintSpooler/res/values-tr/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Arama kutusu gösteriliyor"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Arama kutusu gizli"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Yazıcı ekle"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Yazıcı seç"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Yazıcıyı unut"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> yazıcı bulundu"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> yazıcı bulundu"</item>
diff --git a/packages/PrintSpooler/res/values-vi/strings.xml b/packages/PrintSpooler/res/values-vi/strings.xml
index 6669e99..d21172c 100644
--- a/packages/PrintSpooler/res/values-vi/strings.xml
+++ b/packages/PrintSpooler/res/values-vi/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Hiển thị hộp tìm kiếm"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Ẩn hộp tìm kiếm"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Thêm máy in"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Chọn máy in"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Bỏ qua máy in"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"Đã tìm thấy <xliff:g id="COUNT">%1$s</xliff:g> máy in"</item>
<item quantity="other" msgid="6533817036607128241">"Đã tìm thấy <xliff:g id="COUNT">%1$s</xliff:g> máy in"</item>
diff --git a/packages/PrintSpooler/res/values-zh-rCN/strings.xml b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
index 41f7d65..e43ea60 100644
--- a/packages/PrintSpooler/res/values-zh-rCN/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"搜索框已显示"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"搜索框已隐藏"</string>
<string name="print_add_printer" msgid="1088656468360653455">"添加打印机"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"选择打印机"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"取消保存打印机"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"找到<xliff:g id="COUNT">%1$s</xliff:g>台打印机"</item>
<item quantity="other" msgid="6533817036607128241">"找到<xliff:g id="COUNT">%1$s</xliff:g>台打印机"</item>
diff --git a/packages/PrintSpooler/res/values-zh-rHK/strings.xml b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
index 9a0a2c9..2c2422a9 100644
--- a/packages/PrintSpooler/res/values-zh-rHK/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"搜尋框已顯示"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"搜尋框已隱藏"</string>
<string name="print_add_printer" msgid="1088656468360653455">"新增打印機"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"選取打印機"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"移除打印機"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"找到 <xliff:g id="COUNT">%1$s</xliff:g> 部打印機"</item>
<item quantity="other" msgid="6533817036607128241">"找到 <xliff:g id="COUNT">%1$s</xliff:g> 部打印機"</item>
diff --git a/packages/PrintSpooler/res/values-zh-rTW/strings.xml b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
index e76774c..0fe88d9 100644
--- a/packages/PrintSpooler/res/values-zh-rTW/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"搜尋框已顯示"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"搜尋框已隱藏"</string>
<string name="print_add_printer" msgid="1088656468360653455">"新增印表機"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"選取印表機"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"移除印表機"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"找到 <xliff:g id="COUNT">%1$s</xliff:g> 台印表機"</item>
<item quantity="other" msgid="6533817036607128241">"找到 <xliff:g id="COUNT">%1$s</xliff:g> 台印表機"</item>
diff --git a/packages/PrintSpooler/res/values-zu/strings.xml b/packages/PrintSpooler/res/values-zu/strings.xml
index 3310db8..08e31e1 100644
--- a/packages/PrintSpooler/res/values-zu/strings.xml
+++ b/packages/PrintSpooler/res/values-zu/strings.xml
@@ -41,10 +41,8 @@
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Ibhokisi lokuhlola libonisiwe"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Ibhokisi lokusesha lifihliwe"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Engeza iphrinta"</string>
- <!-- no translation found for print_select_printer (7388760939873368698) -->
- <skip />
- <!-- no translation found for print_forget_printer (5035287497291910766) -->
- <skip />
+ <string name="print_select_printer" msgid="7388760939873368698">"Khetha iphrinta"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Khohlwa iphrinta"</string>
<plurals name="print_search_result_count_utterance">
<item quantity="one" msgid="4484953260685964252">"<xliff:g id="COUNT">%1$s</xliff:g> iphrinta itholiwe"</item>
<item quantity="other" msgid="6533817036607128241">"<xliff:g id="COUNT">%1$s</xliff:g> amaphrinta atholiwe"</item>
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_remote_display.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_remote_display.png
deleted file mode 100644
index 02d7fda..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_remote_display.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_remote_display_connected.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_remote_display_connected.png
deleted file mode 100644
index 263f07c..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_remote_display_connected.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_remote_display.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_remote_display.png
deleted file mode 100644
index 09ae409..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_remote_display.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_remote_display_connected.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_remote_display_connected.png
deleted file mode 100644
index 780cfc8..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_remote_display_connected.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_remote_display.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_remote_display.png
deleted file mode 100644
index 48f90ac..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_remote_display.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_remote_display_connected.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_remote_display_connected.png
deleted file mode 100644
index 621c045..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_remote_display_connected.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_remote_display.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_remote_display.png
deleted file mode 100644
index b07be828..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_remote_display.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_remote_display_connected.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_remote_display_connected.png
deleted file mode 100644
index f02d0ab..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_remote_display_connected.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 6f0d857..988d594 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -196,8 +196,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nie gekoppel nie"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Geen netwerk nie"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi af"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi-skerm"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Draadlose aansig"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Saai na skerm uit"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Helderheid"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OUTO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netwerk word\ndalk gemonitor"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 686488e..1c814b6 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"አልተገናኘም"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ምንም አውታረ መረብ የለም"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ጠፍቷል"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"የWi-Fi ማሳያ"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"ገመድ አልባ ማሳያ"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ብሩህነት"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ራስ-ሰር"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"አውታረ መረብ\nክትትል ሊደረግበት ይችላል"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 214daae..7164a31 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"ليست متصلة"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"لا تتوفر شبكة"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"إيقاف Wi-Fi"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"عرض Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"عرض شاشة لاسلكي"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"السطوع"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"تلقائي"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"قد تكون الشبكة\nخاضعة للرقابة"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 356e7df..8f46657 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -205,8 +205,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Няма падключэння"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Няма сеткi"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi адключаны"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Дысплей Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Бесправадны дысплей"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Яркасць"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АЎТА"</string>
<!-- no translation found for ssl_ca_cert_warning (9005954106902053641) -->
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index c893490..c26d01f 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Няма връзка"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Няма мрежа"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi е изключен"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Дисплей през Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Безжичен дисплей"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Яркост"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТ."</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Мрежата може\nда се наблюдава"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index f29b10e..a8e732f 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -198,8 +198,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Desconnectat"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"No hi ha cap xarxa"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desconnectada"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Pantalla Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Pantalla sense fil"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brillantor"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMÀTICA"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"És possible que la xarxa\nestigui controlada"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 76a7201..fd8302f 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -198,8 +198,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nepřipojeno"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Žádná síť"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi vypnuta"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Displej přes Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Bezdrátový displej"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Jas"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATICKY"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Síť může být\nmonitorována"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 3f31217..7723f92 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ikke forbundet"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Intet netværk"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi slået fra"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi Display"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Trådløs skærm"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Lysstyrke"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netværket kan\nvære overvåget"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 444a555..a2dc38e 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -198,8 +198,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nicht verbunden"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Kein Netz"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"WLAN aus"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"WLAN-Display"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Kabellose Übertragung (WiDi)"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Helligkeit"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netzwerk wird\neventuell überwacht."</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 6be551c..17791d6 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -198,8 +198,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Μη συνδεδεμένο"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Κανένα δίκτυο"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ανενεργό"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Οθόνη Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Ασύρματη οθόνη"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Φωτεινότητα"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ΑΥΤΟΜΑΤΗ"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Το δίκτυο μπορεί\nνα παρακολουθείται"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 93efc15..825692d 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -196,8 +196,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Not Connected"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"No Network"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Off"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi Display"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Wireless Display"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast Screen"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brightness"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Network may\nbe monitored"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 93efc15..825692d 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -196,8 +196,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Not Connected"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"No Network"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Off"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi Display"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Wireless Display"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast Screen"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brightness"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Network may\nbe monitored"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 4c795cb..a79de41 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -198,8 +198,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Sin conexión"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Sin red"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desactivada"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Pantalla Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Pantalla inalámbrica"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brillo"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMÁTICO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Es posible que la red\nesté supervisada."</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 9814ae7..0f71f03 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"No conectado"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"No hay red."</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desactivado"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Pantalla Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Pantalla inalámbrica"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brillo"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"La red se\npuede supervisar"</string>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 510cce2..b61f550 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ühendus puudub"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Võrku pole"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"WiFi-ühendus on väljas"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"WiFi-ekraan"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Juhtmeta ekraaniühendus"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Heledus"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMAATNE"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Võrku võidakse\njälgida"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 4d2b1c4..5782975 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -196,8 +196,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"متصل نیست"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"شبکهای موجود نیست"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi خاموش است"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi Display"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"نمایش بدون سیم"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"فرستادن صفحه"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"روشنایی"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"خودکار"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"ممکن است شبکه\nتحت نظارت باشد"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 299a0a5..57646aa 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ei yhteyttä"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ei verkkoa"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wifi-yhteys pois käytöstä"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wifi-näyttö"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Langaton näyttö"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Kirkkaus"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Verkkoa saatetaan\nvalvoa"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 2a53193..d8b5a43 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -198,8 +198,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Non connecté"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Aucun réseau"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi désactivé"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Affichage Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Affichage sans fil"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Luminosité"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATIQUE"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Le réseau peut\nêtre surveillé."</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 61260fe..e8b8a80 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -198,8 +198,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Non connecté"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Aucun réseau"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi désactivé"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi Display"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Affichage sans fil"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Luminosité"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATIQUE"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Le réseau peut\nêtre surveillé."</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 44273b1..083c80c 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"कनेक्ट नहीं है"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"कोई नेटवर्क नहीं"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi बंद"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi प्रदर्शन"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"वायरलेस डिस्प्ले"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"स्क्रीन की रोशनी"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"स्वत:"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"नेटवर्क को\nमॉनीटर किया जा सकता है"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 7384dd5..1f39428 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nije povezano"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nema mreže"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi isključen"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi zaslon"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Bežični prikaz"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Svjetlina"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATSKI"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Mreža se\nmožda prati"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 65605aa..aa78619 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nincs kapcsolat"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nincs hálózat"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi kikapcsolva"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi kijelző"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Vezeték nélküli kijelző"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Fényerő"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"automatikus"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Lehet, hogy a\nhálózat felügyelt"</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index d2f5096..f5793c0 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -196,8 +196,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Միացված չէ"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ցանց չկա"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi-ը անջատված է"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi ցուցադրիչ"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Անլար էկրան"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Հեռակա էկրան"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Պայծառություն"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"Ինքնաշխատ"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Ցանցը կարող է\nվերահսկվել"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 32e7c42..c8c7f83 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Tidak Tersambung"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Tidak Ada Jaringan"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Mati"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Tampilan Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Layar Nirkabel"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Kecerahan"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OTOMATIS"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Jaringan bisa\ndiawasi"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index f8790bb..e0bc700 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -198,8 +198,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Non connesso"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nessuna rete"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi disattivato"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Display Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Visualizzazione wireless"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Luminosità"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"La rete potrebbe\nessere monitorata"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 5dbe432..fe9c458 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -196,8 +196,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"לא מחובר"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"אין רשת"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi כבוי"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"תצוגת Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"תצוגת Wi-Fi"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"העבר מסך"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"בהירות"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"אוטומטי"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"ייתכן שהרשת\nמנוטרת"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index d1ba2a8..107c4fe 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -198,8 +198,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"接続されていません"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ネットワークなし"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi OFF"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fiディスプレイ"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"ワイヤレスディスプレイ"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"画面の明るさ"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"ネットワークが監視される\n場合があります"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index bd6e87f2..67b29d6 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -196,8 +196,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"არ არის დაკავშირებული."</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ქსელი არ არის"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi გამორთულია"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi ეკრანი"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"უსადენო ეკრანი"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast Screen"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"განათება"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ავტომატურად"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"შესაძლოა ქსელზე\nმონიტორინგი ხორციელდებოდეს"</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 20ee33b..6a99c0b 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"មិនបានតភ្ជាប់"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"គ្មានបណ្ដាញ"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"បិទវ៉ាយហ្វាយ"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"បង្ហាញវ៉ាយហ្វាយ"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"បង្ហាញបណ្ដាញឥតខ្សែ"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ពន្លឺ"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ស្វ័យប្រវត្តិ"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញអាច\nត្រូវបានត្រួតពិនិត្យ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index cec56e5..a079814 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"연결되어 있지 않음"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"네트워크가 연결되지 않음"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi 꺼짐"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi 디스플레이"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"무선 디스플레이"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"밝기"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"자동"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"네트워크가\n모니터링될 수 있음"</string>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index f62dea2..925547d84 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"ບໍ່ໄດ້ເຊື່ອມຕໍ່"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ບໍ່ມີເຄືອຂ່າຍ"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ປິດ"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"ຈໍສະແດງຜົນ Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"ການສະແດງຜົນໄຮ້ສາຍ"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ຄວາມແຈ້ງ"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ອັດຕະໂນມັດ"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"ເຄືອຄ່າຍອາດ\nຖືກຕິດຕາມ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index c38b653..863d713 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Neprisijungta"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Tinklo nėra"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"„Wi-Fi“ išjungta"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"„Wi-Fi“ pateiktis"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Belaidis rodymas"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Skaistis"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATINIS"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Tinklas gali\nbūti stebimas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index e969920..182f5a1 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nav izveidots savienojums"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nav tīkla"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ir izslēgts"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi displejs"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Bezvadu attēlošana"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Spilgtums"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMĀTISKI"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Tīkls var\ntikt uzraudzīts"</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 72d92b5..d92407c 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -196,8 +196,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Холбогдоогүй"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Сүлжээгүй"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi унтарсан"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi Дэлгэц"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Утасгүй дэлгэц"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Дамжуулах дэлгэц"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Тодрол"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТОМАТ"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Сүлжээ хянагдаж\nбайж болзошгүй"</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index dff3279..cc2ce3b 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Tidak Disambungkan"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Tiada Rangkaian"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Dimatikan"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Paparan Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Paparan Wayarles"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Kecerahan"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Rangkaian mungkin\nboleh dipantau"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 66320ce..e46b1d7 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ikke tilkoblet"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ingen nettverk"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi er av"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi-skjerm"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Trådløs skjerm"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Lysstyrke"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Nettverket kan\nvære overvåket"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 9aa7e75..3aa41f8 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Niet verbonden"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Geen netwerk"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wifi uit"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wifi-weergave"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Draadloze weergave"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Helderheid"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATISCH"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netwerk kan\nworden gecontroleerd"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 3fe07e7..9b8f58d 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Brak połączenia"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Brak sieci"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wyłącz Wi-Fi"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wyświetlacz Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Wyświetlacz bezprzewodowy"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Jasność"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATYCZNA"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Sieć może być\nmonitorowana"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 22eda90..73c3e0a 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Não Ligado"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Sem Rede"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Desligado"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Visualização Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Display Sem Fios"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brilho"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMÁTICO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"A rede pode ser\nmonitorizada"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 351965f..c812307 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -198,8 +198,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Não conectado"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Sem rede"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desligado"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi Display"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Display sem fio"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brilho"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"A rede pode estar\nsob monitoração"</string>
diff --git a/packages/SystemUI/res/values-rm/strings.xml b/packages/SystemUI/res/values-rm/strings.xml
index 9e1c9b8..5bc11e9 100644
--- a/packages/SystemUI/res/values-rm/strings.xml
+++ b/packages/SystemUI/res/values-rm/strings.xml
@@ -362,9 +362,7 @@
<skip />
<!-- no translation found for quick_settings_wifi_off_label (7558778100843885864) -->
<skip />
- <!-- no translation found for quick_settings_wifi_display_label (6893592964463624333) -->
- <skip />
- <!-- no translation found for quick_settings_wifi_display_no_connection_label (2355298740765736918) -->
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
<skip />
<!-- no translation found for quick_settings_brightness_dialog_title (8599674057673605368) -->
<skip />
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 7415128..ae4d905 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Neconectat"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nicio reţea"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi deconectat"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Afişaj Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Ecran wireless"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Luminozitate"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMAT"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Rețeaua poate\nfi monitorizată"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index a587dec..a82a9d7 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -200,8 +200,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Нет соединения"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Нет сети"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi выкл."</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Проектор Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Wi-Fi-монитор"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Яркость"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТОНАСТРОЙКА"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Действия в сети\nмогут отслеживаться"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 0b82843..7b54d5e 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -198,8 +198,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nepripojené"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Žiadna sieť"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Sieť Wi-Fi je vypnutá"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Obrazovka Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Bezdrôtový displej"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Jas"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATICKY"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Sieť môže byť\nmonitorovaná"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 6699440..e7dc25c 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Povezava ni vzpostavljena"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ni omrežja"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi izklopljen"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Zaslon Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Prikaz brezžičnih naprav"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Svetlost"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"SAMODEJNO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Omrežje je\nlahko spremljano"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 4e990d1..24f9748 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Веза није успостављена"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Нема мреже"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi је искључен"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi екран"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Бежични екран"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Осветљеност"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АУТОМАТСКА"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Мрежа се можда\nнадгледа"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 06eccfc..5e0beba 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ej ansluten"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Inget nätverk"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi av"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi-skärm"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Trådlös skärm"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ljusstyrka"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Nätverket kan\nvara övervakat"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 6bd8632..53c1cd5 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -194,8 +194,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Haijaunganishwa"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Hakuna Mtandao"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Imezimwa"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Onyesho la Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Uonyeshaji Pasiwaya"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ung\'avu"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"KIOTOMATIKI"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Huenda mtandao\nunafuatiliwa"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 6893694..bca2e15 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -196,8 +196,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"ไม่ได้เชื่อมต่อ"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ไม่มีเครือข่าย"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"ปิด WiFi"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"การแสดงผล WiFi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"จอแสดงผลไร้สาย"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"หน้าจอ Cast"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ความสว่าง"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"อัตโนมัติ"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"เครือข่ายอาจ\nถูกตรวจสอบ"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 6aa4b47..a2d4b10 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Hindi Nakakonekta"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Walang Network"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Naka-off ang Wi-Fi"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Display ng Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Wireless Display"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brightness"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Maaaring\nsinusubaybayan ang network"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 964371f..fab8114 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Bağlı Değil"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ağ yok"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Kablosuz Kapalı"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Kablosuz Ekran"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Kablosuz Ekran"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Parlaklık"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OTOMATİK"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Ağ izleniyor\nolabilir"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index f502691..309368e 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Не під’єднано."</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Немає мережі"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi вимкнено"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Відображення Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Бездротове відображення"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Яскравість"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТО"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Мережа може\nвідстежуватися"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 707f2d8..2122461 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -196,8 +196,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Chưa được kết nối"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Không có mạng nào"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Tắt Wi-Fi"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Hiển thị Wi-Fi"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Hiển thị không dây"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Độ sáng"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"TỰ ĐỘNG"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Mạng có thể\nđược giám sát"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 87f1893..0d8b3de 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -198,8 +198,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"未连接"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"无网络"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"WLAN 已关闭"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"WLAN 显示"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"无线显示"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自动"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"网络可能会\n受到监控"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 1007f85..ff2923c 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -198,8 +198,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"未連線"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"沒有網絡"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi 關閉"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi Display"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"無線顯示"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"放送屏幕"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"網絡可能會\n受到監控"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index edd6fd2..cfb784e 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -198,8 +198,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"未連線"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"沒有網路"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"關閉 Wi-Fi"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi 顯示裝置"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"無線螢幕分享"</string>
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <skip />
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"網路可能\n受到監控"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index d43ccfc..f284b7c 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -196,8 +196,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Akuxhunyiwe"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ayikho inethiwekhi"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"I-Wi-Fi icimile"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Ukuboniswa kwe-Wi-"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Ukubonisa okungenazintambo"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Isikrini sabalingisi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ukugqama"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OKUZENZAKALELAYO"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Kungenzeka inethiwekhi\niqashiwe"</string>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e6fcdff..e36ca8e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -490,10 +490,8 @@
<string name="quick_settings_wifi_no_network">No Network</string>
<!-- QuickSettings: Wifi (Off) [CHAR LIMIT=NONE] -->
<string name="quick_settings_wifi_off_label">Wi-Fi Off</string>
- <!-- QuickSettings: Wifi display [CHAR LIMIT=NONE] -->
- <string name="quick_settings_wifi_display_label">Wi-Fi Display</string>
- <!-- QuickSettings: Wifi display [CHAR LIMIT=NONE] -->
- <string name="quick_settings_wifi_display_no_connection_label">Wireless Display</string>
+ <!-- QuickSettings: Remote display [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_remote_display_no_connection_label">Cast Screen</string>
<!-- QuickSettings: Brightness dialog title [CHAR LIMIT=NONE] -->
<string name="quick_settings_brightness_dialog_title">Brightness</string>
<!-- QuickSettings: Brightness dialog auto brightness button [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 607ce41..bbac4ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -2105,9 +2105,12 @@
}
public void tickerHalting() {
- mStatusBarContents.setVisibility(View.VISIBLE);
+ if (mStatusBarContents.getVisibility() != View.VISIBLE) {
+ mStatusBarContents.setVisibility(View.VISIBLE);
+ mStatusBarContents
+ .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
+ }
mTickerView.setVisibility(View.GONE);
- mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
// we do not animate the ticker away at this point, just get rid of it (b/6992707)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index 2eef2b3..e7b8fa1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -37,9 +37,8 @@
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LevelListDrawable;
import android.hardware.display.DisplayManager;
-import android.hardware.display.WifiDisplayStatus;
+import android.media.MediaRouter;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Handler;
@@ -62,6 +61,7 @@
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.internal.app.MediaRouteDialogPresenter;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.QuickSettingsModel.ActivityState;
import com.android.systemui.statusbar.phone.QuickSettingsModel.BluetoothState;
@@ -92,9 +92,7 @@
private QuickSettingsModel mModel;
private ViewGroup mContainerView;
- private DisplayManager mDisplayManager;
private DevicePolicyManager mDevicePolicyManager;
- private WifiDisplayStatus mWifiDisplayStatus;
private PhoneStatusBar mStatusBarService;
private BluetoothState mBluetoothState;
private BluetoothAdapter mBluetoothAdapter;
@@ -118,13 +116,11 @@
new ArrayList<QuickSettingsTileView>();
public QuickSettings(Context context, QuickSettingsContainerView container) {
- mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
mDevicePolicyManager
= (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
mContext = context;
mContainerView = container;
mModel = new QuickSettingsModel(context);
- mWifiDisplayStatus = new WifiDisplayStatus();
mBluetoothState = new QuickSettingsModel.BluetoothState();
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
@@ -171,7 +167,6 @@
mLocationController = locationController;
setupQuickSettings();
- updateWifiDisplayStatus();
updateResources();
applyLocationEnabledStatus();
@@ -676,20 +671,33 @@
});
parent.addView(alarmTile);
- // Wifi Display
- QuickSettingsBasicTile wifiDisplayTile
+ // Remote Display
+ QuickSettingsBasicTile remoteDisplayTile
= new QuickSettingsBasicTile(mContext);
- wifiDisplayTile.setImageResource(R.drawable.ic_qs_remote_display);
- wifiDisplayTile.setOnClickListener(new View.OnClickListener() {
+ remoteDisplayTile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- startSettingsActivity(android.provider.Settings.ACTION_WIFI_DISPLAY_SETTINGS);
+ collapsePanels();
+
+ final Dialog[] dialog = new Dialog[1];
+ dialog[0] = MediaRouteDialogPresenter.createDialog(mContext,
+ MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dialog[0].dismiss();
+ startSettingsActivity(
+ android.provider.Settings.ACTION_WIFI_DISPLAY_SETTINGS);
+ }
+ });
+ dialog[0].getWindow().setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+ dialog[0].show();
}
});
- mModel.addWifiDisplayTile(wifiDisplayTile,
- new QuickSettingsModel.BasicRefreshCallback(wifiDisplayTile)
+ mModel.addRemoteDisplayTile(remoteDisplayTile,
+ new QuickSettingsModel.BasicRefreshCallback(remoteDisplayTile)
.setShowWhenEnabled(true));
- parent.addView(wifiDisplayTile);
+ parent.addView(remoteDisplayTile);
if (SHOW_IME_TILE || DEBUG_GONE_TILES) {
// IME
@@ -824,15 +832,6 @@
dialog.show();
}
- private void updateWifiDisplayStatus() {
- mWifiDisplayStatus = mDisplayManager.getWifiDisplayStatus();
- applyWifiDisplayStatus();
- }
-
- private void applyWifiDisplayStatus() {
- mModel.onWifiDisplayStateChanged(mWifiDisplayStatus);
- }
-
private void applyBluetoothStatus() {
mModel.onBluetoothStateChange(mBluetoothState);
}
@@ -856,12 +855,7 @@
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- if (DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED.equals(action)) {
- WifiDisplayStatus status = (WifiDisplayStatus)intent.getParcelableExtra(
- DisplayManager.EXTRA_WIFI_DISPLAY_STATUS);
- mWifiDisplayStatus = status;
- applyWifiDisplayStatus();
- } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
+ if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
BluetoothAdapter.ERROR);
mBluetoothState.enabled = (state == BluetoothAdapter.STATE_ON);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index 15d655c..e59dfaa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -27,7 +27,8 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
-import android.hardware.display.WifiDisplayStatus;
+import android.media.MediaRouter;
+import android.media.MediaRouter.RouteInfo;
import android.net.ConnectivityManager;
import android.os.Handler;
import android.os.UserHandle;
@@ -57,7 +58,6 @@
BrightnessStateChangeCallback,
RotationLockControllerCallback,
LocationSettingsChangeCallback {
-
// Sett InputMethoManagerService
private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
@@ -199,6 +199,30 @@
}
}
+ /** Callback for changes to remote display routes. */
+ private class RemoteDisplayRouteCallback extends MediaRouter.SimpleCallback {
+ @Override
+ public void onRouteAdded(MediaRouter router, RouteInfo route) {
+ updateRemoteDisplays();
+ }
+ @Override
+ public void onRouteChanged(MediaRouter router, RouteInfo route) {
+ updateRemoteDisplays();
+ }
+ @Override
+ public void onRouteRemoved(MediaRouter router, RouteInfo route) {
+ updateRemoteDisplays();
+ }
+ @Override
+ public void onRouteSelected(MediaRouter router, int type, RouteInfo route) {
+ updateRemoteDisplays();
+ }
+ @Override
+ public void onRouteUnselected(MediaRouter router, int type, RouteInfo route) {
+ updateRemoteDisplays();
+ }
+ }
+
private final Context mContext;
private final Handler mHandler;
private final CurrentUserTracker mUserTracker;
@@ -206,6 +230,9 @@
private final BugreportObserver mBugreportObserver;
private final BrightnessObserver mBrightnessObserver;
+ private final MediaRouter mMediaRouter;
+ private final RemoteDisplayRouteCallback mRemoteDisplayRouteCallback;
+
private final boolean mHasMobileData;
private QuickSettingsTileView mUserTile;
@@ -228,9 +255,9 @@
private RefreshCallback mWifiCallback;
private WifiState mWifiState = new WifiState();
- private QuickSettingsTileView mWifiDisplayTile;
- private RefreshCallback mWifiDisplayCallback;
- private State mWifiDisplayState = new State();
+ private QuickSettingsTileView mRemoteDisplayTile;
+ private RefreshCallback mRemoteDisplayCallback;
+ private State mRemoteDisplayState = new State();
private QuickSettingsTileView mRSSITile;
private RefreshCallback mRSSICallback;
@@ -278,12 +305,14 @@
mContext = context;
mHandler = new Handler();
mUserTracker = new CurrentUserTracker(mContext) {
+ @Override
public void onUserSwitched(int newUserId) {
mBrightnessObserver.startObserving();
refreshRotationLockTile();
onBrightnessLevelChanged();
onNextAlarmChanged();
onBugreportChanged();
+ rebindMediaRouterAsCurrentUser();
}
};
@@ -294,6 +323,11 @@
mBrightnessObserver = new BrightnessObserver(mHandler);
mBrightnessObserver.startObserving();
+ mMediaRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+ rebindMediaRouterAsCurrentUser();
+
+ mRemoteDisplayRouteCallback = new RemoteDisplayRouteCallback();
+
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
mHasMobileData = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
@@ -621,24 +655,66 @@
mBugreportCallback.refreshView(mBugreportTile, mBugreportState);
}
- // Wifi Display
- void addWifiDisplayTile(QuickSettingsTileView view, RefreshCallback cb) {
- mWifiDisplayTile = view;
- mWifiDisplayCallback = cb;
- }
- public void onWifiDisplayStateChanged(WifiDisplayStatus status) {
- mWifiDisplayState.enabled =
- (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON);
- if (status.getActiveDisplay() != null) {
- mWifiDisplayState.label = status.getActiveDisplay().getFriendlyDisplayName();
- mWifiDisplayState.iconId = R.drawable.ic_qs_remote_display_connected;
- } else {
- mWifiDisplayState.label = mContext.getString(
- R.string.quick_settings_wifi_display_no_connection_label);
- mWifiDisplayState.iconId = R.drawable.ic_qs_remote_display;
- }
- mWifiDisplayCallback.refreshView(mWifiDisplayTile, mWifiDisplayState);
+ // Remote Display
+ void addRemoteDisplayTile(QuickSettingsTileView view, RefreshCallback cb) {
+ mRemoteDisplayTile = view;
+ mRemoteDisplayCallback = cb;
+ final int[] count = new int[1];
+ mRemoteDisplayTile.setOnPrepareListener(new QuickSettingsTileView.OnPrepareListener() {
+ @Override
+ public void onPrepare() {
+ mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
+ mRemoteDisplayRouteCallback,
+ MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
+ updateRemoteDisplays();
+ }
+ @Override
+ public void onUnprepare() {
+ mMediaRouter.removeCallback(mRemoteDisplayRouteCallback);
+ }
+ });
+ updateRemoteDisplays();
+ }
+
+ private void rebindMediaRouterAsCurrentUser() {
+ mMediaRouter.rebindAsUser(mUserTracker.getCurrentUserId());
+ }
+
+ private void updateRemoteDisplays() {
+ MediaRouter.RouteInfo connectedRoute = mMediaRouter.getSelectedRoute(
+ MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
+ boolean enabled = connectedRoute != null
+ && connectedRoute.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
+ boolean connecting;
+ if (enabled) {
+ connecting = connectedRoute.isConnecting();
+ } else {
+ connectedRoute = null;
+ connecting = false;
+ final int count = mMediaRouter.getRouteCount();
+ for (int i = 0; i < count; i++) {
+ MediaRouter.RouteInfo route = mMediaRouter.getRouteAt(i);
+ if (route.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) {
+ enabled = true;
+ break;
+ }
+ }
+ }
+
+ mRemoteDisplayState.enabled = enabled;
+ if (connectedRoute != null) {
+ mRemoteDisplayState.label = connectedRoute.getName().toString();
+ mRemoteDisplayState.iconId = connecting ?
+ com.android.internal.R.drawable.ic_media_route_connecting_holo_dark :
+ com.android.internal.R.drawable.ic_media_route_on_holo_dark;
+ } else {
+ mRemoteDisplayState.label = mContext.getString(
+ R.string.quick_settings_remote_display_no_connection_label);
+ mRemoteDisplayState.iconId =
+ com.android.internal.R.drawable.ic_media_route_off_holo_dark;
+ }
+ mRemoteDisplayCallback.refreshView(mRemoteDisplayTile, mRemoteDisplayState);
}
// IME
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java
index 3d520f7..ad18294 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsTileView.java
@@ -21,6 +21,7 @@
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewParent;
import android.widget.FrameLayout;
/**
@@ -31,14 +32,14 @@
private int mContentLayoutId;
private int mColSpan;
- private int mRowSpan;
+ private boolean mPrepared;
+ private OnPrepareListener mOnPrepareListener;
public QuickSettingsTileView(Context context, AttributeSet attrs) {
super(context, attrs);
mContentLayoutId = -1;
mColSpan = 1;
- mRowSpan = 1;
}
void setColumnSpan(int span) {
@@ -77,4 +78,72 @@
}
super.setVisibility(vis);
}
+
+ public void setOnPrepareListener(OnPrepareListener listener) {
+ if (mOnPrepareListener != listener) {
+ mOnPrepareListener = listener;
+ mPrepared = false;
+ post(new Runnable() {
+ @Override
+ public void run() {
+ updatePreparedState();
+ }
+ });
+ }
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ updatePreparedState();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ updatePreparedState();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ updatePreparedState();
+ }
+
+ private void updatePreparedState() {
+ if (mOnPrepareListener != null) {
+ if (isParentVisible()) {
+ if (!mPrepared) {
+ mPrepared = true;
+ mOnPrepareListener.onPrepare();
+ }
+ } else if (mPrepared) {
+ mPrepared = false;
+ mOnPrepareListener.onUnprepare();
+ }
+ }
+ }
+
+ private boolean isParentVisible() {
+ if (!isAttachedToWindow()) {
+ return false;
+ }
+ for (ViewParent current = getParent(); current instanceof View;
+ current = current.getParent()) {
+ View view = (View)current;
+ if (view.getVisibility() != VISIBLE) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Called when the view's parent becomes visible or invisible to provide
+ * an opportunity for the client to provide new content.
+ */
+ public interface OnPrepareListener {
+ void onPrepare();
+ void onUnprepare();
+ }
}
\ No newline at end of file
diff --git a/policy/src/com/android/internal/policy/impl/BarController.java b/policy/src/com/android/internal/policy/impl/BarController.java
index c38ad04..0ce4b12 100644
--- a/policy/src/com/android/internal/policy/impl/BarController.java
+++ b/policy/src/com/android/internal/policy/impl/BarController.java
@@ -37,8 +37,9 @@
private static final boolean DEBUG = false;
private static final int TRANSIENT_BAR_NONE = 0;
- private static final int TRANSIENT_BAR_SHOWING = 1;
- private static final int TRANSIENT_BAR_HIDING = 2;
+ private static final int TRANSIENT_BAR_SHOW_REQUESTED = 1;
+ private static final int TRANSIENT_BAR_SHOWING = 2;
+ private static final int TRANSIENT_BAR_HIDING = 3;
private static final int TRANSLUCENT_ANIMATION_DELAY_MS = 1000;
@@ -73,13 +74,9 @@
mWin = win;
}
- public boolean isHidden() {
- return mState == StatusBarManager.WINDOW_STATE_HIDDEN;
- }
-
public void showTransient() {
if (mWin != null) {
- setTransientBarState(TRANSIENT_BAR_SHOWING);
+ setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED);
}
}
@@ -87,6 +84,10 @@
return mTransientBarState == TRANSIENT_BAR_SHOWING;
}
+ public boolean isTransientShowRequested() {
+ return mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED;
+ }
+
public boolean wasRecentlyTranslucent() {
return (SystemClock.uptimeMillis() - mLastTranslucent) < TRANSLUCENT_ANIMATION_DELAY_MS;
}
@@ -198,6 +199,9 @@
if (mTransientBarState == TRANSIENT_BAR_SHOWING) {
if (DEBUG) Slog.d(mTag, "Not showing transient bar, already shown");
return false;
+ } else if (mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED) {
+ if (DEBUG) Slog.d(mTag, "Not showing transient bar, already requested");
+ return false;
} else if (mWin == null) {
if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar doesn't exist");
return false;
@@ -211,12 +215,13 @@
public int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) {
if (mWin == null) return vis;
- if (mTransientBarState == TRANSIENT_BAR_SHOWING) { // transient bar requested
+ if (isTransientShowing() || isTransientShowRequested()) { // transient bar requested
if (transientAllowed) {
vis |= mTransientFlag;
if ((oldVis & mTransientFlag) == 0) {
vis |= mUnhideFlag; // tell sysui we're ready to unhide
}
+ setTransientBarState(TRANSIENT_BAR_SHOWING); // request accepted
} else {
setTransientBarState(TRANSIENT_BAR_NONE); // request denied
}
@@ -254,6 +259,7 @@
private static String transientBarStateToString(int state) {
if (state == TRANSIENT_BAR_HIDING) return "TRANSIENT_BAR_HIDING";
if (state == TRANSIENT_BAR_SHOWING) return "TRANSIENT_BAR_SHOWING";
+ if (state == TRANSIENT_BAR_SHOW_REQUESTED) return "TRANSIENT_BAR_SHOW_REQUESTED";
if (state == TRANSIENT_BAR_NONE) return "TRANSIENT_BAR_NONE";
throw new IllegalArgumentException("Unknown state " + state);
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index faca949..c33bd35 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -3468,6 +3468,11 @@
}
// Maintain fullscreen layout until incoming animation is complete.
topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw();
+ // Transient status bar on the lockscreen is not allowed
+ if (mForceStatusBarFromKeyguard && mStatusBarController.isTransientShowing()) {
+ mStatusBarController.updateVisibilityLw(false /*transientAllowed*/,
+ mLastSystemUiFlags, mLastSystemUiFlags);
+ }
} else if (mTopFullscreenOpaqueWindowState != null) {
if (localLOGV) {
Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()
@@ -5158,9 +5163,9 @@
mNavigationBar != null &&
hideNavBarSysui && immersiveSticky;
- boolean denyTransientStatus = mStatusBarController.isTransientShowing()
+ boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
&& !transientStatusBarAllowed && hideStatusBarSysui;
- boolean denyTransientNav = mNavigationBarController.isTransientShowing()
+ boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
&& !transientNavBarAllowed;
if (denyTransientStatus || denyTransientNav) {
// clear the clearable flags instead
diff --git a/services/java/com/android/server/LockSettingsService.java b/services/java/com/android/server/LockSettingsService.java
index cd746cf..35e7afa 100644
--- a/services/java/com/android/server/LockSettingsService.java
+++ b/services/java/com/android/server/LockSettingsService.java
@@ -154,11 +154,11 @@
}
private final void checkWritePermission(int userId) {
- mContext.checkCallingOrSelfPermission(PERMISSION);
+ mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
}
private final void checkPasswordReadPermission(int userId) {
- mContext.checkCallingOrSelfPermission(PERMISSION);
+ mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead");
}
private final void checkReadPermission(String requestedKey, int userId) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index bb14259..a42cbcf 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -55,6 +55,7 @@
import com.android.server.display.DisplayManagerService;
import com.android.server.dreams.DreamManagerService;
import com.android.server.input.InputManagerService;
+import com.android.server.media.MediaRouterService;
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.net.NetworkStatsService;
import com.android.server.os.SchedulingPolicyService;
@@ -356,6 +357,7 @@
DreamManagerService dreamy = null;
AssetAtlasService atlas = null;
PrintManagerService printManager = null;
+ MediaRouterService mediaRouter = null;
// Bring up services needed for UI.
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
@@ -804,6 +806,16 @@
} catch (Throwable e) {
reportWtf("starting Print Service", e);
}
+
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "Media Router Service");
+ mediaRouter = new MediaRouterService(context);
+ ServiceManager.addService(Context.MEDIA_ROUTER_SERVICE, mediaRouter);
+ } catch (Throwable e) {
+ reportWtf("starting MediaRouterService", e);
+ }
+ }
}
// Before things start rolling, be sure we have decided whether
@@ -916,6 +928,7 @@
final InputManagerService inputManagerF = inputManager;
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
final PrintManagerService printManagerF = printManager;
+ final MediaRouterService mediaRouterF = mediaRouter;
// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
@@ -1063,6 +1076,12 @@
} catch (Throwable e) {
reportWtf("Notifying PrintManagerService running", e);
}
+
+ try {
+ if (mediaRouterF != null) mediaRouterF.systemRunning();
+ } catch (Throwable e) {
+ reportWtf("Notifying MediaRouterService running", e);
+ }
}
});
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index f972f70..aa9849e 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -190,10 +190,10 @@
private final HashMap<String, Account[]> accountCache =
new LinkedHashMap<String, Account[]>();
/** protected by the {@link #cacheLock} */
- private HashMap<Account, HashMap<String, String>> userDataCache =
+ private final HashMap<Account, HashMap<String, String>> userDataCache =
new HashMap<Account, HashMap<String, String>>();
/** protected by the {@link #cacheLock} */
- private HashMap<Account, HashMap<String, String>> authTokenCache =
+ private final HashMap<Account, HashMap<String, String>> authTokenCache =
new HashMap<Account, HashMap<String, String>>();
UserAccounts(Context context, int userId) {
@@ -475,6 +475,7 @@
validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
}
+ @Override
public String getPassword(Account account) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getPassword: " + account
@@ -514,6 +515,7 @@
}
}
+ @Override
public String getUserData(Account account, String key) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getUserData: " + account
@@ -533,6 +535,7 @@
}
}
+ @Override
public AuthenticatorDescription[] getAuthenticatorTypes() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getAuthenticatorTypes: "
@@ -763,6 +766,7 @@
return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
}
+ @Override
public void hasFeatures(IAccountManagerResponse response,
Account account, String[] features) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -840,6 +844,7 @@
}
}
+ @Override
public void removeAccount(IAccountManagerResponse response, Account account) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "removeAccount: " + account
@@ -1049,6 +1054,7 @@
}
}
+ @Override
public String peekAuthToken(Account account, String authTokenType) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "peekAuthToken: " + account
@@ -1068,6 +1074,7 @@
}
}
+ @Override
public void setAuthToken(Account account, String authTokenType, String authToken) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setAuthToken: " + account
@@ -1087,6 +1094,7 @@
}
}
+ @Override
public void setPassword(Account account, String password) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setAuthToken: " + account
@@ -1135,6 +1143,7 @@
mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
}
+ @Override
public void clearPassword(Account account) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "clearPassword: " + account
@@ -1152,6 +1161,7 @@
}
}
+ @Override
public void setUserData(Account account, String key, String value) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setUserData: " + account
@@ -1225,6 +1235,7 @@
}
}
+ @Override
public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
final String authTokenType)
throws RemoteException {
@@ -1271,6 +1282,7 @@
}
}
+ @Override
public void getAuthToken(IAccountManagerResponse response, final Account account,
final String authTokenType, final boolean notifyOnAuthFailure,
final boolean expectActivityLaunch, Bundle loginOptionsIn) {
@@ -1284,8 +1296,22 @@
+ ", pid " + Binder.getCallingPid());
}
if (response == null) throw new IllegalArgumentException("response is null");
- if (account == null) throw new IllegalArgumentException("account is null");
- if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
+ try {
+ if (account == null) {
+ Slog.w(TAG, "getAuthToken called with null account");
+ response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
+ return;
+ }
+ if (authTokenType == null) {
+ Slog.w(TAG, "getAuthToken called with null authTokenType");
+ response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
+ return;
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to report error back to the client." + e);
+ return;
+ }
+
checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
final UserAccounts accounts = getUserAccountsForCaller();
final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
@@ -1294,11 +1320,6 @@
final boolean customTokens =
authenticatorInfo != null && authenticatorInfo.type.customTokens;
- // Check to see that the app is authorized to access the account, in case it's a
- // restricted account.
- if (!ArrayUtils.contains(getAccounts((String) null), account)) {
- throw new IllegalArgumentException("no such account");
- }
// skip the check if customTokens
final int callerUid = Binder.getCallingUid();
final boolean permissionGranted = customTokens ||
@@ -1472,6 +1493,7 @@
return id;
}
+ @Override
public void addAccount(final IAccountManagerResponse response, final String accountType,
final String authTokenType, final String[] requiredFeatures,
final boolean expectActivityLaunch, final Bundle optionsIn) {
@@ -1582,6 +1604,7 @@
}
}
+ @Override
public void updateCredentials(IAccountManagerResponse response, final Account account,
final String authTokenType, final boolean expectActivityLaunch,
final Bundle loginOptions) {
@@ -1620,6 +1643,7 @@
}
}
+ @Override
public void editProperties(IAccountManagerResponse response, final String accountType,
final boolean expectActivityLaunch) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -1657,7 +1681,7 @@
private volatile Account[] mAccountsOfType = null;
private volatile ArrayList<Account> mAccountsWithFeatures = null;
private volatile int mCurrentAccount = 0;
- private int mCallingUid;
+ private final int mCallingUid;
public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
IAccountManagerResponse response, String type, String[] features, int callingUid) {
@@ -1941,6 +1965,7 @@
return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName, packageUid);
}
+ @Override
public void getAccountsByFeatures(IAccountManagerResponse response,
String type, String[] features) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -2069,6 +2094,7 @@
unbind();
}
+ @Override
public void binderDied() {
mResponse = null;
close();
@@ -2112,6 +2138,7 @@
mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
}
+ @Override
public void onServiceConnected(ComponentName name, IBinder service) {
mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
try {
@@ -2122,6 +2149,7 @@
}
}
+ @Override
public void onServiceDisconnected(ComponentName name) {
mAuthenticator = null;
IAccountManagerResponse response = getResponseAndClose();
@@ -2217,7 +2245,14 @@
Log.v(TAG, getClass().getSimpleName()
+ " calling onResult() on response " + response);
}
- response.onResult(result);
+ if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
+ (intent == null)) {
+ // All AccountManager error codes are greater than 0
+ response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
+ result.getString(AccountManager.KEY_ERROR_MESSAGE));
+ } else {
+ response.onResult(result);
+ }
}
} catch (RemoteException e) {
// if the caller is dead then there is no one to care about remote exceptions
@@ -2228,10 +2263,12 @@
}
}
+ @Override
public void onRequestContinued() {
mNumRequestContinued++;
}
+ @Override
public void onError(int errorCode, String errorMessage) {
mNumErrors++;
IAccountManagerResponse response = getResponseAndClose();
@@ -2731,6 +2768,7 @@
return true;
}
+ @Override
public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
throws RemoteException {
final int callingUid = getCallingUid();
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index ea0b978a..a80afbc 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -307,7 +307,7 @@
ServiceRecord r = res.record;
NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
callingUid, r.packageName, service, service.getFlags(), null);
- if (unscheduleServiceRestartLocked(r, callingUid)) {
+ if (unscheduleServiceRestartLocked(r, callingUid, false)) {
if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
}
r.lastActivity = SystemClock.uptimeMillis();
@@ -701,7 +701,7 @@
final long origId = Binder.clearCallingIdentity();
try {
- if (unscheduleServiceRestartLocked(s, callerApp.info.uid)) {
+ if (unscheduleServiceRestartLocked(s, callerApp.info.uid, false)) {
if (DEBUG_SERVICE) Slog.v(TAG, "BIND SERVICE WHILE RESTART PENDING: "
+ s);
}
@@ -1002,14 +1002,11 @@
smap.mServicesByIntent.put(filter, r);
// Make sure this component isn't in the pending list.
- int N = mPendingServices.size();
- for (int i=0; i<N; i++) {
+ for (int i=mPendingServices.size()-1; i>=0; i--) {
ServiceRecord pr = mPendingServices.get(i);
if (pr.serviceInfo.applicationInfo.uid == sInfo.applicationInfo.uid
&& pr.name.equals(name)) {
mPendingServices.remove(i);
- i--;
- N--;
}
}
}
@@ -1101,6 +1098,14 @@
boolean allowCancel) {
boolean canceled = false;
+ ServiceMap smap = getServiceMap(r.userId);
+ if (smap.mServicesByName.get(r.name) != r) {
+ ServiceRecord cur = smap.mServicesByName.get(r.name);
+ Slog.wtf(TAG, "Attempting to schedule restart of " + r
+ + " when found in map: " + cur);
+ return false;
+ }
+
final long now = SystemClock.uptimeMillis();
if ((r.serviceInfo.applicationInfo.flags
@@ -1187,6 +1192,7 @@
if (!mRestartingServices.contains(r)) {
r.createdFromFg = false;
mRestartingServices.add(r);
+ r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);
}
r.cancelNotification();
@@ -1209,8 +1215,9 @@
bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true);
}
- private final boolean unscheduleServiceRestartLocked(ServiceRecord r, int callingUid) {
- if (r.restartDelay == 0) {
+ private final boolean unscheduleServiceRestartLocked(ServiceRecord r, int callingUid,
+ boolean force) {
+ if (!force && r.restartDelay == 0) {
return false;
}
// Remove from the restarting list; if the service is currently on the
@@ -1220,10 +1227,32 @@
if (removed || callingUid != r.appInfo.uid) {
r.resetRestartCounter();
}
+ if (removed) {
+ clearRestartingIfNeededLocked(r);
+ }
mAm.mHandler.removeCallbacks(r.restarter);
return true;
}
+ private void clearRestartingIfNeededLocked(ServiceRecord r) {
+ if (r.restartTracker != null) {
+ // If this is the last restarting record with this tracker, then clear
+ // the tracker's restarting state.
+ boolean stillTracking = false;
+ for (int i=mRestartingServices.size()-1; i>=0; i--) {
+ if (mRestartingServices.get(i).restartTracker == r.restartTracker) {
+ stillTracking = true;
+ break;
+ }
+ }
+ if (!stillTracking) {
+ r.restartTracker.setRestarting(false, mAm.mProcessStats.getMemFactorLocked(),
+ SystemClock.uptimeMillis());
+ r.restartTracker = null;
+ }
+ }
+ }
+
private final String bringUpServiceLocked(ServiceRecord r,
int intentFlags, boolean execInFg, boolean whileRestarting) {
//Slog.i(TAG, "Bring up service:");
@@ -1243,7 +1272,9 @@
// We are now bringing the service up, so no longer in the
// restarting state.
- mRestartingServices.remove(r);
+ if (mRestartingServices.remove(r)) {
+ clearRestartingIfNeededLocked(r);
+ }
// Make sure this service is no longer considered delayed, we are starting it now.
if (r.delayed) {
@@ -1383,6 +1414,7 @@
} finally {
if (!created) {
app.services.remove(r);
+ r.app = null;
scheduleServiceRestartLocked(r, false);
}
}
@@ -1553,16 +1585,13 @@
smap.mServicesByName.remove(r.name);
smap.mServicesByIntent.remove(r.intent);
r.totalRestartCount = 0;
- unscheduleServiceRestartLocked(r, 0);
+ unscheduleServiceRestartLocked(r, 0, true);
// Also make sure it is not on the pending list.
- int N = mPendingServices.size();
- for (int i=0; i<N; i++) {
+ for (int i=mPendingServices.size()-1; i>=0; i--) {
if (mPendingServices.get(i) == r) {
mPendingServices.remove(i);
if (DEBUG_SERVICE) Slog.v(TAG, "Removed pending: " + r);
- i--;
- N--;
}
}
@@ -1581,6 +1610,7 @@
}
r.app.services.remove(r);
if (r.app.thread != null) {
+ updateServiceForegroundLocked(r.app, false);
try {
bumpServiceExecutingLocked(r, false, "destroy");
mDestroyingServices.add(r);
@@ -1591,7 +1621,6 @@
+ r.shortName, e);
serviceProcessGoneLocked(r);
}
- updateServiceForegroundLocked(r.app, false);
} else {
if (DEBUG_SERVICE) Slog.v(
TAG, "Removed service that has no process: " + r);
@@ -1769,6 +1798,7 @@
long now = SystemClock.uptimeMillis();
r.tracker.setExecuting(false, memFactor, now);
r.tracker.setBound(false, memFactor, now);
+ r.tracker.setStarted(false, memFactor, now);
}
serviceDoneExecutingLocked(r, true, true);
}
@@ -1816,6 +1846,9 @@
r.tracker = null;
}
}
+ if (finishing) {
+ r.app = null;
+ }
}
}
@@ -1960,8 +1993,7 @@
}
}
- final void killServicesLocked(ProcessRecord app,
- boolean allowRestart) {
+ final void killServicesLocked(ProcessRecord app, boolean allowRestart) {
// Report disconnected services.
if (false) {
// XXX we are letting the client link to the service for
@@ -1990,16 +2022,8 @@
}
}
- // Clean up any connections this application has to other services.
- for (int i=app.connections.size()-1; i>=0; i--) {
- ConnectionRecord r = app.connections.valueAt(i);
- removeConnectionLocked(r, app, null);
- }
- app.connections.clear();
-
+ // First clear app state from services.
for (int i=app.services.size()-1; i>=0; i--) {
- // Any services running in the application need to be placed
- // back in the pending list.
ServiceRecord sr = app.services.valueAt(i);
synchronized (sr.stats.getBatteryStats()) {
sr.stats.stopLaunchedLocked();
@@ -2020,8 +2044,21 @@
b.binder = null;
b.requested = b.received = b.hasBound = false;
}
+ }
- if (sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags
+ // Clean up any connections this application has to other services.
+ for (int i=app.connections.size()-1; i>=0; i--) {
+ ConnectionRecord r = app.connections.valueAt(i);
+ removeConnectionLocked(r, app, null);
+ }
+ app.connections.clear();
+
+ // Now do remaining service cleanup.
+ for (int i=app.services.size()-1; i>=0; i--) {
+ // Any services running in the application may need to be placed
+ // back in the pending list.
+ ServiceRecord sr = app.services.valueAt(i);
+ if (allowRestart && sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags
&ApplicationInfo.FLAG_PERSISTENT) == 0) {
Slog.w(TAG, "Service crashed " + sr.crashCount
+ " times, stopping: " + sr);
@@ -2054,6 +2091,23 @@
if (!allowRestart) {
app.services.clear();
+
+ // Make sure there are no more restarting services for this process.
+ for (int i=mRestartingServices.size()-1; i>=0; i--) {
+ ServiceRecord r = mRestartingServices.get(i);
+ if (r.processName.equals(app.processName) &&
+ r.serviceInfo.applicationInfo.uid == app.info.uid) {
+ mRestartingServices.remove(i);
+ clearRestartingIfNeededLocked(r);
+ }
+ }
+ for (int i=mPendingServices.size()-1; i>=0; i--) {
+ ServiceRecord r = mPendingServices.get(i);
+ if (r.processName.equals(app.processName) &&
+ r.serviceInfo.applicationInfo.uid == app.info.uid) {
+ mPendingServices.remove(i);
+ }
+ }
}
// Make sure we have no more records on the stopping list.
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 164e7b7..fe83e9f 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -11901,6 +11901,7 @@
boolean dumpDalvik = false;
boolean oomOnly = false;
boolean isCompact = false;
+ boolean localOnly = false;
int opti = 0;
while (opti < args.length) {
@@ -11919,12 +11920,15 @@
isCompact = true;
} else if ("--oom".equals(opt)) {
oomOnly = true;
+ } else if ("--local".equals(opt)) {
+ localOnly = true;
} else if ("-h".equals(opt)) {
pw.println("meminfo dump options: [-a] [-d] [-c] [--oom] [process]");
pw.println(" -a: include all available information for each process.");
pw.println(" -d: include dalvik details when dumping process details.");
pw.println(" -c: dump in a compact machine-parseable representation.");
pw.println(" --oom: only show processes organized by oom adj.");
+ pw.println(" --local: only collect details locally, don't call process.");
pw.println("If [process] is specified it can be the name or ");
pw.println("pid of a specific process to dump.");
return;
@@ -12041,14 +12045,22 @@
mi.dalvikPrivateDirty = (int)tmpLong[0];
}
if (dumpDetails) {
- try {
- pw.flush();
- thread.dumpMemInfo(fd, mi, isCheckinRequest, dumpFullDetails,
- dumpDalvik, innerArgs);
- } catch (RemoteException e) {
- if (!isCheckinRequest) {
- pw.println("Got RemoteException!");
+ if (localOnly) {
+ ActivityThread.dumpMemInfoTable(pw, mi, isCheckinRequest, dumpFullDetails,
+ dumpDalvik, pid, r.processName, 0, 0, 0, 0, 0, 0);
+ if (isCheckinRequest) {
+ pw.println();
+ }
+ } else {
+ try {
pw.flush();
+ thread.dumpMemInfo(fd, mi, isCheckinRequest, dumpFullDetails,
+ dumpDalvik, innerArgs);
+ } catch (RemoteException e) {
+ if (!isCheckinRequest) {
+ pw.println("Got RemoteException!");
+ pw.flush();
+ }
}
}
}
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 809452c..569440d 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -36,7 +36,6 @@
import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES;
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
-import android.os.Trace;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.util.Objects;
import com.android.server.Watchdog;
@@ -64,12 +63,14 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.util.EventLog;
import android.util.Slog;
@@ -1143,7 +1144,7 @@
} else if (isActivityOverHome(r)) {
if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r);
showHomeBehindStack = true;
- behindFullscreen = true;
+ behindFullscreen = !isHomeStack();
}
} else {
if (DEBUG_VISBILITY) Slog.v(
@@ -1910,26 +1911,38 @@
// bottom of the activity stack. This also keeps it
// correctly ordered with any activities we previously
// moved.
+ final ThumbnailHolder newThumbHolder;
+ final TaskRecord targetTask;
final ActivityRecord bottom =
!mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ?
- mTaskHistory.get(0).mActivities.get(0) : null;
+ mTaskHistory.get(0).mActivities.get(0) : null;
if (bottom != null && target.taskAffinity != null
&& target.taskAffinity.equals(bottom.task.affinity)) {
// If the activity currently at the bottom has the
// same task affinity as the one we are moving,
// then merge it into the same task.
- target.setTask(bottom.task, bottom.thumbHolder, false);
+ targetTask = bottom.task;
+ newThumbHolder = bottom.thumbHolder == null ? targetTask : bottom.thumbHolder;
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ " out to bottom task " + bottom.task);
} else {
- target.setTask(createTaskRecord(mStackSupervisor.getNextTaskId(), target.info,
- null, false), null, false);
- target.task.affinityIntent = target.intent;
+ targetTask = createTaskRecord(mStackSupervisor.getNextTaskId(), target.info,
+ null, false);
+ newThumbHolder = targetTask;
+ targetTask.affinityIntent = target.intent;
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ " out to new task " + target.task);
}
- final TaskRecord targetTask = target.task;
+ if (clearWhenTaskReset) {
+ // This is the start of a new sub-task.
+ if (target.thumbHolder == null) {
+ target.thumbHolder = new ThumbnailHolder();
+ }
+ } else {
+ target.thumbHolder = newThumbHolder;
+ }
+
final int targetTaskId = targetTask.taskId;
mWindowManager.setAppGroupId(target.appToken, targetTaskId);
@@ -1950,8 +1963,8 @@
}
}
if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing activity " + p + " from task="
- + task + " adding to task=" + targetTask,
- new RuntimeException("here").fillInStackTrace());
+ + task + " adding to task=" + targetTask
+ + " Callers=" + Debug.getCallers(4));
if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
+ " out to target's task " + target.task);
p.setTask(targetTask, curThumbHolder, false);
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 8251364..483b4a0 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1386,17 +1386,22 @@
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}
+ ActivityInfo newTaskInfo = null;
+ Intent newTaskIntent = null;
final ActivityStack sourceStack;
if (sourceRecord != null) {
if (sourceRecord.finishing) {
// If the source is finishing, we can't further count it as our source. This
// is because the task it is associated with may now be empty and on its way out,
// so we don't want to blindly throw it in to that task. Instead we will take
- // the NEW_TASK flow and try to find a task for it.
+ // the NEW_TASK flow and try to find a task for it. But save the task information
+ // so it can be used when creating the new task.
if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
Slog.w(TAG, "startActivity called from finishing " + sourceRecord
+ "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ newTaskInfo = sourceRecord.info;
+ newTaskIntent = sourceRecord.task.intent;
}
sourceRecord = null;
sourceStack = null;
@@ -1668,8 +1673,10 @@
targetStack = adjustStackFocus(r);
moveHomeStack(targetStack.isHomeStack());
if (reuseTask == null) {
- r.setTask(targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true),
- null, true);
+ r.setTask(targetStack.createTaskRecord(getNextTaskId(),
+ newTaskInfo != null ? newTaskInfo : r.info,
+ newTaskIntent != null ? newTaskIntent : intent,
+ true), null, true);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " +
r.task);
} else {
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 0dd950e..2d59678 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -419,6 +419,20 @@
}
}
+ public void noteWifiBatchedScanStartedFromSource(WorkSource ws, int csph) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteWifiBatchedScanStartedFromSourceLocked(ws, csph);
+ }
+ }
+
+ public void noteWifiBatchedScanStoppedFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteWifiBatchedScanStoppedFromSourceLocked(ws);
+ }
+ }
+
public void noteWifiMulticastEnabledFromSource(WorkSource ws) {
enforceCallingPermission();
synchronized (mStats) {
diff --git a/services/java/com/android/server/am/ConnectionRecord.java b/services/java/com/android/server/am/ConnectionRecord.java
index 576adc2..423e540 100644
--- a/services/java/com/android/server/am/ConnectionRecord.java
+++ b/services/java/com/android/server/am/ConnectionRecord.java
@@ -27,7 +27,7 @@
*/
final class ConnectionRecord {
final AppBindRecord binding; // The application/service binding.
- final ActivityRecord activity; // If non-null, the owning activity.
+ final ActivityRecord activity; // If non-null, the owning activity.
final IServiceConnection conn; // The client connection.
final int flags; // Binding options.
final int clientLabel; // String resource labeling this client.
diff --git a/services/java/com/android/server/am/ProcessStatsService.java b/services/java/com/android/server/am/ProcessStatsService.java
index 8d16880..e05fcda 100644
--- a/services/java/com/android/server/am/ProcessStatsService.java
+++ b/services/java/com/android/server/am/ProcessStatsService.java
@@ -750,23 +750,12 @@
return;
} else {
// Not an option, last argument must be a package name.
- try {
- IPackageManager pm = AppGlobals.getPackageManager();
- if (pm.getPackageUid(arg, UserHandle.getCallingUserId()) >= 0) {
- reqPackage = arg;
- // Include all details, since we know we are only going to
- // be dumping a smaller set of data. In fact only the details
- // container per-package data, so that are needed to be able
- // to dump anything at all when filtering by package.
- dumpDetails = true;
- }
- } catch (RemoteException e) {
- }
- if (reqPackage == null) {
- pw.println("Unknown package: " + arg);
- dumpHelp(pw);
- return;
- }
+ reqPackage = arg;
+ // Include all details, since we know we are only going to
+ // be dumping a smaller set of data. In fact only the details
+ // container per-package data, so that are needed to be able
+ // to dump anything at all when filtering by package.
+ dumpDetails = true;
}
}
}
@@ -816,13 +805,14 @@
}
return;
} else if (aggregateHours != 0) {
+ pw.print("AGGREGATED OVER LAST "); pw.print(aggregateHours); pw.println(" HOURS:");
dumpAggregatedStats(pw, aggregateHours, now, reqPackage, isCompact,
dumpDetails, dumpFullDetails, dumpAll, activeOnly);
return;
}
boolean sepNeeded = false;
- if (!currentOnly || isCheckin) {
+ if (dumpAll || isCheckin) {
mWriteLock.lock();
try {
ArrayList<String> files = getCommittedFiles(0, false, !isCheckin);
@@ -882,11 +872,11 @@
}
}
if (!isCheckin) {
- if (dumpAll) {
+ if (!currentOnly) {
if (sepNeeded) {
pw.println();
- pw.println("AGGREGATED OVER LAST 24 HOURS:");
}
+ pw.println("AGGREGATED OVER LAST 24 HOURS:");
dumpAggregatedStats(pw, 24, now, reqPackage, isCompact,
dumpDetails, dumpFullDetails, dumpAll, activeOnly);
pw.println();
@@ -901,8 +891,8 @@
} else {
if (sepNeeded) {
pw.println();
- pw.println("CURRENT STATS:");
}
+ pw.println("CURRENT STATS:");
if (dumpDetails || dumpFullDetails) {
mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll,
activeOnly);
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index cc1172a..80e6e94 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -85,6 +85,7 @@
ProcessRecord app; // where this service is running or null.
ProcessRecord isolatedProc; // keep track of isolated process, if requested
ProcessStats.ServiceState tracker; // tracking service execution, may be null
+ ProcessStats.ServiceState restartTracker; // tracking service restart
boolean delayed; // are we waiting to start this service in the background?
boolean isForeground; // is service currently in foreground mode?
int foregroundId; // Notification ID of last foreground req.
@@ -340,6 +341,19 @@
}
}
+ public void makeRestarting(int memFactor, long now) {
+ if (restartTracker == null) {
+ if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
+ restartTracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
+ serviceInfo.applicationInfo.uid, serviceInfo.processName, serviceInfo.name);
+ }
+ if (restartTracker == null) {
+ return;
+ }
+ }
+ restartTracker.setRestarting(true, memFactor, now);
+ }
+
public AppBindRecord retrieveAppBindingLocked(Intent intent,
ProcessRecord app) {
Intent.FilterComparison filter = new Intent.FilterComparison(intent);
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index f7bbdf8..99f8ebb 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -499,7 +499,7 @@
.setContentText(r.getString(
R.string.wifi_display_notification_message))
.setContentIntent(mSettingsPendingIntent)
- .setSmallIcon(R.drawable.ic_notify_wifidisplay)
+ .setSmallIcon(R.drawable.ic_media_route_on_holo_dark)
.setOngoing(true)
.addAction(android.R.drawable.ic_menu_close_clear_cancel,
r.getString(R.string.wifi_display_notification_disconnect),
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index d749e6c..3145805 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -294,6 +294,7 @@
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addDataScheme("package");
mContext.registerReceiver(new BroadcastReceiver() {
@Override
diff --git a/services/java/com/android/server/media/MediaRouterService.java b/services/java/com/android/server/media/MediaRouterService.java
new file mode 100644
index 0000000..1491eb6
--- /dev/null
+++ b/services/java/com/android/server/media/MediaRouterService.java
@@ -0,0 +1,1390 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import com.android.internal.util.Objects;
+import com.android.server.Watchdog;
+
+import android.Manifest;
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.media.AudioSystem;
+import android.media.IMediaRouterClient;
+import android.media.IMediaRouterService;
+import android.media.MediaRouter;
+import android.media.MediaRouterClientState;
+import android.media.RemoteDisplayState;
+import android.media.RemoteDisplayState.RemoteDisplayInfo;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Provides a mechanism for discovering media routes and manages media playback
+ * behalf of applications.
+ * <p>
+ * Currently supports discovering remote displays via remote display provider
+ * services that have been registered by applications.
+ * </p>
+ */
+public final class MediaRouterService extends IMediaRouterService.Stub
+ implements Watchdog.Monitor {
+ private static final String TAG = "MediaRouterService";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ /**
+ * Timeout in milliseconds for a selected route to transition from a
+ * disconnected state to a connecting state. If we don't observe any
+ * progress within this interval, then we will give up and unselect the route.
+ */
+ static final long CONNECTING_TIMEOUT = 5000;
+
+ /**
+ * Timeout in milliseconds for a selected route to transition from a
+ * connecting state to a connected state. If we don't observe any
+ * progress within this interval, then we will give up and unselect the route.
+ */
+ static final long CONNECTED_TIMEOUT = 60000;
+
+ private final Context mContext;
+
+ // State guarded by mLock.
+ private final Object mLock = new Object();
+ private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
+ private final ArrayMap<IBinder, ClientRecord> mAllClientRecords =
+ new ArrayMap<IBinder, ClientRecord>();
+ private int mCurrentUserId = -1;
+
+ public MediaRouterService(Context context) {
+ mContext = context;
+ Watchdog.getInstance().addMonitor(this);
+ }
+
+ public void systemRunning() {
+ IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_USER_SWITCHED)) {
+ switchUser();
+ }
+ }
+ }, filter);
+
+ switchUser();
+ }
+
+ @Override
+ public void monitor() {
+ synchronized (mLock) { /* check for deadlock */ }
+ }
+
+ // Binder call
+ @Override
+ public void registerClientAsUser(IMediaRouterClient client, String packageName, int userId) {
+ if (client == null) {
+ throw new IllegalArgumentException("client must not be null");
+ }
+
+ final int uid = Binder.getCallingUid();
+ if (!validatePackageName(uid, packageName)) {
+ throw new SecurityException("packageName must match the calling uid");
+ }
+
+ final int pid = Binder.getCallingPid();
+ final int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+ false /*allowAll*/, true /*requireFull*/, "registerClientAsUser", packageName);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ registerClientLocked(client, pid, packageName, resolvedUserId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Binder call
+ @Override
+ public void unregisterClient(IMediaRouterClient client) {
+ if (client == null) {
+ throw new IllegalArgumentException("client must not be null");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ unregisterClientLocked(client, false);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Binder call
+ @Override
+ public MediaRouterClientState getState(IMediaRouterClient client) {
+ if (client == null) {
+ throw new IllegalArgumentException("client must not be null");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ return getStateLocked(client);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Binder call
+ @Override
+ public void setDiscoveryRequest(IMediaRouterClient client,
+ int routeTypes, boolean activeScan) {
+ if (client == null) {
+ throw new IllegalArgumentException("client must not be null");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ setDiscoveryRequestLocked(client, routeTypes, activeScan);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Binder call
+ // A null routeId means that the client wants to unselect its current route.
+ // The explicit flag indicates whether the change was explicitly requested by the
+ // user or the application which may cause changes to propagate out to the rest
+ // of the system. Should be false when the change is in response to a new globally
+ // selected route or a default selection.
+ @Override
+ public void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit) {
+ if (client == null) {
+ throw new IllegalArgumentException("client must not be null");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ setSelectedRouteLocked(client, routeId, explicit);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Binder call
+ @Override
+ public void requestSetVolume(IMediaRouterClient client, String routeId, int volume) {
+ if (client == null) {
+ throw new IllegalArgumentException("client must not be null");
+ }
+ if (routeId == null) {
+ throw new IllegalArgumentException("routeId must not be null");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ requestSetVolumeLocked(client, routeId, volume);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Binder call
+ @Override
+ public void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction) {
+ if (client == null) {
+ throw new IllegalArgumentException("client must not be null");
+ }
+ if (routeId == null) {
+ throw new IllegalArgumentException("routeId must not be null");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ requestUpdateVolumeLocked(client, routeId, direction);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Binder call
+ @Override
+ public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump MediaRouterService from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ pw.println("MEDIA ROUTER SERVICE (dumpsys media_router)");
+ pw.println();
+ pw.println("Global state");
+ pw.println(" mCurrentUserId=" + mCurrentUserId);
+
+ synchronized (mLock) {
+ final int count = mUserRecords.size();
+ for (int i = 0; i < count; i++) {
+ UserRecord userRecord = mUserRecords.valueAt(i);
+ pw.println();
+ userRecord.dump(pw, "");
+ }
+ }
+ }
+
+ void switchUser() {
+ synchronized (mLock) {
+ int userId = ActivityManager.getCurrentUser();
+ if (mCurrentUserId != userId) {
+ final int oldUserId = mCurrentUserId;
+ mCurrentUserId = userId; // do this first
+
+ UserRecord oldUser = mUserRecords.get(oldUserId);
+ if (oldUser != null) {
+ oldUser.mHandler.sendEmptyMessage(UserHandler.MSG_STOP);
+ disposeUserIfNeededLocked(oldUser); // since no longer current user
+ }
+
+ UserRecord newUser = mUserRecords.get(userId);
+ if (newUser != null) {
+ newUser.mHandler.sendEmptyMessage(UserHandler.MSG_START);
+ }
+ }
+ }
+ }
+
+ void clientDied(ClientRecord clientRecord) {
+ synchronized (mLock) {
+ unregisterClientLocked(clientRecord.mClient, true);
+ }
+ }
+
+ private void registerClientLocked(IMediaRouterClient client,
+ int pid, String packageName, int userId) {
+ final IBinder binder = client.asBinder();
+ ClientRecord clientRecord = mAllClientRecords.get(binder);
+ if (clientRecord == null) {
+ boolean newUser = false;
+ UserRecord userRecord = mUserRecords.get(userId);
+ if (userRecord == null) {
+ userRecord = new UserRecord(userId);
+ newUser = true;
+ }
+ clientRecord = new ClientRecord(userRecord, client, pid, packageName);
+ try {
+ binder.linkToDeath(clientRecord, 0);
+ } catch (RemoteException ex) {
+ throw new RuntimeException("Media router client died prematurely.", ex);
+ }
+
+ if (newUser) {
+ mUserRecords.put(userId, userRecord);
+ initializeUserLocked(userRecord);
+ }
+
+ userRecord.mClientRecords.add(clientRecord);
+ mAllClientRecords.put(binder, clientRecord);
+ initializeClientLocked(clientRecord);
+ }
+ }
+
+ private void unregisterClientLocked(IMediaRouterClient client, boolean died) {
+ ClientRecord clientRecord = mAllClientRecords.remove(client.asBinder());
+ if (clientRecord != null) {
+ UserRecord userRecord = clientRecord.mUserRecord;
+ userRecord.mClientRecords.remove(clientRecord);
+ disposeClientLocked(clientRecord, died);
+ disposeUserIfNeededLocked(userRecord); // since client removed from user
+ }
+ }
+
+ private MediaRouterClientState getStateLocked(IMediaRouterClient client) {
+ ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
+ if (clientRecord != null) {
+ return clientRecord.mUserRecord.mState;
+ }
+ return null;
+ }
+
+ private void setDiscoveryRequestLocked(IMediaRouterClient client,
+ int routeTypes, boolean activeScan) {
+ final IBinder binder = client.asBinder();
+ ClientRecord clientRecord = mAllClientRecords.get(binder);
+ if (clientRecord != null) {
+ if (clientRecord.mRouteTypes != routeTypes
+ || clientRecord.mActiveScan != activeScan) {
+ if (DEBUG) {
+ Slog.d(TAG, clientRecord + ": Set discovery request, routeTypes=0x"
+ + Integer.toHexString(routeTypes) + ", activeScan=" + activeScan);
+ }
+ clientRecord.mRouteTypes = routeTypes;
+ clientRecord.mActiveScan = activeScan;
+ clientRecord.mUserRecord.mHandler.sendEmptyMessage(
+ UserHandler.MSG_UPDATE_DISCOVERY_REQUEST);
+ }
+ }
+ }
+
+ private void setSelectedRouteLocked(IMediaRouterClient client,
+ String routeId, boolean explicit) {
+ ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
+ if (clientRecord != null) {
+ final String oldRouteId = clientRecord.mSelectedRouteId;
+ if (!Objects.equal(routeId, oldRouteId)) {
+ if (DEBUG) {
+ Slog.d(TAG, clientRecord + ": Set selected route, routeId=" + routeId
+ + ", oldRouteId=" + oldRouteId
+ + ", explicit=" + explicit);
+ }
+
+ clientRecord.mSelectedRouteId = routeId;
+ if (explicit) {
+ if (oldRouteId != null) {
+ clientRecord.mUserRecord.mHandler.obtainMessage(
+ UserHandler.MSG_UNSELECT_ROUTE, oldRouteId).sendToTarget();
+ }
+ if (routeId != null) {
+ clientRecord.mUserRecord.mHandler.obtainMessage(
+ UserHandler.MSG_SELECT_ROUTE, routeId).sendToTarget();
+ }
+ }
+ }
+ }
+ }
+
+ private void requestSetVolumeLocked(IMediaRouterClient client,
+ String routeId, int volume) {
+ final IBinder binder = client.asBinder();
+ ClientRecord clientRecord = mAllClientRecords.get(binder);
+ if (clientRecord != null) {
+ clientRecord.mUserRecord.mHandler.obtainMessage(
+ UserHandler.MSG_REQUEST_SET_VOLUME, volume, 0, routeId).sendToTarget();
+ }
+ }
+
+ private void requestUpdateVolumeLocked(IMediaRouterClient client,
+ String routeId, int direction) {
+ final IBinder binder = client.asBinder();
+ ClientRecord clientRecord = mAllClientRecords.get(binder);
+ if (clientRecord != null) {
+ clientRecord.mUserRecord.mHandler.obtainMessage(
+ UserHandler.MSG_REQUEST_UPDATE_VOLUME, direction, 0, routeId).sendToTarget();
+ }
+ }
+
+ private void initializeUserLocked(UserRecord userRecord) {
+ if (DEBUG) {
+ Slog.d(TAG, userRecord + ": Initialized");
+ }
+ if (userRecord.mUserId == mCurrentUserId) {
+ userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_START);
+ }
+ }
+
+ private void disposeUserIfNeededLocked(UserRecord userRecord) {
+ // If there are no records left and the user is no longer current then go ahead
+ // and purge the user record and all of its associated state. If the user is current
+ // then leave it alone since we might be connected to a route or want to query
+ // the same route information again soon.
+ if (userRecord.mUserId != mCurrentUserId
+ && userRecord.mClientRecords.isEmpty()) {
+ if (DEBUG) {
+ Slog.d(TAG, userRecord + ": Disposed");
+ }
+ mUserRecords.remove(userRecord.mUserId);
+ // Note: User already stopped (by switchUser) so no need to send stop message here.
+ }
+ }
+
+ private void initializeClientLocked(ClientRecord clientRecord) {
+ if (DEBUG) {
+ Slog.d(TAG, clientRecord + ": Registered");
+ }
+ }
+
+ private void disposeClientLocked(ClientRecord clientRecord, boolean died) {
+ if (DEBUG) {
+ if (died) {
+ Slog.d(TAG, clientRecord + ": Died!");
+ } else {
+ Slog.d(TAG, clientRecord + ": Unregistered");
+ }
+ }
+ if (clientRecord.mRouteTypes != 0 || clientRecord.mActiveScan) {
+ clientRecord.mUserRecord.mHandler.sendEmptyMessage(
+ UserHandler.MSG_UPDATE_DISCOVERY_REQUEST);
+ }
+ clientRecord.dispose();
+ }
+
+ private boolean validatePackageName(int uid, String packageName) {
+ if (packageName != null) {
+ String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
+ if (packageNames != null) {
+ for (String n : packageNames) {
+ if (n.equals(packageName)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Information about a particular client of the media router.
+ * The contents of this object is guarded by mLock.
+ */
+ final class ClientRecord implements DeathRecipient {
+ public final UserRecord mUserRecord;
+ public final IMediaRouterClient mClient;
+ public final int mPid;
+ public final String mPackageName;
+
+ public int mRouteTypes;
+ public boolean mActiveScan;
+ public String mSelectedRouteId;
+
+ public ClientRecord(UserRecord userRecord, IMediaRouterClient client,
+ int pid, String packageName) {
+ mUserRecord = userRecord;
+ mClient = client;
+ mPid = pid;
+ mPackageName = packageName;
+ }
+
+ public void dispose() {
+ mClient.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ clientDied(this);
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + this);
+
+ final String indent = prefix + " ";
+ pw.println(indent + "mRouteTypes=0x" + Integer.toHexString(mRouteTypes));
+ pw.println(indent + "mActiveScan=" + mActiveScan);
+ pw.println(indent + "mSelectedRouteId=" + mSelectedRouteId);
+ }
+
+ @Override
+ public String toString() {
+ return "Client " + mPackageName + " (pid " + mPid + ")";
+ }
+ }
+
+ /**
+ * Information about a particular user.
+ * The contents of this object is guarded by mLock.
+ */
+ final class UserRecord {
+ public final int mUserId;
+ public final ArrayList<ClientRecord> mClientRecords = new ArrayList<ClientRecord>();
+ public final UserHandler mHandler;
+ public MediaRouterClientState mState;
+
+ public UserRecord(int userId) {
+ mUserId = userId;
+ mHandler = new UserHandler(MediaRouterService.this, this);
+ }
+
+ public void dump(final PrintWriter pw, String prefix) {
+ pw.println(prefix + this);
+
+ final String indent = prefix + " ";
+ final int clientCount = mClientRecords.size();
+ if (clientCount != 0) {
+ for (int i = 0; i < clientCount; i++) {
+ mClientRecords.get(i).dump(pw, indent);
+ }
+ } else {
+ pw.println(indent + "<no clients>");
+ }
+
+ if (!mHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ mHandler.dump(pw, indent);
+ }
+ }, 1000)) {
+ pw.println(indent + "<could not dump handler state>");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "User " + mUserId;
+ }
+ }
+
+ /**
+ * Media router handler
+ * <p>
+ * Since remote display providers are designed to be single-threaded by nature,
+ * this class encapsulates all of the associated functionality and exports state
+ * to the service as it evolves.
+ * </p><p>
+ * One important task of this class is to keep track of the current globally selected
+ * route id for certain routes that have global effects, such as remote displays.
+ * Global route selections override local selections made within apps. The change
+ * is propagated to all apps so that they are all in sync. Synchronization works
+ * both ways. Whenever the globally selected route is explicitly unselected by any
+ * app, then it becomes unselected globally and all apps are informed.
+ * </p><p>
+ * This class is currently hardcoded to work with remote display providers but
+ * it is intended to be eventually extended to support more general route providers
+ * similar to the support library media router.
+ * </p>
+ */
+ static final class UserHandler extends Handler
+ implements RemoteDisplayProviderWatcher.Callback,
+ RemoteDisplayProviderProxy.Callback {
+ public static final int MSG_START = 1;
+ public static final int MSG_STOP = 2;
+ public static final int MSG_UPDATE_DISCOVERY_REQUEST = 3;
+ public static final int MSG_SELECT_ROUTE = 4;
+ public static final int MSG_UNSELECT_ROUTE = 5;
+ public static final int MSG_REQUEST_SET_VOLUME = 6;
+ public static final int MSG_REQUEST_UPDATE_VOLUME = 7;
+ private static final int MSG_UPDATE_CLIENT_STATE = 8;
+ private static final int MSG_CONNECTION_TIMED_OUT = 9;
+
+ private static final int TIMEOUT_REASON_NOT_AVAILABLE = 1;
+ private static final int TIMEOUT_REASON_CONNECTION_LOST = 2;
+ private static final int TIMEOUT_REASON_WAITING_FOR_CONNECTING = 3;
+ private static final int TIMEOUT_REASON_WAITING_FOR_CONNECTED = 4;
+
+ // The relative order of these constants is important and expresses progress
+ // through the process of connecting to a route.
+ private static final int PHASE_NOT_AVAILABLE = -1;
+ private static final int PHASE_NOT_CONNECTED = 0;
+ private static final int PHASE_CONNECTING = 1;
+ private static final int PHASE_CONNECTED = 2;
+
+ private final MediaRouterService mService;
+ private final UserRecord mUserRecord;
+ private final RemoteDisplayProviderWatcher mWatcher;
+ private final ArrayList<ProviderRecord> mProviderRecords =
+ new ArrayList<ProviderRecord>();
+ private final ArrayList<IMediaRouterClient> mTempClients =
+ new ArrayList<IMediaRouterClient>();
+
+ private boolean mRunning;
+ private int mDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_NONE;
+ private RouteRecord mGloballySelectedRouteRecord;
+ private int mConnectionPhase = PHASE_NOT_AVAILABLE;
+ private int mConnectionTimeoutReason;
+ private long mConnectionTimeoutStartTime;
+ private boolean mClientStateUpdateScheduled;
+
+ public UserHandler(MediaRouterService service, UserRecord userRecord) {
+ super(Looper.getMainLooper(), null, true);
+ mService = service;
+ mUserRecord = userRecord;
+ mWatcher = new RemoteDisplayProviderWatcher(service.mContext, this,
+ this, mUserRecord.mUserId);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_START: {
+ start();
+ break;
+ }
+ case MSG_STOP: {
+ stop();
+ break;
+ }
+ case MSG_UPDATE_DISCOVERY_REQUEST: {
+ updateDiscoveryRequest();
+ break;
+ }
+ case MSG_SELECT_ROUTE: {
+ selectRoute((String)msg.obj);
+ break;
+ }
+ case MSG_UNSELECT_ROUTE: {
+ unselectRoute((String)msg.obj);
+ break;
+ }
+ case MSG_REQUEST_SET_VOLUME: {
+ requestSetVolume((String)msg.obj, msg.arg1);
+ break;
+ }
+ case MSG_REQUEST_UPDATE_VOLUME: {
+ requestUpdateVolume((String)msg.obj, msg.arg1);
+ break;
+ }
+ case MSG_UPDATE_CLIENT_STATE: {
+ updateClientState();
+ break;
+ }
+ case MSG_CONNECTION_TIMED_OUT: {
+ connectionTimedOut();
+ break;
+ }
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "Handler");
+
+ final String indent = prefix + " ";
+ pw.println(indent + "mRunning=" + mRunning);
+ pw.println(indent + "mDiscoveryMode=" + mDiscoveryMode);
+ pw.println(indent + "mGloballySelectedRouteRecord=" + mGloballySelectedRouteRecord);
+ pw.println(indent + "mConnectionPhase=" + mConnectionPhase);
+ pw.println(indent + "mConnectionTimeoutReason=" + mConnectionTimeoutReason);
+ pw.println(indent + "mConnectionTimeoutStartTime=" + (mConnectionTimeoutReason != 0 ?
+ TimeUtils.formatUptime(mConnectionTimeoutStartTime) : "<n/a>"));
+
+ mWatcher.dump(pw, prefix);
+
+ final int providerCount = mProviderRecords.size();
+ if (providerCount != 0) {
+ for (int i = 0; i < providerCount; i++) {
+ mProviderRecords.get(i).dump(pw, prefix);
+ }
+ } else {
+ pw.println(indent + "<no providers>");
+ }
+ }
+
+ private void start() {
+ if (!mRunning) {
+ mRunning = true;
+ mWatcher.start(); // also starts all providers
+ }
+ }
+
+ private void stop() {
+ if (mRunning) {
+ mRunning = false;
+ unselectGloballySelectedRoute();
+ mWatcher.stop(); // also stops all providers
+ }
+ }
+
+ private void updateDiscoveryRequest() {
+ int routeTypes = 0;
+ boolean activeScan = false;
+ synchronized (mService.mLock) {
+ final int count = mUserRecord.mClientRecords.size();
+ for (int i = 0; i < count; i++) {
+ ClientRecord clientRecord = mUserRecord.mClientRecords.get(i);
+ routeTypes |= clientRecord.mRouteTypes;
+ activeScan |= clientRecord.mActiveScan;
+ }
+ }
+
+ final int newDiscoveryMode;
+ if ((routeTypes & (MediaRouter.ROUTE_TYPE_LIVE_VIDEO
+ | MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) != 0) {
+ if (activeScan) {
+ newDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_ACTIVE;
+ } else {
+ newDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_PASSIVE;
+ }
+ } else {
+ newDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_NONE;
+ }
+
+ if (mDiscoveryMode != newDiscoveryMode) {
+ mDiscoveryMode = newDiscoveryMode;
+ final int count = mProviderRecords.size();
+ for (int i = 0; i < count; i++) {
+ mProviderRecords.get(i).getProvider().setDiscoveryMode(mDiscoveryMode);
+ }
+ }
+ }
+
+ private void selectRoute(String routeId) {
+ if (routeId != null
+ && (mGloballySelectedRouteRecord == null
+ || !routeId.equals(mGloballySelectedRouteRecord.getUniqueId()))) {
+ RouteRecord routeRecord = findRouteRecord(routeId);
+ if (routeRecord != null) {
+ unselectGloballySelectedRoute();
+
+ Slog.i(TAG, "Selected global route:" + routeRecord);
+ mGloballySelectedRouteRecord = routeRecord;
+ checkGloballySelectedRouteState();
+ routeRecord.getProvider().setSelectedDisplay(routeRecord.getDescriptorId());
+
+ scheduleUpdateClientState();
+ }
+ }
+ }
+
+ private void unselectRoute(String routeId) {
+ if (routeId != null
+ && mGloballySelectedRouteRecord != null
+ && routeId.equals(mGloballySelectedRouteRecord.getUniqueId())) {
+ unselectGloballySelectedRoute();
+ }
+ }
+
+ private void unselectGloballySelectedRoute() {
+ if (mGloballySelectedRouteRecord != null) {
+ Slog.i(TAG, "Unselected global route:" + mGloballySelectedRouteRecord);
+ mGloballySelectedRouteRecord.getProvider().setSelectedDisplay(null);
+ mGloballySelectedRouteRecord = null;
+ checkGloballySelectedRouteState();
+
+ scheduleUpdateClientState();
+ }
+ }
+
+ private void requestSetVolume(String routeId, int volume) {
+ if (mGloballySelectedRouteRecord != null
+ && routeId.equals(mGloballySelectedRouteRecord.getUniqueId())) {
+ mGloballySelectedRouteRecord.getProvider().setDisplayVolume(volume);
+ }
+ }
+
+ private void requestUpdateVolume(String routeId, int direction) {
+ if (mGloballySelectedRouteRecord != null
+ && routeId.equals(mGloballySelectedRouteRecord.getUniqueId())) {
+ mGloballySelectedRouteRecord.getProvider().adjustDisplayVolume(direction);
+ }
+ }
+
+ @Override
+ public void addProvider(RemoteDisplayProviderProxy provider) {
+ provider.setCallback(this);
+ provider.setDiscoveryMode(mDiscoveryMode);
+ provider.setSelectedDisplay(null); // just to be safe
+
+ ProviderRecord providerRecord = new ProviderRecord(provider);
+ mProviderRecords.add(providerRecord);
+ providerRecord.updateDescriptor(provider.getDisplayState());
+
+ scheduleUpdateClientState();
+ }
+
+ @Override
+ public void removeProvider(RemoteDisplayProviderProxy provider) {
+ int index = findProviderRecord(provider);
+ if (index >= 0) {
+ ProviderRecord providerRecord = mProviderRecords.remove(index);
+ providerRecord.updateDescriptor(null); // mark routes invalid
+ provider.setCallback(null);
+ provider.setDiscoveryMode(RemoteDisplayState.DISCOVERY_MODE_NONE);
+
+ checkGloballySelectedRouteState();
+ scheduleUpdateClientState();
+ }
+ }
+
+ @Override
+ public void onDisplayStateChanged(RemoteDisplayProviderProxy provider,
+ RemoteDisplayState state) {
+ updateProvider(provider, state);
+ }
+
+ private void updateProvider(RemoteDisplayProviderProxy provider,
+ RemoteDisplayState state) {
+ int index = findProviderRecord(provider);
+ if (index >= 0) {
+ ProviderRecord providerRecord = mProviderRecords.get(index);
+ if (providerRecord.updateDescriptor(state)) {
+ checkGloballySelectedRouteState();
+ scheduleUpdateClientState();
+ }
+ }
+ }
+
+ /**
+ * This function is called whenever the state of the globally selected route
+ * may have changed. It checks the state and updates timeouts or unselects
+ * the route as appropriate.
+ */
+ private void checkGloballySelectedRouteState() {
+ // Unschedule timeouts when the route is unselected.
+ if (mGloballySelectedRouteRecord == null) {
+ mConnectionPhase = PHASE_NOT_AVAILABLE;
+ updateConnectionTimeout(0);
+ return;
+ }
+
+ // Ensure that the route is still present and enabled.
+ if (!mGloballySelectedRouteRecord.isValid()
+ || !mGloballySelectedRouteRecord.isEnabled()) {
+ updateConnectionTimeout(TIMEOUT_REASON_NOT_AVAILABLE);
+ return;
+ }
+
+ // Make sure we haven't lost our connection.
+ final int oldPhase = mConnectionPhase;
+ mConnectionPhase = getConnectionPhase(mGloballySelectedRouteRecord.getStatus());
+ if (oldPhase >= PHASE_CONNECTING && mConnectionPhase < PHASE_CONNECTING) {
+ updateConnectionTimeout(TIMEOUT_REASON_CONNECTION_LOST);
+ return;
+ }
+
+ // Check the route status.
+ switch (mConnectionPhase) {
+ case PHASE_CONNECTED:
+ if (oldPhase != PHASE_CONNECTED) {
+ Slog.i(TAG, "Connected to global route: "
+ + mGloballySelectedRouteRecord);
+ }
+ updateConnectionTimeout(0);
+ break;
+ case PHASE_CONNECTING:
+ if (oldPhase != PHASE_CONNECTING) {
+ Slog.i(TAG, "Connecting to global route: "
+ + mGloballySelectedRouteRecord);
+ }
+ updateConnectionTimeout(TIMEOUT_REASON_WAITING_FOR_CONNECTED);
+ break;
+ case PHASE_NOT_CONNECTED:
+ updateConnectionTimeout(TIMEOUT_REASON_WAITING_FOR_CONNECTING);
+ break;
+ case PHASE_NOT_AVAILABLE:
+ default:
+ updateConnectionTimeout(TIMEOUT_REASON_NOT_AVAILABLE);
+ break;
+ }
+ }
+
+ private void updateConnectionTimeout(int reason) {
+ if (reason != mConnectionTimeoutReason) {
+ if (mConnectionTimeoutReason != 0) {
+ removeMessages(MSG_CONNECTION_TIMED_OUT);
+ }
+ mConnectionTimeoutReason = reason;
+ mConnectionTimeoutStartTime = SystemClock.uptimeMillis();
+ switch (reason) {
+ case TIMEOUT_REASON_NOT_AVAILABLE:
+ case TIMEOUT_REASON_CONNECTION_LOST:
+ // Route became unavailable or connection lost.
+ // Unselect it immediately.
+ sendEmptyMessage(MSG_CONNECTION_TIMED_OUT);
+ break;
+ case TIMEOUT_REASON_WAITING_FOR_CONNECTING:
+ // Waiting for route to start connecting.
+ sendEmptyMessageDelayed(MSG_CONNECTION_TIMED_OUT, CONNECTING_TIMEOUT);
+ break;
+ case TIMEOUT_REASON_WAITING_FOR_CONNECTED:
+ // Waiting for route to complete connection.
+ sendEmptyMessageDelayed(MSG_CONNECTION_TIMED_OUT, CONNECTED_TIMEOUT);
+ break;
+ }
+ }
+ }
+
+ private void connectionTimedOut() {
+ if (mConnectionTimeoutReason == 0 || mGloballySelectedRouteRecord == null) {
+ // Shouldn't get here. There must be a bug somewhere.
+ Log.wtf(TAG, "Handled connection timeout for no reason.");
+ return;
+ }
+
+ switch (mConnectionTimeoutReason) {
+ case TIMEOUT_REASON_NOT_AVAILABLE:
+ Slog.i(TAG, "Global route no longer available: "
+ + mGloballySelectedRouteRecord);
+ break;
+ case TIMEOUT_REASON_CONNECTION_LOST:
+ Slog.i(TAG, "Global route connection lost: "
+ + mGloballySelectedRouteRecord);
+ break;
+ case TIMEOUT_REASON_WAITING_FOR_CONNECTING:
+ Slog.i(TAG, "Global route timed out while waiting for "
+ + "connection attempt to begin after "
+ + (SystemClock.uptimeMillis() - mConnectionTimeoutStartTime)
+ + " ms: " + mGloballySelectedRouteRecord);
+ break;
+ case TIMEOUT_REASON_WAITING_FOR_CONNECTED:
+ Slog.i(TAG, "Global route timed out while connecting after "
+ + (SystemClock.uptimeMillis() - mConnectionTimeoutStartTime)
+ + " ms: " + mGloballySelectedRouteRecord);
+ break;
+ }
+ mConnectionTimeoutReason = 0;
+
+ unselectGloballySelectedRoute();
+ }
+
+ private void scheduleUpdateClientState() {
+ if (!mClientStateUpdateScheduled) {
+ mClientStateUpdateScheduled = true;
+ sendEmptyMessage(MSG_UPDATE_CLIENT_STATE);
+ }
+ }
+
+ private void updateClientState() {
+ mClientStateUpdateScheduled = false;
+
+ // Build a new client state.
+ MediaRouterClientState state = new MediaRouterClientState();
+ state.globallySelectedRouteId = mGloballySelectedRouteRecord != null ?
+ mGloballySelectedRouteRecord.getUniqueId() : null;
+ final int providerCount = mProviderRecords.size();
+ for (int i = 0; i < providerCount; i++) {
+ mProviderRecords.get(i).appendClientState(state);
+ }
+
+ try {
+ synchronized (mService.mLock) {
+ // Update the UserRecord.
+ mUserRecord.mState = state;
+
+ // Collect all clients.
+ final int count = mUserRecord.mClientRecords.size();
+ for (int i = 0; i < count; i++) {
+ mTempClients.add(mUserRecord.mClientRecords.get(i).mClient);
+ }
+ }
+
+ // Notify all clients (outside of the lock).
+ final int count = mTempClients.size();
+ for (int i = 0; i < count; i++) {
+ try {
+ mTempClients.get(i).onStateChanged();
+ } catch (RemoteException ex) {
+ // ignore errors, client probably died
+ }
+ }
+ } finally {
+ // Clear the list in preparation for the next time.
+ mTempClients.clear();
+ }
+ }
+
+ private int findProviderRecord(RemoteDisplayProviderProxy provider) {
+ final int count = mProviderRecords.size();
+ for (int i = 0; i < count; i++) {
+ ProviderRecord record = mProviderRecords.get(i);
+ if (record.getProvider() == provider) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private RouteRecord findRouteRecord(String uniqueId) {
+ final int count = mProviderRecords.size();
+ for (int i = 0; i < count; i++) {
+ RouteRecord record = mProviderRecords.get(i).findRouteByUniqueId(uniqueId);
+ if (record != null) {
+ return record;
+ }
+ }
+ return null;
+ }
+
+ private static int getConnectionPhase(int status) {
+ switch (status) {
+ case MediaRouter.RouteInfo.STATUS_NONE:
+ case MediaRouter.RouteInfo.STATUS_CONNECTED:
+ return PHASE_CONNECTED;
+ case MediaRouter.RouteInfo.STATUS_CONNECTING:
+ return PHASE_CONNECTING;
+ case MediaRouter.RouteInfo.STATUS_SCANNING:
+ case MediaRouter.RouteInfo.STATUS_AVAILABLE:
+ return PHASE_NOT_CONNECTED;
+ case MediaRouter.RouteInfo.STATUS_NOT_AVAILABLE:
+ case MediaRouter.RouteInfo.STATUS_IN_USE:
+ default:
+ return PHASE_NOT_AVAILABLE;
+ }
+ }
+
+ static final class ProviderRecord {
+ private final RemoteDisplayProviderProxy mProvider;
+ private final String mUniquePrefix;
+ private final ArrayList<RouteRecord> mRoutes = new ArrayList<RouteRecord>();
+ private RemoteDisplayState mDescriptor;
+
+ public ProviderRecord(RemoteDisplayProviderProxy provider) {
+ mProvider = provider;
+ mUniquePrefix = provider.getFlattenedComponentName() + ":";
+ }
+
+ public RemoteDisplayProviderProxy getProvider() {
+ return mProvider;
+ }
+
+ public String getUniquePrefix() {
+ return mUniquePrefix;
+ }
+
+ public boolean updateDescriptor(RemoteDisplayState descriptor) {
+ boolean changed = false;
+ if (mDescriptor != descriptor) {
+ mDescriptor = descriptor;
+
+ // Update all existing routes and reorder them to match
+ // the order of their descriptors.
+ int targetIndex = 0;
+ if (descriptor != null) {
+ if (descriptor.isValid()) {
+ final List<RemoteDisplayInfo> routeDescriptors = descriptor.displays;
+ final int routeCount = routeDescriptors.size();
+ for (int i = 0; i < routeCount; i++) {
+ final RemoteDisplayInfo routeDescriptor =
+ routeDescriptors.get(i);
+ final String descriptorId = routeDescriptor.id;
+ final int sourceIndex = findRouteByDescriptorId(descriptorId);
+ if (sourceIndex < 0) {
+ // Add the route to the provider.
+ String uniqueId = assignRouteUniqueId(descriptorId);
+ RouteRecord route =
+ new RouteRecord(this, descriptorId, uniqueId);
+ mRoutes.add(targetIndex++, route);
+ route.updateDescriptor(routeDescriptor);
+ changed = true;
+ } else if (sourceIndex < targetIndex) {
+ // Ignore route with duplicate id.
+ Slog.w(TAG, "Ignoring route descriptor with duplicate id: "
+ + routeDescriptor);
+ } else {
+ // Reorder existing route within the list.
+ RouteRecord route = mRoutes.get(sourceIndex);
+ Collections.swap(mRoutes, sourceIndex, targetIndex++);
+ changed |= route.updateDescriptor(routeDescriptor);
+ }
+ }
+ } else {
+ Slog.w(TAG, "Ignoring invalid descriptor from media route provider: "
+ + mProvider.getFlattenedComponentName());
+ }
+ }
+
+ // Dispose all remaining routes that do not have matching descriptors.
+ for (int i = mRoutes.size() - 1; i >= targetIndex; i--) {
+ RouteRecord route = mRoutes.remove(i);
+ route.updateDescriptor(null); // mark route invalid
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ public void appendClientState(MediaRouterClientState state) {
+ final int routeCount = mRoutes.size();
+ for (int i = 0; i < routeCount; i++) {
+ state.routes.add(mRoutes.get(i).getInfo());
+ }
+ }
+
+ public RouteRecord findRouteByUniqueId(String uniqueId) {
+ final int routeCount = mRoutes.size();
+ for (int i = 0; i < routeCount; i++) {
+ RouteRecord route = mRoutes.get(i);
+ if (route.getUniqueId().equals(uniqueId)) {
+ return route;
+ }
+ }
+ return null;
+ }
+
+ private int findRouteByDescriptorId(String descriptorId) {
+ final int routeCount = mRoutes.size();
+ for (int i = 0; i < routeCount; i++) {
+ RouteRecord route = mRoutes.get(i);
+ if (route.getDescriptorId().equals(descriptorId)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + this);
+
+ final String indent = prefix + " ";
+ mProvider.dump(pw, indent);
+
+ final int routeCount = mRoutes.size();
+ if (routeCount != 0) {
+ for (int i = 0; i < routeCount; i++) {
+ mRoutes.get(i).dump(pw, indent);
+ }
+ } else {
+ pw.println(indent + "<no routes>");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Provider " + mProvider.getFlattenedComponentName();
+ }
+
+ private String assignRouteUniqueId(String descriptorId) {
+ return mUniquePrefix + descriptorId;
+ }
+ }
+
+ static final class RouteRecord {
+ private final ProviderRecord mProviderRecord;
+ private final String mDescriptorId;
+ private final MediaRouterClientState.RouteInfo mMutableInfo;
+ private MediaRouterClientState.RouteInfo mImmutableInfo;
+ private RemoteDisplayInfo mDescriptor;
+
+ public RouteRecord(ProviderRecord providerRecord,
+ String descriptorId, String uniqueId) {
+ mProviderRecord = providerRecord;
+ mDescriptorId = descriptorId;
+ mMutableInfo = new MediaRouterClientState.RouteInfo(uniqueId);
+ }
+
+ public RemoteDisplayProviderProxy getProvider() {
+ return mProviderRecord.getProvider();
+ }
+
+ public ProviderRecord getProviderRecord() {
+ return mProviderRecord;
+ }
+
+ public String getDescriptorId() {
+ return mDescriptorId;
+ }
+
+ public String getUniqueId() {
+ return mMutableInfo.id;
+ }
+
+ public MediaRouterClientState.RouteInfo getInfo() {
+ if (mImmutableInfo == null) {
+ mImmutableInfo = new MediaRouterClientState.RouteInfo(mMutableInfo);
+ }
+ return mImmutableInfo;
+ }
+
+ public boolean isValid() {
+ return mDescriptor != null;
+ }
+
+ public boolean isEnabled() {
+ return mMutableInfo.enabled;
+ }
+
+ public int getStatus() {
+ return mMutableInfo.statusCode;
+ }
+
+ public boolean updateDescriptor(RemoteDisplayInfo descriptor) {
+ boolean changed = false;
+ if (mDescriptor != descriptor) {
+ mDescriptor = descriptor;
+ if (descriptor != null) {
+ final String name = computeName(descriptor);
+ if (!Objects.equal(mMutableInfo.name, name)) {
+ mMutableInfo.name = name;
+ changed = true;
+ }
+ final String description = computeDescription(descriptor);
+ if (!Objects.equal(mMutableInfo.description, description)) {
+ mMutableInfo.description = description;
+ changed = true;
+ }
+ final int supportedTypes = computeSupportedTypes(descriptor);
+ if (mMutableInfo.supportedTypes != supportedTypes) {
+ mMutableInfo.supportedTypes = supportedTypes;
+ changed = true;
+ }
+ final boolean enabled = computeEnabled(descriptor);
+ if (mMutableInfo.enabled != enabled) {
+ mMutableInfo.enabled = enabled;
+ changed = true;
+ }
+ final int statusCode = computeStatusCode(descriptor);
+ if (mMutableInfo.statusCode != statusCode) {
+ mMutableInfo.statusCode = statusCode;
+ changed = true;
+ }
+ final int playbackType = computePlaybackType(descriptor);
+ if (mMutableInfo.playbackType != playbackType) {
+ mMutableInfo.playbackType = playbackType;
+ changed = true;
+ }
+ final int playbackStream = computePlaybackStream(descriptor);
+ if (mMutableInfo.playbackStream != playbackStream) {
+ mMutableInfo.playbackStream = playbackStream;
+ changed = true;
+ }
+ final int volume = computeVolume(descriptor);
+ if (mMutableInfo.volume != volume) {
+ mMutableInfo.volume = volume;
+ changed = true;
+ }
+ final int volumeMax = computeVolumeMax(descriptor);
+ if (mMutableInfo.volumeMax != volumeMax) {
+ mMutableInfo.volumeMax = volumeMax;
+ changed = true;
+ }
+ final int volumeHandling = computeVolumeHandling(descriptor);
+ if (mMutableInfo.volumeHandling != volumeHandling) {
+ mMutableInfo.volumeHandling = volumeHandling;
+ changed = true;
+ }
+ final int presentationDisplayId = computePresentationDisplayId(descriptor);
+ if (mMutableInfo.presentationDisplayId != presentationDisplayId) {
+ mMutableInfo.presentationDisplayId = presentationDisplayId;
+ changed = true;
+ }
+ }
+ }
+ if (changed) {
+ mImmutableInfo = null;
+ }
+ return changed;
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + this);
+
+ final String indent = prefix + " ";
+ pw.println(indent + "mMutableInfo=" + mMutableInfo);
+ pw.println(indent + "mDescriptorId=" + mDescriptorId);
+ pw.println(indent + "mDescriptor=" + mDescriptor);
+ }
+
+ @Override
+ public String toString() {
+ return "Route " + mMutableInfo.name + " (" + mMutableInfo.id + ")";
+ }
+
+ private static String computeName(RemoteDisplayInfo descriptor) {
+ // Note that isValid() already ensures the name is non-empty.
+ return descriptor.name;
+ }
+
+ private static String computeDescription(RemoteDisplayInfo descriptor) {
+ final String description = descriptor.description;
+ return TextUtils.isEmpty(description) ? null : description;
+ }
+
+ private static int computeSupportedTypes(RemoteDisplayInfo descriptor) {
+ return MediaRouter.ROUTE_TYPE_LIVE_AUDIO
+ | MediaRouter.ROUTE_TYPE_LIVE_VIDEO
+ | MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
+ }
+
+ private static boolean computeEnabled(RemoteDisplayInfo descriptor) {
+ switch (descriptor.status) {
+ case RemoteDisplayInfo.STATUS_CONNECTED:
+ case RemoteDisplayInfo.STATUS_CONNECTING:
+ case RemoteDisplayInfo.STATUS_AVAILABLE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static int computeStatusCode(RemoteDisplayInfo descriptor) {
+ switch (descriptor.status) {
+ case RemoteDisplayInfo.STATUS_NOT_AVAILABLE:
+ return MediaRouter.RouteInfo.STATUS_NOT_AVAILABLE;
+ case RemoteDisplayInfo.STATUS_AVAILABLE:
+ return MediaRouter.RouteInfo.STATUS_AVAILABLE;
+ case RemoteDisplayInfo.STATUS_IN_USE:
+ return MediaRouter.RouteInfo.STATUS_IN_USE;
+ case RemoteDisplayInfo.STATUS_CONNECTING:
+ return MediaRouter.RouteInfo.STATUS_CONNECTING;
+ case RemoteDisplayInfo.STATUS_CONNECTED:
+ return MediaRouter.RouteInfo.STATUS_CONNECTED;
+ default:
+ return MediaRouter.RouteInfo.STATUS_NONE;
+ }
+ }
+
+ private static int computePlaybackType(RemoteDisplayInfo descriptor) {
+ return MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
+ }
+
+ private static int computePlaybackStream(RemoteDisplayInfo descriptor) {
+ return AudioSystem.STREAM_MUSIC;
+ }
+
+ private static int computeVolume(RemoteDisplayInfo descriptor) {
+ final int volume = descriptor.volume;
+ final int volumeMax = descriptor.volumeMax;
+ if (volume < 0) {
+ return 0;
+ } else if (volume > volumeMax) {
+ return volumeMax;
+ }
+ return volume;
+ }
+
+ private static int computeVolumeMax(RemoteDisplayInfo descriptor) {
+ final int volumeMax = descriptor.volumeMax;
+ return volumeMax > 0 ? volumeMax : 0;
+ }
+
+ private static int computeVolumeHandling(RemoteDisplayInfo descriptor) {
+ final int volumeHandling = descriptor.volumeHandling;
+ switch (volumeHandling) {
+ case RemoteDisplayInfo.PLAYBACK_VOLUME_VARIABLE:
+ return MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
+ case RemoteDisplayInfo.PLAYBACK_VOLUME_FIXED:
+ default:
+ return MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED;
+ }
+ }
+
+ private static int computePresentationDisplayId(RemoteDisplayInfo descriptor) {
+ // The MediaRouter class validates that the id corresponds to an extant
+ // presentation display. So all we do here is canonicalize the null case.
+ final int displayId = descriptor.presentationDisplayId;
+ return displayId < 0 ? -1 : displayId;
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/media/RemoteDisplayProviderProxy.java b/services/java/com/android/server/media/RemoteDisplayProviderProxy.java
new file mode 100644
index 0000000..b248ee0
--- /dev/null
+++ b/services/java/com/android/server/media/RemoteDisplayProviderProxy.java
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import com.android.internal.util.Objects;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.media.IRemoteDisplayCallback;
+import android.media.IRemoteDisplayProvider;
+import android.media.RemoteDisplayState;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.IBinder.DeathRecipient;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+
+/**
+ * Maintains a connection to a particular remote display provider service.
+ */
+final class RemoteDisplayProviderProxy implements ServiceConnection {
+ private static final String TAG = "RemoteDisplayProvider"; // max. 23 chars
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final Context mContext;
+ private final ComponentName mComponentName;
+ private final int mUserId;
+ private final Handler mHandler;
+
+ private Callback mDisplayStateCallback;
+
+ // Connection state
+ private boolean mRunning;
+ private boolean mBound;
+ private Connection mActiveConnection;
+ private boolean mConnectionReady;
+
+ // Logical state
+ private int mDiscoveryMode;
+ private String mSelectedDisplayId;
+ private RemoteDisplayState mDisplayState;
+ private boolean mScheduledDisplayStateChangedCallback;
+
+ public RemoteDisplayProviderProxy(Context context, ComponentName componentName,
+ int userId) {
+ mContext = context;
+ mComponentName = componentName;
+ mUserId = userId;
+ mHandler = new Handler();
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "Proxy");
+ pw.println(prefix + " mUserId=" + mUserId);
+ pw.println(prefix + " mRunning=" + mRunning);
+ pw.println(prefix + " mBound=" + mBound);
+ pw.println(prefix + " mActiveConnection=" + mActiveConnection);
+ pw.println(prefix + " mConnectionReady=" + mConnectionReady);
+ pw.println(prefix + " mDiscoveryMode=" + mDiscoveryMode);
+ pw.println(prefix + " mSelectedDisplayId=" + mSelectedDisplayId);
+ pw.println(prefix + " mDisplayState=" + mDisplayState);
+ }
+
+ public void setCallback(Callback callback) {
+ mDisplayStateCallback = callback;
+ }
+
+ public RemoteDisplayState getDisplayState() {
+ return mDisplayState;
+ }
+
+ public void setDiscoveryMode(int mode) {
+ if (mDiscoveryMode != mode) {
+ mDiscoveryMode = mode;
+ if (mConnectionReady) {
+ mActiveConnection.setDiscoveryMode(mode);
+ }
+ updateBinding();
+ }
+ }
+
+ public void setSelectedDisplay(String id) {
+ if (!Objects.equal(mSelectedDisplayId, id)) {
+ if (mConnectionReady && mSelectedDisplayId != null) {
+ mActiveConnection.disconnect(mSelectedDisplayId);
+ }
+ mSelectedDisplayId = id;
+ if (mConnectionReady && id != null) {
+ mActiveConnection.connect(id);
+ }
+ updateBinding();
+ }
+ }
+
+ public void setDisplayVolume(int volume) {
+ if (mConnectionReady && mSelectedDisplayId != null) {
+ mActiveConnection.setVolume(mSelectedDisplayId, volume);
+ }
+ }
+
+ public void adjustDisplayVolume(int delta) {
+ if (mConnectionReady && mSelectedDisplayId != null) {
+ mActiveConnection.adjustVolume(mSelectedDisplayId, delta);
+ }
+ }
+
+ public boolean hasComponentName(String packageName, String className) {
+ return mComponentName.getPackageName().equals(packageName)
+ && mComponentName.getClassName().equals(className);
+ }
+
+ public String getFlattenedComponentName() {
+ return mComponentName.flattenToShortString();
+ }
+
+ public void start() {
+ if (!mRunning) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Starting");
+ }
+
+ mRunning = true;
+ updateBinding();
+ }
+ }
+
+ public void stop() {
+ if (mRunning) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Stopping");
+ }
+
+ mRunning = false;
+ updateBinding();
+ }
+ }
+
+ public void rebindIfDisconnected() {
+ if (mActiveConnection == null && shouldBind()) {
+ unbind();
+ bind();
+ }
+ }
+
+ private void updateBinding() {
+ if (shouldBind()) {
+ bind();
+ } else {
+ unbind();
+ }
+ }
+
+ private boolean shouldBind() {
+ if (mRunning) {
+ // Bind whenever there is a discovery request or selected display.
+ if (mDiscoveryMode != RemoteDisplayState.DISCOVERY_MODE_NONE
+ || mSelectedDisplayId != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void bind() {
+ if (!mBound) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Binding");
+ }
+
+ Intent service = new Intent(RemoteDisplayState.SERVICE_INTERFACE);
+ service.setComponent(mComponentName);
+ try {
+ mBound = mContext.bindServiceAsUser(service, this, Context.BIND_AUTO_CREATE,
+ new UserHandle(mUserId));
+ if (!mBound && DEBUG) {
+ Slog.d(TAG, this + ": Bind failed");
+ }
+ } catch (SecurityException ex) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Bind failed", ex);
+ }
+ }
+ }
+ }
+
+ private void unbind() {
+ if (mBound) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Unbinding");
+ }
+
+ mBound = false;
+ disconnect();
+ mContext.unbindService(this);
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Connected");
+ }
+
+ if (mBound) {
+ disconnect();
+
+ IRemoteDisplayProvider provider = IRemoteDisplayProvider.Stub.asInterface(service);
+ if (provider != null) {
+ Connection connection = new Connection(provider);
+ if (connection.register()) {
+ mActiveConnection = connection;
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Registration failed");
+ }
+ }
+ } else {
+ Slog.e(TAG, this + ": Service returned invalid remote display provider binder");
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Service disconnected");
+ }
+ disconnect();
+ }
+
+ private void onConnectionReady(Connection connection) {
+ if (mActiveConnection == connection) {
+ mConnectionReady = true;
+
+ if (mDiscoveryMode != RemoteDisplayState.DISCOVERY_MODE_NONE) {
+ mActiveConnection.setDiscoveryMode(mDiscoveryMode);
+ }
+ if (mSelectedDisplayId != null) {
+ mActiveConnection.connect(mSelectedDisplayId);
+ }
+ }
+ }
+
+ private void onConnectionDied(Connection connection) {
+ if (mActiveConnection == connection) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Service connection died");
+ }
+ disconnect();
+ }
+ }
+
+ private void onDisplayStateChanged(Connection connection, RemoteDisplayState state) {
+ if (mActiveConnection == connection) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": State changed, state=" + state);
+ }
+ setDisplayState(state);
+ }
+ }
+
+ private void disconnect() {
+ if (mActiveConnection != null) {
+ if (mSelectedDisplayId != null) {
+ mActiveConnection.disconnect(mSelectedDisplayId);
+ }
+ mConnectionReady = false;
+ mActiveConnection.dispose();
+ mActiveConnection = null;
+ setDisplayState(null);
+ }
+ }
+
+ private void setDisplayState(RemoteDisplayState state) {
+ if (!Objects.equal(mDisplayState, state)) {
+ mDisplayState = state;
+ if (!mScheduledDisplayStateChangedCallback) {
+ mScheduledDisplayStateChangedCallback = true;
+ mHandler.post(mDisplayStateChanged);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Service connection " + mComponentName.flattenToShortString();
+ }
+
+ private final Runnable mDisplayStateChanged = new Runnable() {
+ @Override
+ public void run() {
+ mScheduledDisplayStateChangedCallback = false;
+ if (mDisplayStateCallback != null) {
+ mDisplayStateCallback.onDisplayStateChanged(
+ RemoteDisplayProviderProxy.this, mDisplayState);
+ }
+ }
+ };
+
+ public interface Callback {
+ void onDisplayStateChanged(RemoteDisplayProviderProxy provider, RemoteDisplayState state);
+ }
+
+ private final class Connection implements DeathRecipient {
+ private final IRemoteDisplayProvider mProvider;
+ private final ProviderCallback mCallback;
+
+ public Connection(IRemoteDisplayProvider provider) {
+ mProvider = provider;
+ mCallback = new ProviderCallback(this);
+ }
+
+ public boolean register() {
+ try {
+ mProvider.asBinder().linkToDeath(this, 0);
+ mProvider.setCallback(mCallback);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onConnectionReady(Connection.this);
+ }
+ });
+ return true;
+ } catch (RemoteException ex) {
+ binderDied();
+ }
+ return false;
+ }
+
+ public void dispose() {
+ mProvider.asBinder().unlinkToDeath(this, 0);
+ mCallback.dispose();
+ }
+
+ public void setDiscoveryMode(int mode) {
+ try {
+ mProvider.setDiscoveryMode(mode);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to deliver request to set discovery mode.", ex);
+ }
+ }
+
+ public void connect(String id) {
+ try {
+ mProvider.connect(id);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to deliver request to connect to display.", ex);
+ }
+ }
+
+ public void disconnect(String id) {
+ try {
+ mProvider.disconnect(id);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to deliver request to disconnect from display.", ex);
+ }
+ }
+
+ public void setVolume(String id, int volume) {
+ try {
+ mProvider.setVolume(id, volume);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to deliver request to set display volume.", ex);
+ }
+ }
+
+ public void adjustVolume(String id, int volume) {
+ try {
+ mProvider.adjustVolume(id, volume);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to deliver request to adjust display volume.", ex);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onConnectionDied(Connection.this);
+ }
+ });
+ }
+
+ void postStateChanged(final RemoteDisplayState state) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onDisplayStateChanged(Connection.this, state);
+ }
+ });
+ }
+ }
+
+ /**
+ * Receives callbacks from the service.
+ * <p>
+ * This inner class is static and only retains a weak reference to the connection
+ * to prevent the client from being leaked in case the service is holding an
+ * active reference to the client's callback.
+ * </p>
+ */
+ private static final class ProviderCallback extends IRemoteDisplayCallback.Stub {
+ private final WeakReference<Connection> mConnectionRef;
+
+ public ProviderCallback(Connection connection) {
+ mConnectionRef = new WeakReference<Connection>(connection);
+ }
+
+ public void dispose() {
+ mConnectionRef.clear();
+ }
+
+ @Override
+ public void onStateChanged(RemoteDisplayState state) throws RemoteException {
+ Connection connection = mConnectionRef.get();
+ if (connection != null) {
+ connection.postStateChanged(state);
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/media/RemoteDisplayProviderWatcher.java b/services/java/com/android/server/media/RemoteDisplayProviderWatcher.java
new file mode 100644
index 0000000..f3a3c2f
--- /dev/null
+++ b/services/java/com/android/server/media/RemoteDisplayProviderWatcher.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.media.RemoteDisplayState;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Watches for remote display provider services to be installed.
+ * Adds a provider to the media router for each registered service.
+ *
+ * @see RemoteDisplayProviderProxy
+ */
+public final class RemoteDisplayProviderWatcher {
+ private static final String TAG = "RemoteDisplayProvider"; // max. 23 chars
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final Context mContext;
+ private final Callback mCallback;
+ private final Handler mHandler;
+ private final int mUserId;
+ private final PackageManager mPackageManager;
+
+ private final ArrayList<RemoteDisplayProviderProxy> mProviders =
+ new ArrayList<RemoteDisplayProviderProxy>();
+ private boolean mRunning;
+
+ public RemoteDisplayProviderWatcher(Context context,
+ Callback callback, Handler handler, int userId) {
+ mContext = context;
+ mCallback = callback;
+ mHandler = handler;
+ mUserId = userId;
+ mPackageManager = context.getPackageManager();
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "Watcher");
+ pw.println(prefix + " mUserId=" + mUserId);
+ pw.println(prefix + " mRunning=" + mRunning);
+ pw.println(prefix + " mProviders.size()=" + mProviders.size());
+ }
+
+ public void start() {
+ if (!mRunning) {
+ mRunning = true;
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverAsUser(mScanPackagesReceiver,
+ new UserHandle(mUserId), filter, null, mHandler);
+
+ // Scan packages.
+ // Also has the side-effect of restarting providers if needed.
+ mHandler.post(mScanPackagesRunnable);
+ }
+ }
+
+ public void stop() {
+ if (mRunning) {
+ mRunning = false;
+
+ mContext.unregisterReceiver(mScanPackagesReceiver);
+ mHandler.removeCallbacks(mScanPackagesRunnable);
+
+ // Stop all providers.
+ for (int i = mProviders.size() - 1; i >= 0; i--) {
+ mProviders.get(i).stop();
+ }
+ }
+ }
+
+ private void scanPackages() {
+ if (!mRunning) {
+ return;
+ }
+
+ // Add providers for all new services.
+ // Reorder the list so that providers left at the end will be the ones to remove.
+ int targetIndex = 0;
+ Intent intent = new Intent(RemoteDisplayState.SERVICE_INTERFACE);
+ for (ResolveInfo resolveInfo : mPackageManager.queryIntentServicesAsUser(
+ intent, 0, mUserId)) {
+ ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ if (serviceInfo != null) {
+ int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
+ if (sourceIndex < 0) {
+ RemoteDisplayProviderProxy provider =
+ new RemoteDisplayProviderProxy(mContext,
+ new ComponentName(serviceInfo.packageName, serviceInfo.name),
+ mUserId);
+ provider.start();
+ mProviders.add(targetIndex++, provider);
+ mCallback.addProvider(provider);
+ } else if (sourceIndex >= targetIndex) {
+ RemoteDisplayProviderProxy provider = mProviders.get(sourceIndex);
+ provider.start(); // restart the provider if needed
+ provider.rebindIfDisconnected();
+ Collections.swap(mProviders, sourceIndex, targetIndex++);
+ }
+ }
+ }
+
+ // Remove providers for missing services.
+ if (targetIndex < mProviders.size()) {
+ for (int i = mProviders.size() - 1; i >= targetIndex; i--) {
+ RemoteDisplayProviderProxy provider = mProviders.get(i);
+ mCallback.removeProvider(provider);
+ mProviders.remove(provider);
+ provider.stop();
+ }
+ }
+ }
+
+ private int findProvider(String packageName, String className) {
+ int count = mProviders.size();
+ for (int i = 0; i < count; i++) {
+ RemoteDisplayProviderProxy provider = mProviders.get(i);
+ if (provider.hasComponentName(packageName, className)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private final BroadcastReceiver mScanPackagesReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received package manager broadcast: " + intent);
+ }
+ scanPackages();
+ }
+ };
+
+ private final Runnable mScanPackagesRunnable = new Runnable() {
+ @Override
+ public void run() {
+ scanPackages();
+ }
+ };
+
+ public interface Callback {
+ void addProvider(RemoteDisplayProviderProxy provider);
+ void removeProvider(RemoteDisplayProviderProxy provider);
+ }
+}
diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java
index d471b57..f2efde1 100644
--- a/services/java/com/android/server/wifi/WifiService.java
+++ b/services/java/com/android/server/wifi/WifiService.java
@@ -369,15 +369,17 @@
}
private class BatchedScanRequest extends DeathRecipient {
- BatchedScanSettings settings;
- int uid;
- int pid;
+ final BatchedScanSettings settings;
+ final int uid;
+ final int pid;
+ final WorkSource workSource;
- BatchedScanRequest(BatchedScanSettings settings, IBinder binder) {
+ BatchedScanRequest(BatchedScanSettings settings, IBinder binder, WorkSource ws) {
super(0, null, binder, null);
this.settings = settings;
this.uid = getCallingUid();
this.pid = getCallingPid();
+ workSource = ws;
}
public void binderDied() {
stopBatchedScan(settings, uid, pid);
@@ -406,12 +408,19 @@
/**
* see {@link android.net.wifi.WifiManager#requestBatchedScan()}
*/
- public boolean requestBatchedScan(BatchedScanSettings requested, IBinder binder) {
+ public boolean requestBatchedScan(BatchedScanSettings requested, IBinder binder,
+ WorkSource workSource) {
enforceChangePermission();
+ if (workSource != null) {
+ enforceWorkSourcePermission();
+ // WifiManager currently doesn't use names, so need to clear names out of the
+ // supplied WorkSource to allow future WorkSource combining.
+ workSource.clearNames();
+ }
if (mBatchedScanSupported == false) return false;
requested = new BatchedScanSettings(requested);
if (requested.isInvalid()) return false;
- BatchedScanRequest r = new BatchedScanRequest(requested, binder);
+ BatchedScanRequest r = new BatchedScanRequest(requested, binder, workSource);
synchronized(mBatchedScanners) {
mBatchedScanners.add(r);
resolveBatchedScannersLocked();
@@ -468,18 +477,48 @@
private void resolveBatchedScannersLocked() {
BatchedScanSettings setting = new BatchedScanSettings();
+ WorkSource responsibleWorkSource = null;
int responsibleUid = 0;
+ double responsibleCsph = 0; // Channel Scans Per Hour
if (mBatchedScanners.size() == 0) {
- mWifiStateMachine.setBatchedScanSettings(null, 0);
+ mWifiStateMachine.setBatchedScanSettings(null, 0, 0, null);
return;
}
for (BatchedScanRequest r : mBatchedScanners) {
BatchedScanSettings s = r.settings;
+
+ // evaluate responsibility
+ int currentChannelCount;
+ int currentScanInterval;
+ double currentCsph;
+
+ if (s.channelSet == null || s.channelSet.isEmpty()) {
+ // all channels - 11 B and 9 A channels roughly.
+ currentChannelCount = 9 + 11;
+ } else {
+ currentChannelCount = s.channelSet.size();
+ // these are rough est - no real need to correct for reg-domain;
+ if (s.channelSet.contains("A")) currentChannelCount += (9 - 1);
+ if (s.channelSet.contains("B")) currentChannelCount += (11 - 1);
+
+ }
+ if (s.scanIntervalSec == BatchedScanSettings.UNSPECIFIED) {
+ currentScanInterval = BatchedScanSettings.DEFAULT_INTERVAL_SEC;
+ } else {
+ currentScanInterval = s.scanIntervalSec;
+ }
+ currentCsph = 60 * 60 * currentChannelCount / currentScanInterval;
+
+ if (currentCsph > responsibleCsph) {
+ responsibleUid = r.uid;
+ responsibleWorkSource = r.workSource;
+ responsibleCsph = currentCsph;
+ }
+
if (s.maxScansPerBatch != BatchedScanSettings.UNSPECIFIED &&
s.maxScansPerBatch < setting.maxScansPerBatch) {
setting.maxScansPerBatch = s.maxScansPerBatch;
- responsibleUid = r.uid;
}
if (s.maxApPerScan != BatchedScanSettings.UNSPECIFIED &&
(setting.maxApPerScan == BatchedScanSettings.UNSPECIFIED ||
@@ -489,7 +528,6 @@
if (s.scanIntervalSec != BatchedScanSettings.UNSPECIFIED &&
s.scanIntervalSec < setting.scanIntervalSec) {
setting.scanIntervalSec = s.scanIntervalSec;
- responsibleUid = r.uid;
}
if (s.maxApForDistance != BatchedScanSettings.UNSPECIFIED &&
(setting.maxApForDistance == BatchedScanSettings.UNSPECIFIED ||
@@ -511,7 +549,8 @@
}
setting.constrain();
- mWifiStateMachine.setBatchedScanSettings(setting, responsibleUid);
+ mWifiStateMachine.setBatchedScanSettings(setting, responsibleUid, (int)responsibleCsph,
+ responsibleWorkSource);
}
private void enforceAccessPermission() {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index e1e9f5c..818cfec 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -578,10 +578,13 @@
private boolean mUpdateRotation = false;
boolean mWallpaperActionPending = false;
- private static final int DISPLAY_CONTENT_UNKNOWN = 0;
- private static final int DISPLAY_CONTENT_MIRROR = 1;
- private static final int DISPLAY_CONTENT_UNIQUE = 2;
- private int mDisplayHasContent = DISPLAY_CONTENT_UNKNOWN;
+ // Set to true when the display contains content to show the user.
+ // When false, the display manager may choose to mirror or blank the display.
+ boolean mDisplayHasContent = false;
+
+ // Only set while traversing the default display based on its content.
+ // Affects the behavior of mirroring on secondary displays.
+ boolean mObscureApplicationContentOnSecondaryDisplays = false;
}
final LayoutFields mInnerFields = new LayoutFields();
@@ -4303,10 +4306,6 @@
// If we are preparing an app transition, then delay changing
// the visibility of this token until we execute that transition.
if (okToDisplay() && mAppTransition.isTransitionSet()) {
- // Already in requested state, don't do anything more.
- if (wtoken.hiddenRequested != visible) {
- return;
- }
wtoken.hiddenRequested = !visible;
if (!wtoken.startingDisplayed) {
@@ -8523,6 +8522,7 @@
// We are transitioning from an activity with
// a wallpaper to one without.
transit = AppTransition.TRANSIT_WALLPAPER_CLOSE;
+ mAnimateWallpaperWithTarget = true;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"New transit away from wallpaper: " + transit);
} else if (mWallpaperTarget != null && mWallpaperTarget.isVisibleLw()) {
@@ -8784,6 +8784,14 @@
final WindowManager.LayoutParams attrs = w.mAttrs;
final int attrFlags = attrs.flags;
final boolean canBeSeen = w.isDisplayedLw();
+ final boolean opaqueDrawn = canBeSeen && w.isOpaqueDrawn();
+
+ if (opaqueDrawn && w.isFullscreen(innerDw, innerDh)) {
+ // This window completely covers everything behind it,
+ // so we want to leave all of them as undimmed (for
+ // performance reasons).
+ mInnerFields.mObscured = true;
+ }
if (w.mHasSurface) {
if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) {
@@ -8812,22 +8820,24 @@
}
if (canBeSeen) {
- if (type == TYPE_DREAM || type == TYPE_KEYGUARD) {
- mInnerFields.mDisplayHasContent = LayoutFields.DISPLAY_CONTENT_MIRROR;
- } else if (mInnerFields.mDisplayHasContent
- == LayoutFields.DISPLAY_CONTENT_UNKNOWN) {
- mInnerFields.mDisplayHasContent = LayoutFields.DISPLAY_CONTENT_UNIQUE;
+ // This function assumes that the contents of the default display are
+ // processed first before secondary displays.
+ if (w.mDisplayContent.isDefaultDisplay) {
+ // While a dream or keyguard is showing, obscure ordinary application
+ // content on secondary displays (by forcibly enabling mirroring unless
+ // there is other content we want to show) but still allow opaque
+ // keyguard dialogs to be shown.
+ if (type == TYPE_DREAM || type == TYPE_KEYGUARD) {
+ mInnerFields.mObscureApplicationContentOnSecondaryDisplays = true;
+ }
+ mInnerFields.mDisplayHasContent = true;
+ } else if (!mInnerFields.mObscureApplicationContentOnSecondaryDisplays
+ || (mInnerFields.mObscured && type == TYPE_KEYGUARD_DIALOG)) {
+ // Allow full screen keyguard presentation dialogs to be seen.
+ mInnerFields.mDisplayHasContent = true;
}
}
}
-
- boolean opaqueDrawn = canBeSeen && w.isOpaqueDrawn();
- if (opaqueDrawn && w.isFullscreen(innerDw, innerDh)) {
- // This window completely covers everything behind it,
- // so we want to leave all of them as undimmed (for
- // performance reasons).
- mInnerFields.mObscured = true;
- }
}
private void handleFlagDimBehind(WindowState w, int innerDw, int innerDh) {
@@ -8905,7 +8915,7 @@
mInnerFields.mScreenBrightness = -1;
mInnerFields.mButtonBrightness = -1;
mInnerFields.mUserActivityTimeout = -1;
- mInnerFields.mDisplayHasContent = LayoutFields.DISPLAY_CONTENT_UNKNOWN;
+ mInnerFields.mObscureApplicationContentOnSecondaryDisplays = false;
mTransactionSequence++;
@@ -8940,10 +8950,8 @@
final int innerDh = displayInfo.appHeight;
final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- // Reset for each display unless we are forcing mirroring.
- if (mInnerFields.mDisplayHasContent != LayoutFields.DISPLAY_CONTENT_MIRROR) {
- mInnerFields.mDisplayHasContent = LayoutFields.DISPLAY_CONTENT_UNKNOWN;
- }
+ // Reset for each display.
+ mInnerFields.mDisplayHasContent = false;
int repeats = 0;
do {
@@ -9152,20 +9160,8 @@
updateResizingWindows(w);
}
- final boolean hasUniqueContent;
- switch (mInnerFields.mDisplayHasContent) {
- case LayoutFields.DISPLAY_CONTENT_MIRROR:
- hasUniqueContent = isDefaultDisplay;
- break;
- case LayoutFields.DISPLAY_CONTENT_UNIQUE:
- hasUniqueContent = true;
- break;
- case LayoutFields.DISPLAY_CONTENT_UNKNOWN:
- default:
- hasUniqueContent = false;
- break;
- }
- mDisplayManagerService.setDisplayHasContent(displayId, hasUniqueContent,
+ mDisplayManagerService.setDisplayHasContent(displayId,
+ mInnerFields.mDisplayHasContent,
true /* inTraversal, must call performTraversalInTrans... below */);
getDisplayContentLocked(displayId).stopDimmingIfNeeded();
diff --git a/tests/RemoteDisplayProvider/Android.mk b/tests/RemoteDisplayProvider/Android.mk
new file mode 100644
index 0000000..77e9815
--- /dev/null
+++ b/tests/RemoteDisplayProvider/Android.mk
@@ -0,0 +1,25 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# Build the application.
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := RemoteDisplayProviderTest
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR = $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := com.android.media.remotedisplay
+include $(BUILD_PACKAGE)
diff --git a/tests/RemoteDisplayProvider/AndroidManifest.xml b/tests/RemoteDisplayProvider/AndroidManifest.xml
new file mode 100644
index 0000000..e8e31da
--- /dev/null
+++ b/tests/RemoteDisplayProvider/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.media.remotedisplay.test" >
+
+ <uses-sdk android:minSdkVersion="19" />
+
+ <application android:label="@string/app_name"
+ android:icon="@drawable/ic_app">
+ <uses-library android:name="com.android.media.remotedisplay"
+ android:required="true" />
+
+ <service android:name=".RemoteDisplayProviderService"
+ android:label="@string/app_name"
+ android:exported="true"
+ android:permission="android.permission.BIND_REMOTE_DISPLAY">
+ <intent-filter>
+ <action android:name="com.android.media.remotedisplay.RemoteDisplayProvider"/>
+ </intent-filter>
+ </service>
+
+ </application>
+</manifest>
diff --git a/tests/RemoteDisplayProvider/README b/tests/RemoteDisplayProvider/README
new file mode 100644
index 0000000..8bf0130
--- /dev/null
+++ b/tests/RemoteDisplayProvider/README
@@ -0,0 +1,16 @@
+This directory contains sample code to test system integration with
+remote display providers using the API declared by the
+com.android.media.remotedisplay.jar library.
+
+--- DESCRIPTION ---
+
+The application registers a service that publishes a few different
+remote display routes. Behavior can be controlled by modifying the
+code.
+
+To exercise the provider, use System UI features for connecting to
+wireless displays or launch an activity that uses the MediaRouter,
+such as the PresentationWithMediaRouterActivity in ApiDemos.
+
+This code is mainly intended for development and not meant to be
+used as an example implementation of a robust remote display provider.
diff --git a/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png b/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png
new file mode 100755
index 0000000..66a1984
--- /dev/null
+++ b/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png
Binary files differ
diff --git a/tests/RemoteDisplayProvider/res/drawable-mdpi/ic_app.png b/tests/RemoteDisplayProvider/res/drawable-mdpi/ic_app.png
new file mode 100644
index 0000000..5ae7701
--- /dev/null
+++ b/tests/RemoteDisplayProvider/res/drawable-mdpi/ic_app.png
Binary files differ
diff --git a/tests/RemoteDisplayProvider/res/values/strings.xml b/tests/RemoteDisplayProvider/res/values/strings.xml
new file mode 100644
index 0000000..dd82d2c
--- /dev/null
+++ b/tests/RemoteDisplayProvider/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name">Remote Display Provider Test</string>
+</resources>
diff --git a/tests/RemoteDisplayProvider/src/com/android/media/remotedisplay/test/RemoteDisplayProviderService.java b/tests/RemoteDisplayProvider/src/com/android/media/remotedisplay/test/RemoteDisplayProviderService.java
new file mode 100644
index 0000000..611d7e4
--- /dev/null
+++ b/tests/RemoteDisplayProvider/src/com/android/media/remotedisplay/test/RemoteDisplayProviderService.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media.remotedisplay.test;
+
+import com.android.media.remotedisplay.RemoteDisplay;
+import com.android.media.remotedisplay.RemoteDisplayProvider;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+
+/**
+ * Remote display provider implementation that publishes working routes.
+ */
+public class RemoteDisplayProviderService extends Service {
+ private static final String TAG = "RemoteDisplayProviderTest";
+
+ private Provider mProvider;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (intent.getAction().equals(RemoteDisplayProvider.SERVICE_INTERFACE)) {
+ if (mProvider == null) {
+ mProvider = new Provider();
+ return mProvider.getBinder();
+ }
+ }
+ return null;
+ }
+
+ final class Provider extends RemoteDisplayProvider {
+ private RemoteDisplay mTestDisplay1; // variable volume
+ private RemoteDisplay mTestDisplay2; // fixed volume
+ private RemoteDisplay mTestDisplay3; // not available
+ private RemoteDisplay mTestDisplay4; // in use
+ private RemoteDisplay mTestDisplay5; // available but ignores request to connect
+ private RemoteDisplay mTestDisplay6; // available but never finishes connecting
+ private RemoteDisplay mTestDisplay7; // blinks in and out of existence
+ private RemoteDisplay mTestDisplay8; // available but connecting attempt flakes out
+ private RemoteDisplay mTestDisplay9; // available but connection flakes out
+ private RemoteDisplay mTestDisplay10; // available and reconnects periodically
+
+ private final Handler mHandler;
+ private boolean mBlinking;
+
+ public Provider() {
+ super(RemoteDisplayProviderService.this);
+ mHandler = new Handler(getMainLooper());
+ }
+
+ @Override
+ public void onDiscoveryModeChanged(int mode) {
+ Log.d(TAG, "onDiscoveryModeChanged: mode=" + mode);
+
+ if (mode != DISCOVERY_MODE_NONE) {
+ // When discovery begins, go find all of the routes.
+ if (mTestDisplay1 == null) {
+ mTestDisplay1 = new RemoteDisplay("testDisplay1",
+ "Test Display 1 (variable)");
+ mTestDisplay1.setDescription("Variable volume");
+ mTestDisplay1.setStatus(RemoteDisplay.STATUS_AVAILABLE);
+ mTestDisplay1.setVolume(10);
+ mTestDisplay1.setVolumeHandling(RemoteDisplay.PLAYBACK_VOLUME_VARIABLE);
+ mTestDisplay1.setVolumeMax(15);
+ addDisplay(mTestDisplay1);
+ }
+ if (mTestDisplay2 == null) {
+ mTestDisplay2 = new RemoteDisplay("testDisplay2",
+ "Test Display 2 (fixed)");
+ mTestDisplay2.setDescription("Fixed volume");
+ mTestDisplay2.setStatus(RemoteDisplay.STATUS_AVAILABLE);
+ addDisplay(mTestDisplay2);
+ }
+ if (mTestDisplay3 == null) {
+ mTestDisplay3 = new RemoteDisplay("testDisplay3",
+ "Test Display 3 (unavailable)");
+ mTestDisplay3.setDescription("Always unavailable");
+ mTestDisplay3.setStatus(RemoteDisplay.STATUS_NOT_AVAILABLE);
+ addDisplay(mTestDisplay3);
+ }
+ if (mTestDisplay4 == null) {
+ mTestDisplay4 = new RemoteDisplay("testDisplay4",
+ "Test Display 4 (in-use)");
+ mTestDisplay4.setDescription("Always in-use");
+ mTestDisplay4.setStatus(RemoteDisplay.STATUS_IN_USE);
+ addDisplay(mTestDisplay4);
+ }
+ if (mTestDisplay5 == null) {
+ mTestDisplay5 = new RemoteDisplay("testDisplay5",
+ "Test Display 5 (connect ignored)");
+ mTestDisplay5.setDescription("Ignores connect");
+ mTestDisplay5.setStatus(RemoteDisplay.STATUS_AVAILABLE);
+ addDisplay(mTestDisplay5);
+ }
+ if (mTestDisplay6 == null) {
+ mTestDisplay6 = new RemoteDisplay("testDisplay6",
+ "Test Display 6 (connect hangs)");
+ mTestDisplay6.setDescription("Never finishes connecting");
+ mTestDisplay6.setStatus(RemoteDisplay.STATUS_AVAILABLE);
+ addDisplay(mTestDisplay6);
+ }
+ if (mTestDisplay8 == null) {
+ mTestDisplay8 = new RemoteDisplay("testDisplay8",
+ "Test Display 8 (flaky when connecting)");
+ mTestDisplay8.setDescription("Aborts spontaneously while connecting");
+ mTestDisplay8.setStatus(RemoteDisplay.STATUS_AVAILABLE);
+ addDisplay(mTestDisplay8);
+ }
+ if (mTestDisplay9 == null) {
+ mTestDisplay9 = new RemoteDisplay("testDisplay9",
+ "Test Display 9 (flaky when connected)");
+ mTestDisplay9.setDescription("Aborts spontaneously while connected");
+ mTestDisplay9.setStatus(RemoteDisplay.STATUS_AVAILABLE);
+ addDisplay(mTestDisplay9);
+ }
+ if (mTestDisplay10 == null) {
+ mTestDisplay10 = new RemoteDisplay("testDisplay10",
+ "Test Display 10 (reconnects periodically)");
+ mTestDisplay10.setDescription("Reconnects spontaneously");
+ mTestDisplay10.setStatus(RemoteDisplay.STATUS_AVAILABLE);
+ addDisplay(mTestDisplay10);
+ }
+ } else {
+ // When discovery ends, go hide some of the routes we can't actually use.
+ // This isn't something a normal route provider would do though.
+ // The routes will usually stay published.
+ if (mTestDisplay3 != null) {
+ removeDisplay(mTestDisplay3);
+ mTestDisplay3 = null;
+ }
+ if (mTestDisplay4 != null) {
+ removeDisplay(mTestDisplay4);
+ mTestDisplay4 = null;
+ }
+ }
+
+ // When active discovery is on, pretend there's a route that we can't quite
+ // reach that blinks in and out of existence.
+ if (mode == DISCOVERY_MODE_ACTIVE) {
+ if (!mBlinking) {
+ mBlinking = true;
+ mHandler.post(mBlink);
+ }
+ } else {
+ mBlinking = false;
+ }
+ }
+
+ @Override
+ public void onConnect(final RemoteDisplay display) {
+ Log.d(TAG, "onConnect: display.getId()=" + display.getId());
+
+ if (display == mTestDisplay1 || display == mTestDisplay2) {
+ display.setStatus(RemoteDisplay.STATUS_CONNECTING);
+ updateDisplay(display);
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if ((display == mTestDisplay1 || display == mTestDisplay2)
+ && display.getStatus() == RemoteDisplay.STATUS_CONNECTING) {
+ display.setStatus(RemoteDisplay.STATUS_CONNECTED);
+ updateDisplay(display);
+ }
+ }
+ }, 2000);
+ } else if (display == mTestDisplay6 || display == mTestDisplay7) {
+ // never finishes connecting
+ display.setStatus(RemoteDisplay.STATUS_CONNECTING);
+ updateDisplay(display);
+ } else if (display == mTestDisplay8) {
+ // flakes out while connecting
+ display.setStatus(RemoteDisplay.STATUS_CONNECTING);
+ updateDisplay(display);
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if ((display == mTestDisplay8)
+ && display.getStatus() == RemoteDisplay.STATUS_CONNECTING) {
+ display.setStatus(RemoteDisplay.STATUS_AVAILABLE);
+ updateDisplay(display);
+ }
+ }
+ }, 2000);
+ } else if (display == mTestDisplay9) {
+ // flakes out when connected
+ display.setStatus(RemoteDisplay.STATUS_CONNECTING);
+ updateDisplay(display);
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if ((display == mTestDisplay9)
+ && display.getStatus() == RemoteDisplay.STATUS_CONNECTING) {
+ display.setStatus(RemoteDisplay.STATUS_CONNECTED);
+ updateDisplay(display);
+ }
+ }
+ }, 2000);
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if ((display == mTestDisplay9)
+ && display.getStatus() == RemoteDisplay.STATUS_CONNECTED) {
+ display.setStatus(RemoteDisplay.STATUS_AVAILABLE);
+ updateDisplay(display);
+ }
+ }
+ }, 5000);
+ } else if (display == mTestDisplay10) {
+ display.setStatus(RemoteDisplay.STATUS_CONNECTING);
+ updateDisplay(display);
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (display == mTestDisplay10) {
+ if (display.getStatus() == RemoteDisplay.STATUS_CONNECTING) {
+ display.setStatus(RemoteDisplay.STATUS_CONNECTED);
+ updateDisplay(display);
+ mHandler.postDelayed(this, 7000);
+ } else if (display.getStatus() == RemoteDisplay.STATUS_CONNECTED) {
+ display.setStatus(RemoteDisplay.STATUS_CONNECTING);
+ updateDisplay(display);
+ mHandler.postDelayed(this, 2000);
+ }
+ }
+ }
+ }, 2000);
+ }
+ }
+
+ @Override
+ public void onDisconnect(RemoteDisplay display) {
+ Log.d(TAG, "onDisconnect: display.getId()=" + display.getId());
+
+ if (display == mTestDisplay1 || display == mTestDisplay2
+ || display == mTestDisplay6 || display == mTestDisplay8
+ || display == mTestDisplay9 || display == mTestDisplay10) {
+ display.setStatus(RemoteDisplay.STATUS_AVAILABLE);
+ updateDisplay(display);
+ }
+ }
+
+ @Override
+ public void onSetVolume(RemoteDisplay display, int volume) {
+ Log.d(TAG, "onSetVolume: display.getId()=" + display.getId()
+ + ", volume=" + volume);
+
+ if (display == mTestDisplay1) {
+ display.setVolume(Math.max(0, Math.min(display.getVolumeMax(), volume)));
+ updateDisplay(display);
+ }
+ }
+
+ @Override
+ public void onAdjustVolume(RemoteDisplay display, int delta) {
+ Log.d(TAG, "onAdjustVolume: display.getId()=" + display.getId()
+ + ", delta=" + delta);
+
+ if (display == mTestDisplay1) {
+ display.setVolume(Math.max(0, Math.min(display.getVolumeMax(),
+ display .getVolume() + delta)));
+ updateDisplay(display);
+ }
+ }
+
+ @Override
+ public void addDisplay(RemoteDisplay display) {
+ Log.d(TAG, "addDisplay: display=" + display);
+ super.addDisplay(display);
+ }
+
+ @Override
+ public void removeDisplay(RemoteDisplay display) {
+ Log.d(TAG, "removeDisplay: display=" + display);
+ super.removeDisplay(display);
+ }
+
+ @Override
+ public void updateDisplay(RemoteDisplay display) {
+ Log.d(TAG, "updateDisplay: display=" + display);
+ super.updateDisplay(display);
+ }
+
+ private final Runnable mBlink = new Runnable() {
+ @Override
+ public void run() {
+ if (mTestDisplay7 == null) {
+ if (mBlinking) {
+ mTestDisplay7 = new RemoteDisplay("testDisplay7",
+ "Test Display 7 (blinky)");
+ mTestDisplay7.setDescription("Comes and goes but can't connect");
+ mTestDisplay7.setStatus(RemoteDisplay.STATUS_AVAILABLE);
+ addDisplay(mTestDisplay7);
+ mHandler.postDelayed(this, 7000);
+ }
+ } else {
+ removeDisplay(mTestDisplay7);
+ mTestDisplay7 = null;
+ if (mBlinking) {
+ mHandler.postDelayed(this, 4000);
+ }
+ }
+ }
+ };
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 17b0eb6..bcd08eb4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -290,7 +290,7 @@
TypedValue out = new TypedValue();
if (ResourceHelper.parseFloatAttribute("textSize", textSize.getValue(), out,
true /*requireUnit*/)) {
- textView.setTextSize(
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
out.getDimension(bridgeContext.getResources().getDisplayMetrics()));
}
}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 5a1928c..149bda6 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -117,7 +117,7 @@
void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable);
- boolean requestBatchedScan(in BatchedScanSettings requested, IBinder binder);
+ boolean requestBatchedScan(in BatchedScanSettings requested, IBinder binder, in WorkSource ws);
void stopBatchedScan(in BatchedScanSettings requested);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7fc8bef..3b47e63 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -799,7 +799,13 @@
*/
public boolean requestBatchedScan(BatchedScanSettings requested) {
try {
- return mService.requestBatchedScan(requested, new Binder());
+ return mService.requestBatchedScan(requested, new Binder(), null);
+ } catch (RemoteException e) { return false; }
+ }
+ /** @hide */
+ public boolean requestBatchedScan(BatchedScanSettings requested, WorkSource workSource) {
+ try {
+ return mService.requestBatchedScan(requested, new Binder(), workSource);
} catch (RemoteException e) { return false; }
}
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 520668e..c2f278a 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -39,7 +39,6 @@
public class WifiNative {
private static final boolean DBG = false;
- private static final boolean VDBG = false;
private final String mTAG;
private static final int DEFAULT_GROUP_OWNER_INTENT = 6;
@@ -118,12 +117,12 @@
public boolean connectToSupplicant() {
// No synchronization necessary .. it is implemented in WifiMonitor
- if (VDBG) localLog(mInterfacePrefix + "connectToSupplicant");
+ localLog(mInterfacePrefix + "connectToSupplicant");
return connectToSupplicantNative();
}
public void closeSupplicantConnection() {
- if (VDBG) localLog(mInterfacePrefix + "closeSupplicantConnection");
+ localLog(mInterfacePrefix + "closeSupplicantConnection");
closeSupplicantConnectionNative();
}
@@ -136,9 +135,9 @@
if (DBG) Log.d(mTAG, "doBoolean: " + command);
synchronized (mLock) {
int cmdId = getNewCmdIdLocked();
- if (VDBG) localLog(cmdId + "->" + mInterfacePrefix + command);
+ localLog(cmdId + "->" + mInterfacePrefix + command);
boolean result = doBooleanCommandNative(mInterfacePrefix + command);
- if (VDBG) localLog(cmdId + "<-" + result);
+ localLog(cmdId + "<-" + result);
if (DBG) Log.d(mTAG, " returned " + result);
return result;
}
@@ -148,9 +147,9 @@
if (DBG) Log.d(mTAG, "doInt: " + command);
synchronized (mLock) {
int cmdId = getNewCmdIdLocked();
- if (VDBG) localLog(cmdId + "->" + mInterfacePrefix + command);
+ localLog(cmdId + "->" + mInterfacePrefix + command);
int result = doIntCommandNative(mInterfacePrefix + command);
- if (VDBG) localLog(cmdId + "<-" + result);
+ localLog(cmdId + "<-" + result);
if (DBG) Log.d(mTAG, " returned " + result);
return result;
}
@@ -160,9 +159,9 @@
if (DBG) Log.d(mTAG, "doString: " + command);
synchronized (mLock) {
int cmdId = getNewCmdIdLocked();
- if (VDBG) localLog(cmdId + "->" + mInterfacePrefix + command);
+ localLog(cmdId + "->" + mInterfacePrefix + command);
String result = doStringCommandNative(mInterfacePrefix + command);
- if (VDBG) localLog(cmdId + "<-" + result);
+ localLog(cmdId + "<-" + result);
if (DBG) Log.d(mTAG, " returned " + result);
return result;
}
@@ -298,7 +297,9 @@
* the firmware can remember before it runs out of buffer space or -1 on error
*/
public String setBatchedScanSettings(BatchedScanSettings settings) {
- if (settings == null) return doStringCommand("DRIVER WLS_BATCHING STOP");
+ if (settings == null) {
+ return doStringCommand("DRIVER WLS_BATCHING STOP");
+ }
String cmd = "DRIVER WLS_BATCHING SET SCANFREQ=" + settings.scanIntervalSec;
cmd += " MSCAN=" + settings.maxScansPerBatch;
if (settings.maxApPerScan != BatchedScanSettings.UNSPECIFIED) {
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 78da7e7..eedd66f 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -57,6 +57,7 @@
import android.net.wifi.p2p.WifiP2pService;
import android.os.BatteryStats;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Message;
@@ -416,7 +417,8 @@
/* change the batch scan settings.
* arg1 = responsible UID
- * obj = the new settings
+ * arg2 = csph (channel scans per hour)
+ * obj = bundle with the new settings and the optional worksource
*/
public static final int CMD_SET_BATCHED_SCAN = BASE + 135;
public static final int CMD_START_NEXT_BATCHED_SCAN = BASE + 136;
@@ -628,6 +630,15 @@
private BatchedScanSettings mBatchedScanSettings = null;
+ /**
+ * Track the worksource/cost of the current settings and track what's been noted
+ * to the battery stats, so we can mark the end of the previous when changing.
+ */
+ private WorkSource mBatchedScanWorkSource = null;
+ private int mBatchedScanCsph = 0;
+ private WorkSource mNotedBatchedScanWorkSource = null;
+ private int mNotedBatchedScanCsph = 0;
+
public WifiStateMachine(Context context, String wlanInterface) {
super("WifiStateMachine");
@@ -841,8 +852,14 @@
/**
* start or stop batched scanning using the given settings
*/
- public void setBatchedScanSettings(BatchedScanSettings settings, int callingUid) {
- sendMessage(CMD_SET_BATCHED_SCAN, callingUid, 0, settings);
+ private static final String BATCHED_SETTING = "batched_settings";
+ private static final String BATCHED_WORKSOURCE = "batched_worksource";
+ public void setBatchedScanSettings(BatchedScanSettings settings, int callingUid, int csph,
+ WorkSource workSource) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(BATCHED_SETTING, settings);
+ bundle.putParcelable(BATCHED_WORKSOURCE, workSource);
+ sendMessage(CMD_SET_BATCHED_SCAN, callingUid, csph, bundle);
}
public List<BatchedScanResult> syncGetBatchedScanResultsList() {
@@ -861,6 +878,8 @@
}
private void startBatchedScan() {
+ if (mBatchedScanSettings == null) return;
+
if (mDhcpActive) {
if (DBG) log("not starting Batched Scans due to DHCP");
return;
@@ -872,10 +891,10 @@
mAlarmManager.cancel(mBatchedScanIntervalIntent);
String scansExpected = mWifiNative.setBatchedScanSettings(mBatchedScanSettings);
-
try {
mExpectedBatchedScans = Integer.parseInt(scansExpected);
setNextBatchedAlarm(mExpectedBatchedScans);
+ if (mExpectedBatchedScans > 0) noteBatchedScanStart();
} catch (NumberFormatException e) {
stopBatchedScan();
loge("Exception parsing WifiNative.setBatchedScanSettings response " + e);
@@ -918,25 +937,31 @@
}
// return true if new/different
- private boolean recordBatchedScanSettings(BatchedScanSettings settings) {
- if (DBG) log("set batched scan to " + settings);
+ private boolean recordBatchedScanSettings(int responsibleUid, int csph, Bundle bundle) {
+ BatchedScanSettings settings = bundle.getParcelable(BATCHED_SETTING);
+ WorkSource responsibleWorkSource = bundle.getParcelable(BATCHED_WORKSOURCE);
+
+ if (DBG) {
+ log("set batched scan to " + settings + " for uid=" + responsibleUid +
+ ", worksource=" + responsibleWorkSource);
+ }
if (settings != null) {
- // TODO - noteBatchedScanStart(message.arg1);
if (settings.equals(mBatchedScanSettings)) return false;
} else {
if (mBatchedScanSettings == null) return false;
- // TODO - noteBatchedScanStop(message.arg1);
}
mBatchedScanSettings = settings;
+ if (responsibleWorkSource == null) responsibleWorkSource = new WorkSource(responsibleUid);
+ mBatchedScanWorkSource = responsibleWorkSource;
+ mBatchedScanCsph = csph;
return true;
}
private void stopBatchedScan() {
mAlarmManager.cancel(mBatchedScanIntervalIntent);
- if (mBatchedScanSettings != null) {
- retrieveBatchedScanData();
- mWifiNative.setBatchedScanSettings(null);
- }
+ retrieveBatchedScanData();
+ mWifiNative.setBatchedScanSettings(null);
+ noteBatchedScanStop();
}
private void setNextBatchedAlarm(int scansExpected) {
@@ -1164,6 +1189,44 @@
}
}
+ private void noteBatchedScanStart() {
+ // note the end of a previous scan set
+ if (mNotedBatchedScanWorkSource != null &&
+ (mNotedBatchedScanWorkSource.equals(mBatchedScanWorkSource) == false ||
+ mNotedBatchedScanCsph != mBatchedScanCsph)) {
+ try {
+ mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
+ } catch (RemoteException e) {
+ log(e.toString());
+ } finally {
+ mNotedBatchedScanWorkSource = null;
+ mNotedBatchedScanCsph = 0;
+ }
+ }
+ // note the start of the new
+ try {
+ mBatteryStats.noteWifiBatchedScanStartedFromSource(mBatchedScanWorkSource,
+ mBatchedScanCsph);
+ mNotedBatchedScanWorkSource = mBatchedScanWorkSource;
+ mNotedBatchedScanCsph = mBatchedScanCsph;
+ } catch (RemoteException e) {
+ log(e.toString());
+ }
+ }
+
+ private void noteBatchedScanStop() {
+ if (mNotedBatchedScanWorkSource != null) {
+ try {
+ mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
+ } catch (RemoteException e) {
+ log(e.toString());
+ } finally {
+ mNotedBatchedScanWorkSource = null;
+ mNotedBatchedScanCsph = 0;
+ }
+ }
+ }
+
private void startScanNative(int type) {
mWifiNative.scan(type);
mScanResultIsPending = true;
@@ -1868,6 +1931,9 @@
return;
}
+ // note that all these splits and substrings keep references to the original
+ // huge string buffer while the amount we really want is generally pretty small
+ // so make copies instead (one example b/11087956 wasted 400k of heap here).
synchronized(mScanResultCache) {
mScanResults = new ArrayList<ScanResult>();
String[] lines = scanResults.split("\n");
@@ -2307,9 +2373,7 @@
mDhcpActive = false;
- if (mBatchedScanSettings != null) {
- startBatchedScan();
- }
+ startBatchedScan();
}
private void handleSuccessfulIpConfiguration(DhcpResults dhcpResults) {
@@ -2447,7 +2511,7 @@
}
break;
case CMD_SET_BATCHED_SCAN:
- recordBatchedScanSettings((BatchedScanSettings)message.obj);
+ recordBatchedScanSettings(message.arg1, message.arg2, (Bundle)message.obj);
break;
case CMD_POLL_BATCHED_SCAN:
handleBatchedScanPollRequest();
@@ -2960,9 +3024,7 @@
mDhcpActive = false;
- if (mBatchedScanSettings != null) {
- startBatchedScan();
- }
+ startBatchedScan();
if (mOperationalMode != CONNECT_MODE) {
mWifiNative.disconnect();
@@ -3021,8 +3083,10 @@
startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
break;
case CMD_SET_BATCHED_SCAN:
- recordBatchedScanSettings((BatchedScanSettings)message.obj);
- startBatchedScan();
+ if (recordBatchedScanSettings(message.arg1, message.arg2,
+ (Bundle)message.obj)) {
+ startBatchedScan();
+ }
break;
case CMD_SET_COUNTRY_CODE:
String country = (String) message.obj;
@@ -3160,9 +3224,7 @@
updateBatteryWorkSource(null);
mScanResults = new ArrayList<ScanResult>();
- if (mBatchedScanSettings != null) {
- stopBatchedScan();
- }
+ stopBatchedScan();
final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);