Merge "Import translations. DO NOT MERGE"
diff --git a/Android.mk b/Android.mk
index c2910fd..be7e055 100644
--- a/Android.mk
+++ b/Android.mk
@@ -155,6 +155,7 @@
core/java/android/net/INetworkManagementEventObserver.aidl \
core/java/android/net/INetworkPolicyListener.aidl \
core/java/android/net/INetworkPolicyManager.aidl \
+ core/java/android/net/INetworkScoreService.aidl \
core/java/android/net/INetworkStatsService.aidl \
core/java/android/net/INetworkStatsSession.aidl \
core/java/android/net/nsd/INsdManager.aidl \
diff --git a/api/current.txt b/api/current.txt
index 3573e6d..4e2a3bb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27974,7 +27974,6 @@
public static final class TvInputManager.Session {
method public void release();
- method public void setSurface(android.view.Surface);
method public void setVolume(float);
method public void tune(android.net.Uri);
}
@@ -27996,12 +27995,22 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.tv.TvInputService";
}
- public static abstract class TvInputService.TvInputSessionImpl {
+ public abstract class TvInputService.TvInputSessionImpl {
ctor public TvInputService.TvInputSessionImpl();
+ method public android.view.View onCreateOverlayView();
method public abstract void onRelease();
method public abstract boolean onSetSurface(android.view.Surface);
method public abstract void onSetVolume(float);
method public abstract boolean onTune(android.net.Uri);
+ method public void setOverlayViewEnabled(boolean);
+ }
+
+ public class TvView extends android.view.SurfaceView {
+ ctor public TvView(android.content.Context);
+ ctor public TvView(android.content.Context, android.util.AttributeSet);
+ ctor public TvView(android.content.Context, android.util.AttributeSet, int);
+ method public void bindTvInput(android.content.ComponentName, android.tv.TvInputManager.SessionCreateCallback);
+ method public void unbindTvInput();
}
}
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 51cb12a..edf21dd 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -33,17 +33,20 @@
import android.view.Surface;
import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
-import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import dalvik.system.CloseGuard;
+
+import java.lang.ref.WeakReference;
/** @hide */
public class ActivityView extends ViewGroup {
- private final String TAG = "ActivityView";
- private final boolean DEBUG = false;
+ private static final String TAG = "ActivityView";
+ private static final boolean DEBUG = false;
+ DisplayMetrics mMetrics;
private final TextureView mTextureView;
- private IActivityContainer mActivityContainer;
+ private ActivityContainerWrapper mActivityContainer;
private Activity mActivity;
private int mWidth;
private int mHeight;
@@ -75,9 +78,23 @@
throw new IllegalStateException("The ActivityView's Context is not an Activity.");
}
+ try {
+ mActivityContainer = new ActivityContainerWrapper(
+ ActivityManagerNative.getDefault().createActivityContainer(
+ mActivity.getActivityToken(), new ActivityContainerCallback(this)));
+ } catch (RemoteException e) {
+ throw new RuntimeException("ActivityView: Unable to create ActivityContainer. "
+ + e);
+ }
+
mTextureView = new TextureView(context);
mTextureView.setSurfaceTextureListener(new ActivityViewSurfaceTextureListener());
addView(mTextureView);
+
+ WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
+ mMetrics = new DisplayMetrics();
+ wm.getDefaultDisplay().getMetrics(mMetrics);
+
if (DEBUG) Log.v(TAG, "ctor()");
}
@@ -86,57 +103,8 @@
mTextureView.layout(0, 0, r - l, b - t);
}
- @Override
- protected void onAttachedToWindow() {
- if (DEBUG) Log.v(TAG, "onAttachedToWindow()");
- super.onAttachedToWindow();
- try {
- final IBinder token = mActivity.getActivityToken();
- mActivityContainer = ActivityManagerNative.getDefault().createActivityContainer(token,
- new ActivityContainerCallback());
- } catch (RemoteException e) {
- throw new IllegalStateException("ActivityView: Unable to create ActivityContainer. "
- + e);
- }
-
- attachToSurfaceWhenReady();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- if (DEBUG) Log.v(TAG, "onDetachedFromWindow(): mActivityContainer=" + mActivityContainer);
- super.onDetachedFromWindow();
- if (mActivityContainer != null) {
- detach();
- try {
- ActivityManagerNative.getDefault().deleteActivityContainer(mActivityContainer);
- } catch (RemoteException e) {
- }
- mActivityContainer = null;
- }
- }
-
- @Override
- protected void onWindowVisibilityChanged(int visibility) {
- if (DEBUG) Log.v(TAG, "onWindowVisibilityChanged(): visibility=" + visibility);
- super.onWindowVisibilityChanged(visibility);
- switch (visibility) {
- case View.VISIBLE:
- attachToSurfaceWhenReady();
- break;
- case View.INVISIBLE:
- break;
- case View.GONE:
- break;
- }
- }
-
private boolean injectInputEvent(InputEvent event) {
- try {
- return mActivityContainer != null && mActivityContainer.injectEvent(event);
- } catch (RemoteException e) {
- return false;
- }
+ return mActivityContainer != null && mActivityContainer.injectEvent(event);
}
@Override
@@ -154,40 +122,45 @@
return super.onGenericMotionEvent(event);
}
+ @Override
+ public void onAttachedToWindow() {
+ if (DEBUG) Log.v(TAG, "onAttachedToWindow(): mActivityContainer=" + mActivityContainer +
+ " mSurface=" + mSurface);
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ if (DEBUG) Log.v(TAG, "onDetachedFromWindow(): mActivityContainer=" + mActivityContainer +
+ " mSurface=" + mSurface);
+ }
+
public boolean isAttachedToDisplay() {
return mSurface != null;
}
public void startActivity(Intent intent) {
+ if (mActivityContainer == null) {
+ throw new IllegalStateException("Attempt to call startActivity after release");
+ }
if (DEBUG) Log.v(TAG, "startActivity(): intent=" + intent + " " +
(isAttachedToDisplay() ? "" : "not") + " attached");
if (mSurface != null) {
- try {
- mActivityContainer.startActivity(intent);
- } catch (RemoteException e) {
- throw new IllegalStateException("ActivityView: Unable to startActivity. " + e);
- }
+ mActivityContainer.startActivity(intent);
} else {
mQueuedIntent = intent;
mQueuedPendingIntent = null;
}
}
- private void startActivityIntentSender(IIntentSender iIntentSender) {
- try {
- mActivityContainer.startActivityIntentSender(iIntentSender);
- } catch (RemoteException e) {
- throw new IllegalStateException(
- "ActivityView: Unable to startActivity from IntentSender. " + e);
- }
- }
-
public void startActivity(IntentSender intentSender) {
+ if (mActivityContainer == null) {
+ throw new IllegalStateException("Attempt to call startActivity after release");
+ }
if (DEBUG) Log.v(TAG, "startActivityIntentSender(): intentSender=" + intentSender + " " +
(isAttachedToDisplay() ? "" : "not") + " attached");
final IIntentSender iIntentSender = intentSender.getTarget();
if (mSurface != null) {
- startActivityIntentSender(iIntentSender);
+ mActivityContainer.startActivityIntentSender(iIntentSender);
} else {
mQueuedPendingIntent = iIntentSender;
mQueuedIntent = null;
@@ -195,84 +168,102 @@
}
public void startActivity(PendingIntent pendingIntent) {
+ if (mActivityContainer == null) {
+ throw new IllegalStateException("Attempt to call startActivity after release");
+ }
if (DEBUG) Log.v(TAG, "startActivityPendingIntent(): PendingIntent=" + pendingIntent + " "
+ (isAttachedToDisplay() ? "" : "not") + " attached");
final IIntentSender iIntentSender = pendingIntent.getTarget();
if (mSurface != null) {
- startActivityIntentSender(iIntentSender);
+ mActivityContainer.startActivityIntentSender(iIntentSender);
} else {
mQueuedPendingIntent = iIntentSender;
mQueuedIntent = null;
}
}
+ public void release() {
+ if (DEBUG) Log.v(TAG, "release() mActivityContainer=" + mActivityContainer +
+ " mSurface=" + mSurface);
+ if (mActivityContainer == null) {
+ Log.e(TAG, "Duplicate call to release");
+ return;
+ }
+ mActivityContainer.release();
+ mActivityContainer = null;
+
+ if (mSurface != null) {
+ mSurface.release();
+ mSurface = null;
+ }
+
+ mTextureView.setSurfaceTextureListener(null);
+ }
+
private void attachToSurfaceWhenReady() {
final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
- if (mActivityContainer == null || surfaceTexture == null || mSurface != null) {
+ if (surfaceTexture == null || mSurface != null) {
// Either not ready to attach, or already attached.
return;
}
- WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
- DisplayMetrics metrics = new DisplayMetrics();
- wm.getDefaultDisplay().getMetrics(metrics);
-
mSurface = new Surface(surfaceTexture);
try {
- mActivityContainer.attachToSurface(mSurface, mWidth, mHeight, metrics.densityDpi);
+ mActivityContainer.setSurface(mSurface, mWidth, mHeight, mMetrics.densityDpi);
} catch (RemoteException e) {
mSurface.release();
mSurface = null;
- throw new IllegalStateException(
- "ActivityView: Unable to create ActivityContainer. " + e);
+ throw new RuntimeException("ActivityView: Unable to create ActivityContainer. " + e);
}
if (DEBUG) Log.v(TAG, "attachToSurfaceWhenReady: " + (mQueuedIntent != null ||
mQueuedPendingIntent != null ? "" : "no") + " queued intent");
if (mQueuedIntent != null) {
- startActivity(mQueuedIntent);
+ mActivityContainer.startActivity(mQueuedIntent);
mQueuedIntent = null;
} else if (mQueuedPendingIntent != null) {
- startActivityIntentSender(mQueuedPendingIntent);
+ mActivityContainer.startActivityIntentSender(mQueuedPendingIntent);
mQueuedPendingIntent = null;
}
}
- private void detach() {
- if (DEBUG) Log.d(TAG, "detach: attached=" + isAttachedToDisplay());
- if (mSurface != null) {
- try {
- mActivityContainer.detachFromDisplay();
- } catch (RemoteException e) {
- }
- mSurface.release();
- mSurface = null;
- }
- }
-
private class ActivityViewSurfaceTextureListener implements SurfaceTextureListener {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width,
int height) {
+ if (mActivityContainer == null) {
+ return;
+ }
if (DEBUG) Log.d(TAG, "onSurfaceTextureAvailable: width=" + width + " height="
+ height);
mWidth = width;
mHeight = height;
- if (mActivityContainer != null) {
- attachToSurfaceWhenReady();
- }
+ attachToSurfaceWhenReady();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width,
int height) {
+ if (mActivityContainer == null) {
+ return;
+ }
if (DEBUG) Log.d(TAG, "onSurfaceTextureSizeChanged: w=" + width + " h=" + height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
+ if (mActivityContainer == null) {
+ return true;
+ }
if (DEBUG) Log.d(TAG, "onSurfaceTextureDestroyed");
- detach();
+ mSurface.release();
+ mSurface = null;
+ try {
+ mActivityContainer.setSurface(null, mWidth, mHeight, mMetrics.densityDpi);
+ } catch (RemoteException e) {
+ throw new RuntimeException(
+ "ActivityView: Unable to set surface of ActivityContainer. " + e);
+ }
return true;
}
@@ -283,13 +274,95 @@
}
- private class ActivityContainerCallback extends IActivityContainerCallback.Stub {
+ private static class ActivityContainerCallback extends IActivityContainerCallback.Stub {
+ private final WeakReference<ActivityView> mActivityViewWeakReference;
+
+ ActivityContainerCallback(ActivityView activityView) {
+ mActivityViewWeakReference = new WeakReference<ActivityView>(activityView);
+ }
+
@Override
public void setVisible(IBinder container, boolean visible) {
- if (DEBUG) Log.v(TAG, "setVisible(): container=" + container + " visible=" + visible);
- if (visible) {
- } else {
+ if (DEBUG) Log.v(TAG, "setVisible(): container=" + container + " visible=" + visible +
+ " ActivityView=" + mActivityViewWeakReference.get());
+ }
+ }
+
+ private static class ActivityContainerWrapper {
+ private final IActivityContainer mIActivityContainer;
+ private final CloseGuard mGuard = CloseGuard.get();
+
+ ActivityContainerWrapper(IActivityContainer container) {
+ mIActivityContainer = container;
+ mGuard.open("release");
+ }
+
+ void attachToDisplay(int displayId) {
+ try {
+ mIActivityContainer.attachToDisplay(displayId);
+ } catch (RemoteException e) {
}
}
+
+ void setSurface(Surface surface, int width, int height, int density)
+ throws RemoteException {
+ mIActivityContainer.setSurface(surface, width, height, density);
+ }
+
+ int startActivity(Intent intent) {
+ try {
+ return mIActivityContainer.startActivity(intent);
+ } catch (RemoteException e) {
+ throw new RuntimeException("ActivityView: Unable to startActivity. " + e);
+ }
+ }
+
+ int startActivityIntentSender(IIntentSender intentSender) {
+ try {
+ return mIActivityContainer.startActivityIntentSender(intentSender);
+ } catch (RemoteException e) {
+ throw new RuntimeException(
+ "ActivityView: Unable to startActivity from IntentSender. " + e);
+ }
+ }
+
+ int getDisplayId() {
+ try {
+ return mIActivityContainer.getDisplayId();
+ } catch (RemoteException e) {
+ return -1;
+ }
+ }
+
+ boolean injectEvent(InputEvent event) {
+ try {
+ return mIActivityContainer.injectEvent(event);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ void release() {
+ if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called");
+ try {
+ mIActivityContainer.release();
+ mGuard.close();
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: finalize called");
+ try {
+ if (mGuard != null) {
+ mGuard.warnIfOpen();
+ release();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
}
}
diff --git a/core/java/android/app/IActivityContainer.aidl b/core/java/android/app/IActivityContainer.aidl
index 5b80e06..cc3b10c 100644
--- a/core/java/android/app/IActivityContainer.aidl
+++ b/core/java/android/app/IActivityContainer.aidl
@@ -26,10 +26,10 @@
/** @hide */
interface IActivityContainer {
void attachToDisplay(int displayId);
- void attachToSurface(in Surface surface, int width, int height, int density);
- void detachFromDisplay();
+ void setSurface(in Surface surface, int width, int height, int density);
int startActivity(in Intent intent);
int startActivityIntentSender(in IIntentSender intentSender);
int getDisplayId();
boolean injectEvent(in InputEvent event);
+ void release();
}
diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl
new file mode 100644
index 0000000..a72d9a0
--- /dev/null
+++ b/core/java/android/net/INetworkScoreService.aidl
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2014, 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.net;
+
+import android.net.ScoredNetwork;
+
+/**
+ * A service for updating network scores from a network scorer application.
+ * @hide
+ */
+interface INetworkScoreService
+{
+ /**
+ * Update scores.
+ * @return whether the update was successful.
+ * @throws SecurityException if the caller is not the current active scorer.
+ */
+ boolean updateScores(in ScoredNetwork[] networks);
+
+ /**
+ * Clear all scores.
+ * @return whether the clear was successful.
+ * @throws SecurityException if the caller is neither the current active scorer nor the scorer
+ * manager.
+ */
+ boolean clearScores();
+
+ /**
+ * Set the active scorer and clear existing scores.
+ * @param packageName the package name of the new scorer to use.
+ * @return true if the operation succeeded, or false if the new package is not a valid scorer.
+ * @throws SecurityException if the caller is not the scorer manager.
+ */
+ boolean setActiveScorer(in String packageName);
+}
diff --git a/core/java/android/net/NetworkKey.java b/core/java/android/net/NetworkKey.java
index cc3ad3e..bc19658 100644
--- a/core/java/android/net/NetworkKey.java
+++ b/core/java/android/net/NetworkKey.java
@@ -19,11 +19,19 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Information which identifies a specific network.
*
* @hide
*/
+// NOTE: Ideally, we would abstract away the details of what identifies a network of a specific
+// type, so that all networks appear the same and can be scored without concern to the network type
+// itself. However, because no such cross-type identifier currently exists in the Android framework,
+// and because systems might obtain information about networks from sources other than Android
+// devices, we need to provide identifying details about each specific network type (wifi, cell,
+// etc.) so that clients can pull out these details depending on the type of network.
public class NetworkKey implements Parcelable {
/** A wifi network, for which {@link #wifiKey} will be populated. */
@@ -79,6 +87,21 @@
}
@Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ NetworkKey that = (NetworkKey) o;
+
+ return type == that.type && Objects.equals(wifiKey, that.wifiKey);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, wifiKey);
+ }
+
+ @Override
public String toString() {
switch (type) {
case TYPE_WIFI:
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 3430547..5e61613 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -19,6 +19,9 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
/**
* Class that manages communication between network subsystems and a network scorer.
@@ -40,7 +43,7 @@
* <p>The system keeps track of a default scorer application; at any time, only this application
* will receive {@link #ACTION_SCORE_NETWORKS} broadcasts and will be permitted to call
* {@link #updateScores}. Applications may determine the current default scorer with
- * {@link #getDefaultScorerPackage()} and request to change the default scorer by sending an
+ * {@link #getActiveScorerPackage()} and request to change the default scorer by sending an
* {@link #ACTION_CHANGE_DEFAULT} broadcast with another scorer.
*
* @hide
@@ -81,38 +84,82 @@
public static final String EXTRA_NETWORKS_TO_SCORE = "networksToScore";
private final Context mContext;
+ private final INetworkScoreService mService;
/** @hide */
public NetworkScoreManager(Context context) {
mContext = context;
+ IBinder iBinder = ServiceManager.getService(Context.NETWORK_SCORE_SERVICE);
+ mService = INetworkScoreService.Stub.asInterface(iBinder);
}
/**
- * Obtain the package name of the current default network scorer.
+ * Obtain the package name of the current active network scorer.
*
- * At any time, only one scorer application will receive {@link #ACTION_SCORE_NETWORKS}
+ * <p>At any time, only one scorer application will receive {@link #ACTION_SCORE_NETWORKS}
* broadcasts and be allowed to call {@link #updateScores}. Applications may use this method to
* determine the current scorer and offer the user the ability to select a different scorer via
* the {@link #ACTION_CHANGE_DEFAULT} intent.
- * @return the full package name of the current default scorer, or null if there is no active
+ * @return the full package name of the current active scorer, or null if there is no active
* scorer.
*/
- public String getDefaultScorerPackage() {
- // TODO: Implement.
- return null;
+ public String getActiveScorerPackage() {
+ return NetworkScorerAppManager.getActiveScorer(mContext);
}
/**
* Update network scores.
*
- * This may be called at any time to re-score active networks. Scores will generally be updated
- * quickly, but if this method is called too frequently, the scores may be held and applied at
- * a later time.
+ * <p>This may be called at any time to re-score active networks. Scores will generally be
+ * updated quickly, but if this method is called too frequently, the scores may be held and
+ * applied at a later time.
*
* @param networks the networks which have been scored by the scorer.
- * @throws SecurityException if the caller is not the default scorer.
+ * @return whether the update was successful.
+ * @throws SecurityException if the caller is not the active scorer.
*/
- public void updateScores(ScoredNetwork[] networks) throws SecurityException {
- // TODO: Implement.
+ public boolean updateScores(ScoredNetwork[] networks) throws SecurityException {
+ try {
+ return mService.updateScores(networks);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Clear network scores.
+ *
+ * <p>Should be called when all scores need to be invalidated, i.e. because the scoring
+ * algorithm has changed and old scores can no longer be compared to future scores.
+ *
+ * <p>Note that scores will be cleared automatically when the active scorer changes, as scores
+ * from one scorer cannot be compared to those from another scorer.
+ *
+ * @return whether the clear was successful.
+ * @throws SecurityException if the caller is not the active scorer or privileged.
+ */
+ public boolean clearScores() throws SecurityException {
+ try {
+ return mService.clearScores();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Set the active scorer to a new package and clear existing scores.
+ *
+ * @return true if the operation succeeded, or false if the new package is not a valid scorer.
+ * @throws SecurityException if the caller does not hold the
+ * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission indicating that
+ * it can manage scorer applications.
+ * @hide
+ */
+ public boolean setActiveScorer(String packageName) throws SecurityException {
+ try {
+ return mService.setActiveScorer(packageName);
+ } catch (RemoteException e) {
+ return false;
+ }
}
}
diff --git a/core/java/android/net/NetworkScorerApplication.java b/core/java/android/net/NetworkScorerAppManager.java
similarity index 88%
rename from core/java/android/net/NetworkScorerApplication.java
rename to core/java/android/net/NetworkScorerAppManager.java
index b137ad3..726208a 100644
--- a/core/java/android/net/NetworkScorerApplication.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -26,6 +26,7 @@
import android.provider.Settings;
import android.provider.Settings.Global;
import android.text.TextUtils;
+import android.util.Log;
import java.util.ArrayList;
import java.util.Collection;
@@ -36,13 +37,14 @@
*
* @hide
*/
-public final class NetworkScorerApplication {
+public final class NetworkScorerAppManager {
+ private static final String TAG = "NetworkScorerAppManager";
private static final Intent SCORE_INTENT =
new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS);
/** This class cannot be instantiated. */
- private NetworkScorerApplication() {}
+ private NetworkScorerAppManager() {}
/**
* Returns the list of available scorer app package names.
@@ -111,30 +113,38 @@
* @param context the context of the calling application
* @param packageName the packageName of the new scorer to use. If null, scoring will be
* disabled. Otherwise, the scorer will only be set if it is a valid scorer application.
+ * @return true if the scorer was changed, or false if the package is not a valid scorer.
*/
- public static void setActiveScorer(Context context, String packageName) {
+ public static boolean setActiveScorer(Context context, String packageName) {
String oldPackageName = Settings.Global.getString(context.getContentResolver(),
Settings.Global.NETWORK_SCORER_APP);
if (TextUtils.equals(oldPackageName, packageName)) {
// No change.
- return;
+ return true;
}
+ Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName);
+
if (packageName == null) {
Settings.Global.putString(context.getContentResolver(), Global.NETWORK_SCORER_APP,
null);
+ return true;
} else {
// We only make the change if the new package is valid.
Collection<String> applications = getAllValidScorers(context);
if (isPackageValidScorer(applications, packageName)) {
Settings.Global.putString(context.getContentResolver(),
Settings.Global.NETWORK_SCORER_APP, packageName);
+ return true;
+ } else {
+ Log.w(TAG, "Requested network scorer is not valid: " + packageName);
+ return false;
}
}
}
/** Determine whether the application with the given UID is the enabled scorer. */
- public static boolean isCallerDefaultScorer(Context context, int callingUid) {
+ public static boolean isCallerActiveScorer(Context context, int callingUid) {
String defaultApp = getActiveScorer(context);
if (defaultApp == null) {
return false;
diff --git a/core/java/android/net/RssiCurve.java b/core/java/android/net/RssiCurve.java
index 7af7998..33e81c2 100644
--- a/core/java/android/net/RssiCurve.java
+++ b/core/java/android/net/RssiCurve.java
@@ -19,6 +19,9 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Arrays;
+import java.util.Objects;
+
/**
* A curve defining the network score over a range of RSSI values.
*
@@ -94,6 +97,30 @@
out.writeByteArray(rssiBuckets);
}
+ /**
+ * Determine if two RSSI curves are defined in the same way.
+ *
+ * <p>Note that two curves can be equivalent but defined differently, e.g. if one bucket in one
+ * curve is split into two buckets in another. For the purpose of this method, these curves are
+ * not considered equal to each other.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ RssiCurve rssiCurve = (RssiCurve) o;
+
+ return start == rssiCurve.start &&
+ bucketWidth == rssiCurve.bucketWidth &&
+ Arrays.equals(rssiBuckets, rssiCurve.rssiBuckets);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(start, bucketWidth, rssiBuckets);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/net/ScoredNetwork.java b/core/java/android/net/ScoredNetwork.java
index 8af3c3c..7902313 100644
--- a/core/java/android/net/ScoredNetwork.java
+++ b/core/java/android/net/ScoredNetwork.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* A network identifier along with a score for the quality of that network.
*
@@ -80,6 +82,22 @@
}
@Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ScoredNetwork that = (ScoredNetwork) o;
+
+ return Objects.equals(networkKey, that.networkKey) &&
+ Objects.equals(rssiCurve, that.rssiCurve);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkKey, rssiCurve);
+ }
+
+ @Override
public String toString() {
return "ScoredNetwork[key=" + networkKey + ",score=" + rssiCurve + "]";
}
diff --git a/core/java/android/net/WifiKey.java b/core/java/android/net/WifiKey.java
index ffcd85a..9e92e89 100644
--- a/core/java/android/net/WifiKey.java
+++ b/core/java/android/net/WifiKey.java
@@ -19,6 +19,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
import java.util.regex.Pattern;
/**
@@ -87,6 +88,21 @@
}
@Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ WifiKey wifiKey = (WifiKey) o;
+
+ return Objects.equals(ssid, wifiKey.ssid) && Objects.equals(bssid, wifiKey.bssid);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ssid, bssid);
+ }
+
+ @Override
public String toString() {
return "WifiKey[SSID=" + ssid + ",BSSID=" + bssid + "]";
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 426f21e..e640649 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2861,29 +2861,21 @@
int oldTemp = -1;
int oldVolt = -1;
long lastTime = -1;
+ long firstTime = -1;
- public void printNextItem(PrintWriter pw, HistoryItem rec, long now, boolean checkin,
+ public void printNextItem(PrintWriter pw, HistoryItem rec, long baseTime, boolean checkin,
boolean verbose) {
if (!checkin) {
pw.print(" ");
- if (now >= 0) {
- TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
- } else {
- TimeUtils.formatDuration(rec.time, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
- }
+ TimeUtils.formatDuration(rec.time - baseTime, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
pw.print(" (");
pw.print(rec.numReadInts);
pw.print(") ");
} else {
if (lastTime < 0) {
- if (now >= 0) {
- pw.print("@");
- pw.print(rec.time-now);
- } else {
- pw.print(rec.time);
- }
+ pw.print(rec.time - baseTime);
} else {
- pw.print(rec.time-lastTime);
+ pw.print(rec.time - lastTime);
}
lastTime = rec.time;
}
@@ -3132,10 +3124,27 @@
pw.println("):");
HistoryPrinter hprinter = new HistoryPrinter();
long lastTime = -1;
+ long baseTime = -1;
+ boolean printed = false;
while (getNextHistoryLocked(rec)) {
lastTime = rec.time;
+ if (baseTime < 0) {
+ baseTime = lastTime;
+ }
if (rec.time >= histStart) {
- hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, false,
+ if (histStart >= 0 && !printed) {
+ if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
+ printed = true;
+ } else if (rec.currentTime != 0) {
+ printed = true;
+ byte cmd = rec.cmd;
+ rec.cmd = HistoryItem.CMD_CURRENT_TIME;
+ hprinter.printNextItem(pw, rec, baseTime, false,
+ (flags&DUMP_VERBOSE) != 0);
+ rec.cmd = cmd;
+ }
+ }
+ hprinter.printNextItem(pw, rec, baseTime, false,
(flags&DUMP_VERBOSE) != 0);
}
}
@@ -3152,8 +3161,12 @@
try {
pw.println("Old battery History:");
HistoryPrinter hprinter = new HistoryPrinter();
+ long baseTime = -1;
while (getNextOldHistoryLocked(rec)) {
- hprinter.printNextItem(pw, rec, now, false, (flags&DUMP_VERBOSE) != 0);
+ if (baseTime < 0) {
+ baseTime = rec.time;
+ }
+ hprinter.printNextItem(pw, rec, baseTime, false, (flags&DUMP_VERBOSE) != 0);
}
pw.println();
} finally {
@@ -3226,20 +3239,42 @@
pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
pw.print(HISTORY_STRING_POOL); pw.print(',');
pw.print(i);
- pw.print(',');
- pw.print(getHistoryTagPoolString(i));
- pw.print(',');
+ pw.print(",");
pw.print(getHistoryTagPoolUid(i));
+ pw.print(",\"");
+ String str = getHistoryTagPoolString(i);
+ str = str.replace("\\", "\\\\");
+ str = str.replace("\"", "\\\"");
+ pw.print(str);
+ pw.print("\"");
pw.println();
}
HistoryPrinter hprinter = new HistoryPrinter();
long lastTime = -1;
+ long baseTime = -1;
+ boolean printed = false;
while (getNextHistoryLocked(rec)) {
lastTime = rec.time;
+ if (baseTime < 0) {
+ baseTime = lastTime;
+ }
if (rec.time >= histStart) {
+ if (histStart >= 0 && !printed) {
+ if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
+ printed = true;
+ } else if (rec.currentTime != 0) {
+ printed = true;
+ byte cmd = rec.cmd;
+ rec.cmd = HistoryItem.CMD_CURRENT_TIME;
+ pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+ pw.print(HISTORY_DATA); pw.print(',');
+ hprinter.printNextItem(pw, rec, baseTime, true, false);
+ rec.cmd = cmd;
+ }
+ }
pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
pw.print(HISTORY_DATA); pw.print(',');
- hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, true, false);
+ hprinter.printNextItem(pw, rec, baseTime, true, false);
}
}
if (histStart >= 0) {
diff --git a/core/java/android/tv/ITvInputManager.aidl b/core/java/android/tv/ITvInputManager.aidl
index a927dc9..a4c99e4 100644
--- a/core/java/android/tv/ITvInputManager.aidl
+++ b/core/java/android/tv/ITvInputManager.aidl
@@ -17,6 +17,7 @@
package android.tv;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.net.Uri;
import android.tv.ITvInputClient;
import android.tv.TvInputInfo;
@@ -40,4 +41,9 @@
void setSurface(in IBinder sessionToken, in Surface surface, int userId);
void setVolume(in IBinder sessionToken, float volume, int userId);
void tune(in IBinder sessionToken, in Uri channelUri, int userId);
+
+ void createOverlayView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
+ int userId);
+ void relayoutOverlayView(in IBinder sessionToken, in Rect frame, int userId);
+ void removeOverlayView(in IBinder sessionToken, int userId);
}
diff --git a/core/java/android/tv/ITvInputService.aidl b/core/java/android/tv/ITvInputService.aidl
index d80f286..672784f 100644
--- a/core/java/android/tv/ITvInputService.aidl
+++ b/core/java/android/tv/ITvInputService.aidl
@@ -17,7 +17,6 @@
package android.tv;
import android.tv.ITvInputServiceCallback;
-import android.tv.ITvInputSession;
import android.tv.ITvInputSessionCallback;
/**
diff --git a/core/java/android/tv/ITvInputSession.aidl b/core/java/android/tv/ITvInputSession.aidl
index d379d2d..32fee4b 100644
--- a/core/java/android/tv/ITvInputSession.aidl
+++ b/core/java/android/tv/ITvInputSession.aidl
@@ -16,6 +16,7 @@
package android.tv;
+import android.graphics.Rect;
import android.net.Uri;
import android.view.Surface;
@@ -31,4 +32,8 @@
// is to introduce some new concepts that will solve a number of problems in audio policy today.
void setVolume(float volume);
void tune(in Uri channelUri);
+
+ void createOverlayView(in IBinder windowToken, in Rect frame);
+ void relayoutOverlayView(in Rect frame);
+ void removeOverlayView();
}
diff --git a/core/java/android/tv/ITvInputSessionWrapper.java b/core/java/android/tv/ITvInputSessionWrapper.java
index 66fe5e1..a6e0877 100644
--- a/core/java/android/tv/ITvInputSessionWrapper.java
+++ b/core/java/android/tv/ITvInputSessionWrapper.java
@@ -17,13 +17,16 @@
package android.tv;
import android.content.Context;
+import android.graphics.Rect;
import android.net.Uri;
+import android.os.IBinder;
import android.os.Message;
import android.tv.TvInputService.TvInputSessionImpl;
import android.util.Log;
import android.view.Surface;
import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
/**
* Implements the internal ITvInputSession interface to convert incoming calls on to it back to
@@ -38,6 +41,9 @@
private static final int DO_SET_SURFACE = 2;
private static final int DO_SET_VOLUME = 3;
private static final int DO_TUNE = 4;
+ private static final int DO_CREATE_OVERLAY_VIEW = 5;
+ private static final int DO_RELAYOUT_OVERLAY_VIEW = 6;
+ private static final int DO_REMOVE_OVERLAY_VIEW = 7;
private TvInputSessionImpl mTvInputSession;
private final HandlerCaller mCaller;
@@ -71,6 +77,20 @@
mTvInputSession.tune((Uri) msg.obj);
return;
}
+ case DO_CREATE_OVERLAY_VIEW: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mTvInputSession.createOverlayView((IBinder) args.arg1, (Rect) args.arg2);
+ args.recycle();
+ return;
+ }
+ case DO_RELAYOUT_OVERLAY_VIEW: {
+ mTvInputSession.relayoutOverlayView((Rect) msg.obj);
+ return;
+ }
+ case DO_REMOVE_OVERLAY_VIEW: {
+ mTvInputSession.removeOverlayView(true);
+ return;
+ }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
return;
@@ -97,4 +117,20 @@
public void tune(Uri channelUri) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_TUNE, channelUri));
}
+
+ @Override
+ public void createOverlayView(IBinder windowToken, Rect frame) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_OVERLAY_VIEW, windowToken,
+ frame));
+ }
+
+ @Override
+ public void relayoutOverlayView(Rect frame) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RELAYOUT_OVERLAY_VIEW, frame));
+ }
+
+ @Override
+ public void removeOverlayView() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_OVERLAY_VIEW));
+ }
}
diff --git a/core/java/android/tv/TvInputManager.java b/core/java/android/tv/TvInputManager.java
index 4cf2b35..05f0b9c 100644
--- a/core/java/android/tv/TvInputManager.java
+++ b/core/java/android/tv/TvInputManager.java
@@ -17,6 +17,7 @@
package android.tv;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
@@ -24,6 +25,7 @@
import android.util.Log;
import android.util.SparseArray;
import android.view.Surface;
+import android.view.View;
import java.util.ArrayList;
import java.util.HashMap;
@@ -320,8 +322,8 @@
/** The Session provides the per-session functionality of TV inputs. */
public static final class Session {
private final ITvInputManager mService;
- private final IBinder mToken;
private final int mUserId;
+ private IBinder mToken;
/** @hide */
private Session(ComponentName name, IBinder token, ITvInputManager service, int userId) {
@@ -332,10 +334,16 @@
/**
* Releases this session.
+ *
+ * @throws IllegalStateException if the session has been already released.
*/
public void release() {
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
try {
mService.releaseSession(mToken, mUserId);
+ mToken = null;
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -345,8 +353,12 @@
* Sets the {@link android.view.Surface} for this session.
*
* @param surface A {@link android.view.Surface} used to render video.
+ * @throws IllegalStateException if the session has been already released.
*/
- public void setSurface(Surface surface) {
+ void setSurface(Surface surface) {
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
// surface can be null.
try {
mService.setSurface(mToken, surface, mUserId);
@@ -360,8 +372,12 @@
*
* @param volume A volume value between 0.0f to 1.0f.
* @throws IllegalArgumentException if the volume value is out of range.
+ * @throws IllegalStateException if the session has been already released.
*/
public void setVolume(float volume) {
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
try {
if (volume < 0.0f || volume > 1.0f) {
throw new IllegalArgumentException("volume should be between 0.0f and 1.0f");
@@ -377,16 +393,90 @@
*
* @param channelUri The URI of a channel.
* @throws IllegalArgumentException if the argument is {@code null}.
+ * @throws IllegalStateException if the session has been already released.
*/
public void tune(Uri channelUri) {
if (channelUri == null) {
throw new IllegalArgumentException("channelUri cannot be null");
}
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
try {
mService.tune(mToken, channelUri, mUserId);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
+
+ /**
+ * Creates an overlay view. Once the overlay view is created, {@link #relayoutOverlayView}
+ * should be called whenever the layout of its containing view is changed.
+ * {@link #removeOverlayView()} should be called to remove the overlay view.
+ * Since a session can have only one overlay view, this method should be called only once
+ * or it can be called again after calling {@link #removeOverlayView()}.
+ *
+ * @param view A view playing TV.
+ * @param frame A position of the overlay view.
+ * @throws IllegalArgumentException if any of the arguments is {@code null}.
+ * @throws IllegalStateException if {@code view} is not attached to a window or
+ * if the session has been already released.
+ */
+ void createOverlayView(View view, Rect frame) {
+ if (view == null) {
+ throw new IllegalArgumentException("view cannot be null");
+ }
+ if (frame == null) {
+ throw new IllegalArgumentException("frame cannot be null");
+ }
+ if (view.getWindowToken() == null) {
+ throw new IllegalStateException("view must be attached to a window");
+ }
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
+ try {
+ mService.createOverlayView(mToken, view.getWindowToken(), frame, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Relayouts the current overlay view.
+ *
+ * @param frame A new position of the overlay view.
+ * @throws IllegalArgumentException if the arguments is {@code null}.
+ * @throws IllegalStateException if the session has been already released.
+ */
+ void relayoutOverlayView(Rect frame) {
+ if (frame == null) {
+ throw new IllegalArgumentException("frame cannot be null");
+ }
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
+ try {
+ mService.relayoutOverlayView(mToken, frame, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Removes the current overlay view.
+ *
+ * @throws IllegalStateException if the session has been already released.
+ */
+ void removeOverlayView() {
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
+ try {
+ mService.removeOverlayView(mToken, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
}
diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java
index d7f6c32..80eb407 100644
--- a/core/java/android/tv/TvInputService.java
+++ b/core/java/android/tv/TvInputService.java
@@ -18,7 +18,10 @@
import android.app.Service;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
@@ -26,7 +29,10 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
+import android.view.Gravity;
import android.view.Surface;
+import android.view.View;
+import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
@@ -125,7 +131,37 @@
/**
* Base class for derived classes to implement to provide {@link TvInputManager.Session}.
*/
- public abstract static class TvInputSessionImpl {
+ public abstract class TvInputSessionImpl {
+ private final WindowManager mWindowManager;
+ private WindowManager.LayoutParams mWindowParams;
+ private View mOverlayView;
+ private boolean mOverlayViewEnabled;
+ private IBinder mWindowToken;
+ private Rect mOverlayFrame;
+
+ public TvInputSessionImpl() {
+ mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
+ }
+
+ public void setOverlayViewEnabled(final boolean enable) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (enable == mOverlayViewEnabled) {
+ return;
+ }
+ mOverlayViewEnabled = enable;
+ if (enable) {
+ if (mWindowToken != null) {
+ createOverlayView(mWindowToken, mOverlayFrame);
+ }
+ } else {
+ removeOverlayView(false);
+ }
+ }
+ });
+ }
+
/**
* Called when the session is released.
*/
@@ -157,11 +193,22 @@
public abstract boolean onTune(Uri channelUri);
/**
+ * Called when an application requests to create an overlay view. Each session
+ * implementation can override this method and return its own view.
+ *
+ * @return a view attached to the overlay window
+ */
+ public View onCreateOverlayView() {
+ return null;
+ }
+
+ /**
* This method is called when the application would like to stop using the current input
* session.
*/
void release() {
onRelease();
+ removeOverlayView(true);
}
/**
@@ -186,6 +233,87 @@
onTune(channelUri);
// TODO: Handle failure.
}
+
+ /**
+ * Creates an overlay view. This calls {@link onCreateOverlayView} to get
+ * a view to attach to the overlay window.
+ *
+ * @param windowToken A window token of an application.
+ * @param frame A position of the overlay view.
+ */
+ void createOverlayView(IBinder windowToken, Rect frame) {
+ if (mOverlayView != null) {
+ mWindowManager.removeView(mOverlayView);
+ mOverlayView = null;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "create overlay view(" + frame + ")");
+ }
+ mWindowToken = windowToken;
+ mOverlayFrame = frame;
+ if (!mOverlayViewEnabled) {
+ return;
+ }
+ mOverlayView = onCreateOverlayView();
+ if (mOverlayView == null) {
+ return;
+ }
+ // TvView's window type is TYPE_APPLICATION_MEDIA and we want to create
+ // an overlay window above the media window but below the application window.
+ int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+ // We make the overlay view non-focusable and non-touchable so that
+ // the application that owns the window token can decide whether to consume or
+ // dispatch the input events.
+ int flag = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ mWindowParams = new WindowManager.LayoutParams(
+ frame.right - frame.left, frame.bottom - frame.top,
+ frame.left, frame.top, type, flag, PixelFormat.TRANSPARENT);
+ mWindowParams.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ mWindowParams.gravity = Gravity.START | Gravity.TOP;
+ mWindowParams.token = windowToken;
+ mWindowManager.addView(mOverlayView, mWindowParams);
+ }
+
+ /**
+ * Relayouts the current overlay view.
+ *
+ * @param frame A new position of the overlay view.
+ */
+ void relayoutOverlayView(Rect frame) {
+ if (DEBUG) {
+ Log.d(TAG, "relayout overlay view(" + frame + ")");
+ }
+ mOverlayFrame = frame;
+ if (!mOverlayViewEnabled || mOverlayView == null) {
+ return;
+ }
+ mWindowParams.x = frame.left;
+ mWindowParams.y = frame.top;
+ mWindowParams.width = frame.right - frame.left;
+ mWindowParams.height = frame.bottom - frame.top;
+ mWindowManager.updateViewLayout(mOverlayView, mWindowParams);
+ }
+
+ /**
+ * Removes the current overlay view.
+ */
+ void removeOverlayView(boolean clearWindowToken) {
+ if (DEBUG) {
+ Log.d(TAG, "remove overlay view(" + mOverlayView + ")");
+ }
+ if (clearWindowToken) {
+ mWindowToken = null;
+ mOverlayFrame = null;
+ }
+ if (mOverlayView != null) {
+ mWindowManager.removeView(mOverlayView);
+ mOverlayView = null;
+ mWindowParams = null;
+ }
+ }
}
private final class ServiceHandler extends Handler {
diff --git a/core/java/android/tv/TvView.java b/core/java/android/tv/TvView.java
new file mode 100644
index 0000000..325950d
--- /dev/null
+++ b/core/java/android/tv/TvView.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2014 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.tv;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.tv.TvInputManager;
+import android.tv.TvInputManager.Session;
+import android.tv.TvInputManager.SessionCreateCallback;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewTreeObserver;
+
+/**
+ * View playing TV
+ */
+public class TvView extends SurfaceView {
+ private static final String TAG = "TvView";
+
+ private final Handler mHandler = new Handler();
+ private TvInputManager.Session mSession;
+ private Surface mSurface;
+ private boolean mOverlayViewCreated;
+ private Rect mOverlayViewFrame;
+ private boolean mGlobalListenersAdded;
+ private TvInputManager mTvInputManager;
+ private SessionCreateCallback mSessionCreateCallback;
+
+ private SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Log.d(TAG, "surfaceChanged(holder=" + holder + ", format=" + format + ", width=" + width
+ + ", height=" + height + ")");
+ if (holder.getSurface() == mSurface) {
+ return;
+ }
+ mSurface = holder.getSurface();
+ setSessionSurface(mSurface);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mSurface = holder.getSurface();
+ setSessionSurface(mSurface);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mSurface = null;
+ setSessionSurface(null);
+ }
+ };
+
+ public TvView(Context context) {
+ this(context, null, 0);
+ }
+
+ public TvView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TvView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ getHolder().addCallback(mSurfaceHolderCallback);
+ mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
+ }
+
+ /**
+ * Binds a TV input to this view. {@link SessionCreateCallback#onSessionCreated} will be
+ * called to send the result of this binding with {@link TvInputManager.Session}.
+ * If a TV input is already bound, the input will be unbound from this view and its session
+ * will be released.
+ *
+ * @param name TV input name will be bound to this view.
+ * @param callback called when TV input is bound. The callback sends
+ * {@link TvInputManager.Session}
+ * @throws IllegalArgumentException if any of the arguments is {@code null}.
+ */
+ public void bindTvInput(ComponentName name, SessionCreateCallback callback) {
+ if (name == null) {
+ throw new IllegalArgumentException("name cannot be null");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+ if (mSession != null) {
+ release();
+ }
+ // When bindTvInput is called multiple times before the callback is called,
+ // only the callback of the last bindTvInput call will be actually called back.
+ // The previous callbacks will be ignored. For the logic, mSessionCreateCallback
+ // is newly assigned for every bindTvInput call and compared with
+ // MySessionCreateCallback.this.
+ mSessionCreateCallback = new MySessionCreateCallback(callback);
+ mTvInputManager.createSession(name, mSessionCreateCallback, mHandler);
+ }
+
+ /**
+ * Unbinds a TV input currently bound. Its corresponding {@link TvInputManager.Session}
+ * is released.
+ */
+ public void unbindTvInput() {
+ if (mSession != null) {
+ release();
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ createSessionOverlayView();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ removeSessionOverlayView();
+ super.onDetachedFromWindow();
+ }
+
+ /** @hide */
+ @Override
+ protected void updateWindow(boolean force, boolean redrawNeeded) {
+ super.updateWindow(force, redrawNeeded);
+ relayoutSessionOverlayView();
+ }
+
+ private void release() {
+ setSessionSurface(null);
+ removeSessionOverlayView();
+ mSession.release();
+ mSession = null;
+ }
+
+ private void setSessionSurface(Surface surface) {
+ if (mSession == null) {
+ return;
+ }
+ mSession.setSurface(surface);
+ }
+
+ private void createSessionOverlayView() {
+ if (mSession == null || !isAttachedToWindow()
+ || mOverlayViewCreated) {
+ return;
+ }
+ mOverlayViewFrame = getViewFrameOnScreen();
+ mSession.createOverlayView(this, mOverlayViewFrame);
+ mOverlayViewCreated = true;
+ }
+
+ private void removeSessionOverlayView() {
+ if (mSession == null || !mOverlayViewCreated) {
+ return;
+ }
+ mSession.removeOverlayView();
+ mOverlayViewCreated = false;
+ mOverlayViewFrame = null;
+ }
+
+ private void relayoutSessionOverlayView() {
+ if (mSession == null || !isAttachedToWindow()
+ || !mOverlayViewCreated) {
+ return;
+ }
+ Rect viewFrame = getViewFrameOnScreen();
+ if (viewFrame.equals(mOverlayViewFrame)) {
+ return;
+ }
+ mSession.relayoutOverlayView(viewFrame);
+ mOverlayViewFrame = viewFrame;
+ }
+
+ private Rect getViewFrameOnScreen() {
+ int[] location = new int[2];
+ getLocationOnScreen(location);
+ return new Rect(location[0], location[1],
+ location[0] + getWidth(), location[1] + getHeight());
+ }
+
+ private class MySessionCreateCallback implements SessionCreateCallback {
+ final SessionCreateCallback mExternalCallback;
+
+ MySessionCreateCallback(SessionCreateCallback externalCallback) {
+ mExternalCallback = externalCallback;
+ }
+
+ @Override
+ public void onSessionCreated(Session session) {
+ if (this != mSessionCreateCallback) {
+ // This callback is obsolete.
+ session.release();
+ return;
+ }
+ mSession = session;
+ if (session != null) {
+ // mSurface may not be ready yet as soon as starting an application.
+ // In the case, we don't send Session.setSurface(null) unnecessarily.
+ // setSessionSurface will be called in surfaceCreated.
+ if (mSurface != null) {
+ setSessionSurface(mSurface);
+ }
+ createSessionOverlayView();
+ }
+ if (mExternalCallback != null) {
+ mExternalCallback.onSessionCreated(session);
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 23123dd..4a2cc1a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -422,7 +422,8 @@
mWindowType = type;
}
- private void updateWindow(boolean force, boolean redrawNeeded) {
+ /** @hide */
+ protected void updateWindow(boolean force, boolean redrawNeeded) {
if (!mHaveFrame) {
return;
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1f9ba46..be316e2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4737,13 +4737,13 @@
private void manageFocusHotspot(boolean focused, View v) {
if (mBackground != null && mBackground.supportsHotspots()) {
final Rect r = new Rect();
- if (v != null) {
+ if (!focused && v != null) {
v.getBoundsOnScreen(r);
final int[] location = new int[2];
getLocationOnScreen(location);
r.offset(-location[0], -location[1]);
} else {
- r.set(mLeft, mTop, mRight, mBottom);
+ r.set(0, 0, mRight - mLeft, mBottom - mTop);
}
final float x = r.exactCenterX();
@@ -4858,16 +4858,13 @@
if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
mPrivateFlags &= ~PFLAG_FOCUSED;
- if (hasFocus()) {
- manageFocusHotspot(false, focused);
- }
-
if (propagate && mParent != null) {
mParent.clearChildFocus(this);
}
onFocusChanged(false, 0, null);
+ manageFocusHotspot(false, focused);
refreshDrawableState();
if (propagate && (!refocus || !rootViewRequestFocus())) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4e4f37b..097dff0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3722,7 +3722,8 @@
if (result == InputMethodManager.DISPATCH_HANDLED) {
return FINISH_HANDLED;
} else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
- return FINISH_NOT_HANDLED;
+ // The IME could not handle it, so skip along to the next InputStage
+ return FORWARD;
} else {
return DEFER; // callback will be invoked later
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4c11fa9..eaedba5 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5580,13 +5580,13 @@
if (end) {
Slog.w(TAG, "New history ends before old history!");
} else if (!out.same(mHistoryReadTmp)) {
- long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
PrintWriter pw = new FastPrintWriter(new LogWriter(android.util.Log.WARN, TAG));
pw.println("Histories differ!");
pw.println("Old history:");
- (new HistoryPrinter()).printNextItem(pw, out, now, false, true);
+ (new HistoryPrinter()).printNextItem(pw, out, 0, false, true);
pw.println("New history:");
- (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now, false, true);
+ (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, 0, false,
+ true);
pw.flush();
}
}
@@ -5664,7 +5664,12 @@
return false;
}
+ final long lastRealtime = out.time;
+ final long lastWalltime = out.currentTime;
readHistoryDelta(mHistoryBuffer, out);
+ if (out.cmd != HistoryItem.CMD_CURRENT_TIME && lastWalltime != 0) {
+ out.currentTime = lastWalltime + (out.time - lastRealtime);
+ }
return true;
}
diff --git a/core/res/res/drawable/edit_text_quantum.xml b/core/res/res/drawable/edit_text_quantum.xml
index d1f9831..c42c7b7 100644
--- a/core/res/res/drawable/edit_text_quantum.xml
+++ b/core/res/res/drawable/edit_text_quantum.xml
@@ -14,29 +14,26 @@
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="false" android:state_enabled="true">
- <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:state_window_focused="false" android:state_enabled="false">
- <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:state_enabled="true" android:state_focused="true">
- <nine-patch android:src="@drawable/textfield_activated_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:state_enabled="true" android:state_activated="true">
- <nine-patch android:src="@drawable/textfield_activated_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:state_enabled="true">
- <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="?attr/colorControlActivated">
<item>
- <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
+ <selector>
+ <item android:state_window_focused="false">
+ <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item android:state_enabled="false">
+ <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item android:state_focused="false" android:state_activated="false">
+ <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item>
+ <nine-patch android:src="@drawable/textfield_activated_qntm_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ </selector>
</item>
-</selector>
+</touch-feedback>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2df5dc1..c610146 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1432,4 +1432,6 @@
<!-- default window inset isRound property -->
<bool name="config_windowIsRound">false</bool>
+ <!-- Package name for default network scorer app; overridden by product overlays. -->
+ <string name="config_defaultNetworkScorerPackageName"></string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 03c617a..26efe36 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1625,6 +1625,7 @@
<java-symbol type="bool" name="config_powerDecoupleAutoSuspendModeFromDisplay" />
<java-symbol type="bool" name="config_powerDecoupleInteractiveModeFromDisplay" />
<java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" />
+ <java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
<java-symbol type="layout" name="resolver_list" />
<java-symbol type="id" name="resolver_list" />
diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml
index 60b6dc1..cbfa84d 100644
--- a/core/tests/bluetoothtests/AndroidManifest.xml
+++ b/core/tests/bluetoothtests/AndroidManifest.xml
@@ -31,5 +31,8 @@
<instrumentation android:name="android.bluetooth.BluetoothTestRunner"
android:targetPackage="com.android.bluetooth.tests"
android:label="Bluetooth Tests" />
+ <instrumentation android:name="android.bluetooth.BluetoothInstrumentation"
+ android:targetPackage="com.android.bluetooth.tests"
+ android:label="Bluetooth Test Utils" />
</manifest>
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
new file mode 100644
index 0000000..34393f9
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 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.bluetooth;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.Bundle;
+
+public class BluetoothInstrumentation extends Instrumentation {
+
+ private BluetoothTestUtils mUtils = null;
+ private BluetoothAdapter mAdapter = null;
+ private Bundle mArgs = null;
+
+ private BluetoothTestUtils getBluetoothTestUtils() {
+ if (mUtils == null) {
+ mUtils = new BluetoothTestUtils(getContext(),
+ BluetoothInstrumentation.class.getSimpleName());
+ }
+ return mUtils;
+ }
+
+ private BluetoothAdapter getBluetoothAdapter() {
+ if (mAdapter == null) {
+ mAdapter = ((BluetoothManager)getContext().getSystemService(
+ Context.BLUETOOTH_SERVICE)).getAdapter();
+ }
+ return mAdapter;
+ }
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ super.onCreate(arguments);
+ mArgs = arguments;
+ start();
+ }
+
+ @Override
+ public void onStart() {
+ String command = mArgs.getString("command");
+ if ("enable".equals(command)) {
+ enable();
+ } else if ("disable".equals(command)) {
+ disable();
+ } else if ("unpairAll".equals(command)) {
+ unpairAll();
+ } else if ("getName".equals(command)) {
+ getName();
+ } else {
+ finish(null);
+ }
+ }
+
+ public void enable() {
+ getBluetoothTestUtils().enable(getBluetoothAdapter());
+ finish(null);
+ }
+
+ public void disable() {
+ getBluetoothTestUtils().disable(getBluetoothAdapter());
+ finish(null);
+ }
+
+ public void unpairAll() {
+ getBluetoothTestUtils().unpairAll(getBluetoothAdapter());
+ finish(null);
+ }
+
+ public void getName() {
+ String name = getBluetoothAdapter().getName();
+ Bundle bundle = new Bundle();
+ bundle.putString("name", name);
+ finish(bundle);
+ }
+
+ public void finish(Bundle result) {
+ if (result == null) {
+ result = new Bundle();
+ }
+ finish(Activity.RESULT_OK, result);
+ }
+}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index 4858be8..8fbd214 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -34,6 +34,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
public class BluetoothTestUtils extends Assert {
@@ -893,6 +894,17 @@
}
/**
+ * Deletes all pairings of remote devices
+ * @param adapter the BT adapter
+ */
+ public void unpairAll(BluetoothAdapter adapter) {
+ Set<BluetoothDevice> devices = adapter.getBondedDevices();
+ for (BluetoothDevice device : devices) {
+ unpair(adapter, device);
+ }
+ }
+
+ /**
* Connects a profile from the local device to a remote device and checks to make sure that the
* profile is connected and that the correct actions were broadcast.
*
diff --git a/core/tests/coretests/src/android/net/NetworkScorerApplicationTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
similarity index 95%
rename from core/tests/coretests/src/android/net/NetworkScorerApplicationTest.java
rename to core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index 6d5ede8..cac6b93 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerApplicationTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -33,7 +33,7 @@
import java.util.Iterator;
-public class NetworkScorerApplicationTest extends InstrumentationTestCase {
+public class NetworkScorerAppManagerTest extends InstrumentationTestCase {
@Mock private Context mMockContext;
@Mock private PackageManager mMockPm;
@@ -64,7 +64,7 @@
setScorers(package1, package2, package3);
Iterator<String> result =
- NetworkScorerApplication.getAllValidScorers(mMockContext).iterator();
+ NetworkScorerAppManager.getAllValidScorers(mMockContext).iterator();
assertTrue(result.hasNext());
assertEquals("package1", result.next());
diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd
index a122443..f169f5f 100644
--- a/docs/html/guide/topics/ui/actionbar.jd
+++ b/docs/html/guide/topics/ui/actionbar.jd
@@ -1145,7 +1145,7 @@
<item>Venus</item>
<item>Earth</item>
</string-array>
-</pre>
+</resources>
</pre>
<p>The {@link android.widget.ArrayAdapter} returned by {@link
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index 03dd841..796da50 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -62,27 +62,31 @@
/** Whether the center is within the parent bounds. */
private boolean mInside;
+
+ /** Whether to pulse this ripple. */
+ boolean mPulse;
/** Enter state. A value in [0...1] or -1 if not set. */
- private float mEnterState = -1;
+ float mEnterState = -1;
/** Exit state. A value in [0...1] or -1 if not set. */
- private float mExitState = -1;
+ float mExitState = -1;
/** Outside state. A value in [0...1] or -1 if not set. */
- private float mOutsideState = -1;
+ float mOutsideState = -1;
/** Pulse state. A value in [0...1] or -1 if not set. */
- private float mPulseState = -1;
+ float mPulseState = -1;
/**
* Creates a new ripple with the specified parent bounds, padding, initial
* position, and screen density.
*/
- public Ripple(Rect bounds, Rect padding, float x, float y, float density) {
+ public Ripple(Rect bounds, Rect padding, float x, float y, float density, boolean pulse) {
mBounds = bounds;
mPadding = padding;
mInside = mBounds.contains((int) x, (int) y);
+ mPulse = pulse;
mX = x;
mY = y;
@@ -115,6 +119,16 @@
}
}
+ public void onBoundsChanged() {
+ final boolean inside = mBounds.contains((int) mX, (int) mY);
+ if (mInside != inside) {
+ if (mAnimator != null) {
+ mAnimator.outside();
+ }
+ mInside = inside;
+ }
+ }
+
public RippleAnimator animate() {
if (mAnimator == null) {
mAnimator = new RippleAnimator(this);
@@ -308,9 +322,11 @@
MathUtils.constrain((currentTime - mOutsideTime) / (float) OUTSIDE_DURATION, 0, 1));
// Pulse is a little more complicated.
- final long pulseTime = (currentTime - mEnterTime - ENTER_DURATION - PULSE_DELAY);
- mTarget.mPulseState = pulseTime < 0 ? -1
- : (pulseTime % (PULSE_INTERVAL + PULSE_DURATION)) / (float) PULSE_DURATION;
+ if (mTarget.mPulse) {
+ final long pulseTime = (currentTime - mEnterTime - ENTER_DURATION - PULSE_DELAY);
+ mTarget.mPulseState = pulseTime < 0 ? -1
+ : (pulseTime % (PULSE_INTERVAL + PULSE_DURATION)) / (float) PULSE_DURATION;
+ }
}
}
}
diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
index cf08031..813d755c 100644
--- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
+++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
@@ -112,6 +112,16 @@
}
@Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+
+ final int N = mActiveRipplesCount;
+ for (int i = 0; i < N; i++) {
+ mActiveRipples[i].onBoundsChanged();
+ }
+ }
+
+ @Override
public boolean setVisible(boolean visible, boolean restart) {
if (!visible) {
if (mTouchedRipples != null) {
@@ -291,7 +301,9 @@
y = bounds.exactCenterY();
}
- final Ripple newRipple = new Ripple(bounds, padding, x, y, mDensity);
+ // TODO: Clean this up in the API.
+ final boolean pulse = (id != R.attr.state_focused);
+ final Ripple newRipple = new Ripple(bounds, padding, x, y, mDensity, pulse);
newRipple.animate().enter();
mActiveRipples[mActiveRipplesCount++] = newRipple;
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 7c45682..4fbd2a4 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -297,8 +297,6 @@
}
JDrm::~JDrm() {
- mDrm.clear();
-
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->DeleteWeakGlobalRef(mObject);
@@ -363,6 +361,13 @@
}
}
+void JDrm::disconnect() {
+ if (mDrm != NULL) {
+ mDrm->destroyPlugin();
+ mDrm.clear();
+ }
+}
+
// static
bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) {
@@ -527,6 +532,7 @@
sp<JDrm> drm = setDrm(env, thiz, NULL);
if (drm != NULL) {
drm->setListener(NULL);
+ drm->disconnect();
}
}
diff --git a/media/jni/android_media_MediaDrm.h b/media/jni/android_media_MediaDrm.h
index 620ad28..b7b8e5d 100644
--- a/media/jni/android_media_MediaDrm.h
+++ b/media/jni/android_media_MediaDrm.h
@@ -47,6 +47,8 @@
void notify(DrmPlugin::EventType, int extra, const Parcel *obj);
status_t setListener(const sp<DrmListener>& listener);
+ void disconnect();
+
protected:
virtual ~JDrm();
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index a9322b9..1406f6b 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -878,7 +878,7 @@
protected int[] mConfigSpec;
private int[] filterConfigSpec(int[] configSpec) {
- if (mEGLContextClientVersion != 2) {
+ if (mEGLContextClientVersion != 2 && mEGLContextClientVersion != 3) {
return configSpec;
}
/* We know none of the subclasses define EGL_RENDERABLE_TYPE.
@@ -888,7 +888,11 @@
int[] newConfigSpec = new int[len + 2];
System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
- newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
+ if (mEGLContextClientVersion == 2) {
+ newConfigSpec[len] = EGL14.EGL_OPENGL_ES2_BIT; /* EGL_OPENGL_ES2_BIT */
+ } else {
+ newConfigSpec[len] = EGLExt.EGL_OPENGL_ES3_BIT_KHR; /* EGL_OPENGL_ES3_BIT_KHR */
+ }
newConfigSpec[len+1] = EGL10.EGL_NONE;
return newConfigSpec;
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index d371d70..327df8d 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -273,7 +273,8 @@
<service
android:name=".keyguard.KeyguardService"
- android:exported="true" />
+ android:exported="true"
+ android:enabled="@bool/config_enableKeyguardService" />
<activity android:name=".Somnambulator"
android:label="@string/start_dreams"
diff --git a/packages/SystemUI/res/drawable-hdpi/search_bg_transparent.9.png b/packages/SystemUI/res/drawable-hdpi/search_bg_transparent.9.png
new file mode 100644
index 0000000..85db9c8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/search_bg_transparent.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/search_bg_transparent.9.png b/packages/SystemUI/res/drawable-mdpi/search_bg_transparent.9.png
new file mode 100644
index 0000000..c4941a6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/search_bg_transparent.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/search_bg_transparent.9.png b/packages/SystemUI/res/drawable-xhdpi/search_bg_transparent.9.png
new file mode 100644
index 0000000..4618f40
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/search_bg_transparent.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_task_shadow.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_task_shadow.9.png
new file mode 100644
index 0000000..36e7e45
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/recents_task_shadow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/search_bg_transparent.9.png b/packages/SystemUI/res/drawable-xxhdpi/search_bg_transparent.9.png
new file mode 100644
index 0000000..c0bf31d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/search_bg_transparent.9.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/recents_search_bar.xml b/packages/SystemUI/res/layout/recents_search_bar.xml
new file mode 100644
index 0000000..915283e
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_search_bar.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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="match_parent"
+ android:background="@drawable/search_bg_transparent">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/recents_search_bar_label"
+ android:textColor="#99ffffff"
+ android:textSize="18sp"
+ android:textAllCaps="true" />
+</FrameLayout>
+
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 7f64032..4442bca 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -63,13 +63,6 @@
android:maxLines="2"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
- <ImageView
- android:id="@+id/activity_icon"
- android:layout_width="@dimen/recents_task_view_activity_icon_size"
- android:layout_height="@dimen/recents_task_view_activity_icon_size"
- android:layout_gravity="center_vertical|end"
- android:padding="12dp"
- android:visibility="invisible" />
</com.android.systemui.recents.views.TaskBarView>
</com.android.systemui.recents.views.TaskView>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 85a9388..722ca15 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -72,7 +72,7 @@
<!-- decay duration (from size_max -> size), in ms -->
<integer name="navigation_bar_deadzone_hold">333</integer>
<integer name="navigation_bar_deadzone_decay">333</integer>
-
+
<bool name="config_dead_zone_flash">false</bool>
<!-- Min alpha % that recent items will fade to while being dismissed -->
@@ -122,6 +122,9 @@
<!-- The minimum alpha for the dim applied to cards that go deeper into the stack. -->
<integer name="recents_max_task_stack_view_dim">96</integer>
+ <!-- Whether to enable KeyguardService or not -->
+ <bool name="config_enableKeyguardService">true</bool>
+
<!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
card. -->
<integer name="keyguard_max_notification_count">4</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 25ae38c..3047f15 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -236,6 +236,12 @@
<!-- The amount of space a user has to scroll to dismiss any info panes. -->
<dimen name="recents_task_stack_scroll_dismiss_info_pane_distance">50dp</dimen>
+ <!-- The height of the search bar space. -->
+ <dimen name="recents_search_bar_space_height">40dp</dimen>
+
+ <!-- The search bar edge margins. -->
+ <dimen name="recents_search_bar_space_edge_margins">12dp</dimen>
+
<!-- Used to calculate the translation animation duration, the expected amount of movement
in dps over one second of time. -->
<dimen name="recents_animation_movement_in_dps_per_second">800dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 73e5e19..72d14fe 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -511,6 +511,8 @@
<string name="recents_empty_message">RECENTS</string>
<!-- Recents: The info panel app info button string. [CHAR LIMIT=NONE] -->
<string name="recents_app_info_button_label">Application Info</string>
+ <!-- Recents: Temporary string for the button in the recents search bar. [CHAR LIMIT=NONE] -->
+ <string name="recents_search_bar_label">search</string>
<!-- Glyph to be overlaid atop the battery when the level is extremely low. Do not translate. -->
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 4fb90cb..f2e322d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -44,6 +44,7 @@
import android.view.WindowManager;
import com.android.systemui.R;
+import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -240,12 +241,10 @@
}
/** Returns whether there is are multiple recents tasks */
- boolean hasMultipleRecentsTask() {
+ boolean hasMultipleRecentsTask(List<ActivityManager.RecentTaskInfo> tasks) {
// NOTE: Currently there's no method to get the number of non-home tasks, so we have to
// compute this ourselves
SystemServicesProxy ssp = mSystemServicesProxy;
- List<ActivityManager.RecentTaskInfo> tasks = ssp.getRecentTasks(4,
- UserHandle.CURRENT.getIdentifier());
Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
while (iter.hasNext()) {
ActivityManager.RecentTaskInfo t = iter.next();
@@ -259,6 +258,17 @@
return (tasks.size() > 1);
}
+ /** Returns whether the base intent of the top task stack was launched with the flag
+ * Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS. */
+ boolean isTopTaskExcludeFromRecents(List<ActivityManager.RecentTaskInfo> tasks) {
+ if (tasks.size() > 0) {
+ ActivityManager.RecentTaskInfo t = tasks.get(0);
+ Console.log(t.baseIntent.toString());
+ return (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
+ }
+ return false;
+ }
+
/** Converts from the device rotation to the degree */
float getDegreesForRotation(int value) {
switch (value) {
@@ -334,10 +344,18 @@
isTopTaskHome = ssp.isInHomeStack(topTask.id);
}
- // Otherwise, Recents is not the front-most activity and we should animate into it
- boolean hasMultipleTasks = hasMultipleRecentsTask();
+ // Otherwise, Recents is not the front-most activity and we should animate into it. If
+ // the activity at the root of the top task stack is excluded from recents, or if that
+ // task stack is in the home stack, then we just do a simple transition. Otherwise, we
+ // animate to the rects defined by the Recents service, which can differ depending on the
+ // number of items in the list.
+ List<ActivityManager.RecentTaskInfo> recentTasks =
+ ssp.getRecentTasks(4, UserHandle.CURRENT.getIdentifier());
+ boolean hasMultipleTasks = hasMultipleRecentsTask(recentTasks);
+ boolean isTaskExcludedFromRecents = isTopTaskExcludeFromRecents(recentTasks);
Rect taskRect = hasMultipleTasks ? mMultipleCountFirstTaskRect : mSingleCountFirstTaskRect;
- if (!isTopTaskHome && taskRect != null && taskRect.width() > 0 && taskRect.height() > 0) {
+ if (!isTopTaskHome && !isTaskExcludedFromRecents &&
+ (taskRect != null) && (taskRect.width() > 0) && (taskRect.height() > 0)) {
// Loading from thumbnail
Bitmap thumbnail;
Bitmap firstThumbnail = loadFirstTaskThumbnail();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 64770a4..72d9a52 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -29,6 +29,7 @@
public static final boolean EnableTaskFiltering = true;
public static final boolean EnableTaskStackClipping = false;
public static final boolean EnableInfoPane = true;
+ public static final boolean EnableSearchButton = false;
// This disables the bitmap and icon caches
public static final boolean DisableBackgroundCache = false;
@@ -84,6 +85,9 @@
public static final int TaskStackOverscrollRange = 150;
public static final int FilterStartDelay = 25;
+ // The amount to inverse scale the movement if we are overscrolling
+ public static final float TouchOverscrollScaleFactor = 3f;
+
// The padding will be applied to the smallest dimension, and then applied to all sides
public static final float StackPaddingPct = 0.15f;
// The overlap height relative to the task height
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 5e5b841..d54df13 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -44,6 +44,8 @@
public int taskStackMaxDim;
public int taskViewInfoPaneAnimDuration;
public int taskViewRoundedCornerRadiusPx;
+ public int searchBarSpaceHeightPx;
+ public int searchBarSpaceEdgeMarginsPx;
public boolean launchedWithThumbnailAnimation;
@@ -92,6 +94,9 @@
res.getInteger(R.integer.recents_animate_task_view_info_pane_duration);
taskViewRoundedCornerRadiusPx =
res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
+ searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
+ searchBarSpaceEdgeMarginsPx =
+ res.getDimensionPixelSize(R.dimen.recents_search_bar_space_edge_margins);
}
/** Updates the system insets */
@@ -99,6 +104,26 @@
systemInsets.set(insets);
}
+ /** Returns the search bar bounds in the specified orientation */
+ public void getSearchBarBounds(int width, int height,
+ Rect searchBarSpaceBounds, Rect searchBarBounds) {
+ // Return empty rects if search is not enabled
+ if (!Constants.DebugFlags.App.EnableSearchButton) {
+ searchBarSpaceBounds.set(0, 0, 0, 0);
+ searchBarBounds.set(0, 0, 0, 0);
+ return;
+ }
+
+ // Calculate the search bar bounds, and account for the system insets
+ int edgeMarginPx = searchBarSpaceEdgeMarginsPx;
+ int availableWidth = width - systemInsets.left - systemInsets.right;
+ searchBarSpaceBounds.set(0, 0, availableWidth, 2 * edgeMarginPx + searchBarSpaceHeightPx);
+
+ // Inset from the search bar space to get the search bar bounds
+ searchBarBounds.set(searchBarSpaceBounds);
+ searchBarBounds.inset(edgeMarginPx, edgeMarginPx);
+ }
+
/** Converts from DPs to PXs */
public int pxFromDp(float size) {
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
index 06ca9e2..36b761e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
@@ -66,22 +66,33 @@
Bundle replyData = new Bundle();
TaskViewTransform transform;
+ // Get the search bar bounds so that we can account for its height in the children
+ Rect searchBarSpaceBounds = new Rect();
+ Rect searchBarBounds = new Rect();
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
+ config.getSearchBarBounds(windowRect.width(), windowRect.height(),
+ searchBarSpaceBounds, searchBarBounds);
+
// Calculate the target task rect for when there is one task
// NOTE: Since the nav bar height is already accounted for in the windowRect, don't
// pass in a bottom inset
stack.addTask(new Task());
- tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top, 0);
+ tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top -
+ systemInsets.bottom - searchBarSpaceBounds.height(), 0);
tsv.boundScroll();
transform = tsv.getStackTransform(0, tsv.getStackScroll());
+ transform.rect.offset(0, searchBarSpaceBounds.height());
replyData.putParcelable(AlternateRecentsComponent.KEY_SINGLE_TASK_STACK_RECT,
new Rect(transform.rect));
// Also calculate the target task rect when there are multiple tasks
stack.addTask(new Task());
- tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top, 0);
+ tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top -
+ systemInsets.bottom - searchBarSpaceBounds.height(), 0);
tsv.setStackScrollRaw(Integer.MAX_VALUE);
tsv.boundScroll();
transform = tsv.getStackTransform(1, tsv.getStackScroll());
+ transform.rect.offset(0, searchBarSpaceBounds.height());
replyData.putParcelable(AlternateRecentsComponent.KEY_MULTIPLE_TASK_STACK_RECT,
new Rect(transform.rect));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index fd0f6d1..da265e1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -415,7 +415,10 @@
ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent(), t.userId);
String activityLabel = (t.activityLabel == null ? ssp.getActivityLabel(info) :
t.activityLabel.toString());
- Bitmap activityIcon = t.activityIcon;
+ BitmapDrawable activityIcon = null;
+ if (t.activityIcon != null) {
+ activityIcon = new BitmapDrawable(res, t.activityIcon);
+ }
boolean isForemostTask = (i == (taskCount - 1));
// Create a new task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
index 7f0d9ee..33ac0a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
@@ -19,6 +19,8 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AppGlobals;
+import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -26,11 +28,15 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.Log;
import java.util.ArrayList;
import java.util.List;
@@ -45,6 +51,7 @@
PackageManager mPm;
IPackageManager mIpm;
UserManager mUm;
+ SearchManager mSm;
String mPackage;
Bitmap mDummyIcon;
@@ -55,6 +62,7 @@
mPm = context.getPackageManager();
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
mIpm = AppGlobals.getPackageManager();
+ mSm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
mPackage = context.getPackageName();
if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
@@ -199,4 +207,28 @@
}
return icon;
}
+
+
+ /**
+ * Composes an intent to launch the global search activity.
+ */
+ public Intent getGlobalSearchIntent(Rect sourceBounds) {
+ if (mSm == null) return null;
+
+ // Try and get the global search activity
+ ComponentName globalSearchActivity = mSm.getGlobalSearchActivity();
+ if (globalSearchActivity == null) return null;
+
+ // Bundle the source of the search
+ Bundle appSearchData = new Bundle();
+ appSearchData.putString("source", mPackage);
+
+ // Compose the intent and Start the search activity
+ Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setComponent(globalSearchActivity);
+ intent.putExtra(SearchManager.APP_DATA, appSearchData);
+ intent.setSourceBounds(sourceBounds);
+ return intent;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index ff062f6c..1566a49 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -18,6 +18,7 @@
import android.content.Intent;
import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
@@ -69,8 +70,8 @@
public TaskKey key;
public Drawable applicationIcon;
+ public Drawable activityIcon;
public String activityLabel;
- public Bitmap activityIcon;
public Bitmap thumbnail;
public boolean isActive;
public int userId;
@@ -82,7 +83,7 @@
}
public Task(int id, boolean isActive, Intent intent, String activityTitle,
- Bitmap activityIcon, int userId) {
+ BitmapDrawable activityIcon, int userId) {
this.key = new TaskKey(id, intent, userId);
this.activityLabel = activityTitle;
this.activityIcon = activityIcon;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index b054a22..a04cd3e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -27,8 +27,11 @@
import android.net.Uri;
import android.os.UserHandle;
import android.provider.Settings;
+import android.view.Gravity;
+import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
+import android.widget.TextView;
import com.android.systemui.recents.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
@@ -36,6 +39,7 @@
import com.android.systemui.recents.model.SpaceNode;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.R;
import java.util.ArrayList;
@@ -53,11 +57,16 @@
// The space partitioning root of this container
SpaceNode mBSP;
+ // Search bar view
+ View mSearchBar;
// Recents view callbacks
RecentsViewCallbacks mCb;
+ LayoutInflater mInflater;
+
public RecentsView(Context context) {
super(context);
+ mInflater = LayoutInflater.from(context);
setWillNotDraw(false);
}
@@ -71,12 +80,22 @@
mBSP = n;
// Create and add all the stacks for this partition of space.
+ boolean hasTasks = false;
removeAllViews();
ArrayList<TaskStack> stacks = mBSP.getStacks();
for (TaskStack stack : stacks) {
TaskStackView stackView = new TaskStackView(getContext(), stack);
stackView.setCallbacks(this);
addView(stackView);
+ hasTasks |= (stack.getTaskCount() > 0);
+ }
+
+ // Create the search bar (and hide it if we have no recent tasks)
+ if (Constants.DebugFlags.App.EnableSearchButton) {
+ createSearchBar();
+ if (!hasTasks) {
+ mSearchBar.setVisibility(View.GONE);
+ }
}
}
@@ -85,29 +104,45 @@
// Get the first stack view
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
- TaskStackView stackView = (TaskStackView) getChildAt(i);
- TaskStack stack = stackView.mStack;
- ArrayList<Task> tasks = stack.getTasks();
+ View child = getChildAt(i);
+ if (child instanceof TaskStackView) {
+ TaskStackView stackView = (TaskStackView) child;
+ TaskStack stack = stackView.mStack;
+ ArrayList<Task> tasks = stack.getTasks();
- // Get the first task in the stack
- if (!tasks.isEmpty()) {
- Task task = tasks.get(tasks.size() - 1);
- TaskView tv = null;
+ // Get the first task in the stack
+ if (!tasks.isEmpty()) {
+ Task task = tasks.get(tasks.size() - 1);
+ TaskView tv = null;
- // Try and use the first child task view as the source of the launch animation
- if (stackView.getChildCount() > 0) {
- TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1);
- if (stv.getTask() == task) {
- tv = stv;
+ // Try and use the first child task view as the source of the launch animation
+ if (stackView.getChildCount() > 0) {
+ TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1);
+ if (stv.getTask() == task) {
+ tv = stv;
+ }
}
+ onTaskLaunched(stackView, tv, stack, task);
+ return true;
}
- onTaskLaunched(stackView, tv, stack, task);
- return true;
}
}
return false;
}
+ /** Creates and adds the search bar */
+ void createSearchBar() {
+ // Create a temporary search bar
+ mSearchBar = mInflater.inflate(R.layout.recents_search_bar, this, false);
+ mSearchBar.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onSearchTriggered();
+ }
+ });
+ addView(mSearchBar);
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
@@ -120,16 +155,26 @@
Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
Constants.DebugFlags.App.TimeRecentsStartupKey, "RecentsView.onMeasure");
- // We measure our stack views sans the status bar. It will handle the nav bar itself.
+ // Get the search bar bounds so that we can account for its height in the children
+ Rect searchBarSpaceBounds = new Rect();
+ Rect searchBarBounds = new Rect();
RecentsConfiguration config = RecentsConfiguration.getInstance();
+ config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
+ searchBarSpaceBounds, searchBarBounds);
+ if (mSearchBar != null) {
+ mSearchBar.measure(MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), widthMode),
+ MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), heightMode));
+ }
+
+ // We measure our stack views sans the status bar. It will handle the nav bar itself.
int childWidth = width - config.systemInsets.right;
- int childHeight = height - config.systemInsets.top;
+ int childHeight = height - config.systemInsets.top - searchBarSpaceBounds.height();
// Measure each child
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
+ View child = getChildAt(i);
+ if (child instanceof TaskStackView && child.getVisibility() != GONE) {
child.measure(MeasureSpec.makeMeasureSpec(childWidth, widthMode),
MeasureSpec.makeMeasureSpec(childHeight, heightMode));
}
@@ -145,18 +190,30 @@
Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
Constants.DebugFlags.App.TimeRecentsStartupKey, "RecentsView.onLayout");
- // We offset our stack views by the status bar height. It will handle the nav bar itself.
+ // Get the search bar bounds so that we can account for its height in the children
+ Rect searchBarSpaceBounds = new Rect();
+ Rect searchBarBounds = new Rect();
RecentsConfiguration config = RecentsConfiguration.getInstance();
- top += config.systemInsets.top;
+ config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
+ searchBarSpaceBounds, searchBarBounds);
+ if (mSearchBar != null) {
+ mSearchBar.layout(config.systemInsets.left + searchBarSpaceBounds.left,
+ config.systemInsets.top + searchBarSpaceBounds.top,
+ config.systemInsets.left + mSearchBar.getMeasuredWidth(),
+ config.systemInsets.top + mSearchBar.getMeasuredHeight());
+ }
+
+ // We offset our stack views by the status bar height. It will handle the nav bar itself.
+ top += config.systemInsets.top + searchBarSpaceBounds.height();
// Layout each child
// XXX: Based on the space node for that task view
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- final int width = child.getMeasuredWidth();
- final int height = child.getMeasuredHeight();
+ View child = getChildAt(i);
+ if (child instanceof TaskStackView && child.getVisibility() != GONE) {
+ int width = child.getMeasuredWidth();
+ int height = child.getMeasuredHeight();
child.layout(left, top, left + width, top + height);
}
}
@@ -188,9 +245,12 @@
// Get the first stack view
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
- TaskStackView stackView = (TaskStackView) getChildAt(i);
- if (stackView.closeOpenInfoPanes()) {
- return true;
+ View child = getChildAt(i);
+ if (child instanceof TaskStackView) {
+ TaskStackView stackView = (TaskStackView) child;
+ if (stackView.closeOpenInfoPanes()) {
+ return true;
+ }
}
}
}
@@ -266,7 +326,6 @@
b, offsetX, offsetY);
}
-
if (task.isActive) {
// Bring an active task to the foreground
RecentsTaskLoader.getInstance().getSystemServicesProxy()
@@ -315,4 +374,24 @@
TaskStackBuilder.create(getContext())
.addNextIntentWithParentStack(intent).startActivities();
}
+
+ public void onSearchTriggered() {
+ // Get the search bar source bounds
+ Rect searchBarSpaceBounds = new Rect();
+ Rect searchBarBounds = new Rect();
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
+ config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
+ searchBarSpaceBounds, searchBarBounds);
+
+ // Get the search intent and start it
+ Intent searchIntent = RecentsTaskLoader.getInstance().getSystemServicesProxy()
+ .getGlobalSearchIntent(searchBarBounds);
+ if (searchIntent != null) {
+ try {
+ getContext().startActivity(searchIntent);
+ } catch (ActivityNotFoundException anfe) {
+ Console.logError(getContext(), "Could not start Search activity");
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index c9a6d67..124f11e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -31,7 +31,6 @@
Task mTask;
ImageView mApplicationIcon;
- ImageView mActivityIcon;
TextView mActivityDescription;
public TaskBarView(Context context) {
@@ -54,23 +53,22 @@
protected void onFinishInflate() {
// Initialize the icon and description views
mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
- mActivityIcon = (ImageView) findViewById(R.id.activity_icon);
mActivityDescription = (TextView) findViewById(R.id.activity_description);
}
/** Binds the bar view to the task */
void rebindToTask(Task t, boolean animate) {
mTask = t;
- if (t.applicationIcon != null) {
+ // If an activity icon is defined, then we use that as the primary icon to show in the bar,
+ // otherwise, we fall back to the application icon
+ if (t.activityIcon != null) {
+ mApplicationIcon.setImageDrawable(t.activityIcon);
+ } else if (t.applicationIcon != null) {
mApplicationIcon.setImageDrawable(t.applicationIcon);
- mActivityDescription.setText(t.activityLabel);
- if (t.activityIcon != null) {
- mActivityIcon.setImageBitmap(t.activityIcon);
- mActivityIcon.setVisibility(View.VISIBLE);
- }
- if (animate) {
- // XXX: Investigate how expensive it will be to create a second bitmap and crossfade
- }
+ }
+ mActivityDescription.setText(t.activityLabel);
+ if (animate) {
+ // XXX: Investigate how expensive it will be to create a second bitmap and crossfade
}
}
@@ -78,8 +76,6 @@
void unbindFromTask() {
mTask = null;
mApplicationIcon.setImageDrawable(null);
- mActivityIcon.setImageBitmap(null);
- mActivityIcon.setVisibility(View.INVISIBLE);
mActivityDescription.setText("");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
index 233e38c..a81d01c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
@@ -70,10 +70,8 @@
/** Updates the positions of each of the items to fit in the rect specified */
void updateContents(Rect visibleRect) {
// Offset the app info button
- LayoutParams lp = (LayoutParams) mAppInfoButton.getLayoutParams();
- lp.topMargin = visibleRect.top +
- (visibleRect.height() - mAppInfoButton.getMeasuredHeight()) / 2;
- requestLayout();
+ mAppInfoButton.setTranslationY(visibleRect.top +
+ (visibleRect.height() - mAppInfoButton.getMeasuredHeight()) / 2);
}
/** Sets the circular clip radius on this panel */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index ee92b16..a77e61d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -395,9 +395,12 @@
return false;
}
- /** Returns whether the current scroll is out of bounds */
+ /** Returns whether the specified scroll is out of bounds */
+ boolean isScrollOutOfBounds(int scroll) {
+ return (scroll < mMinScroll) || (scroll > mMaxScroll);
+ }
boolean isScrollOutOfBounds() {
- return (getStackScroll() < 0) || (getStackScroll() > mMaxScroll);
+ return isScrollOutOfBounds(getStackScroll());
}
/** Updates the min and max virtual scroll bounds */
@@ -556,7 +559,14 @@
int smallestDimension = Math.min(width, height);
int padding = (int) (Constants.Values.TaskStackView.StackPaddingPct * smallestDimension / 2f);
- mStackRect.inset(padding, padding);
+ if (Constants.DebugFlags.App.EnableSearchButton) {
+ // Don't need to pad the top since we have some padding on the search bar already
+ mStackRect.left += padding;
+ mStackRect.right -= padding;
+ mStackRect.bottom -= padding;
+ } else {
+ mStackRect.inset(padding, padding);
+ }
mStackRectSansPeek.set(mStackRect);
mStackRectSansPeek.top += Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height();
@@ -1275,7 +1285,12 @@
}
}
if (mIsScrolling) {
- mSv.setStackScroll(mSv.getStackScroll() + deltaY);
+ int curStackScroll = mSv.getStackScroll();
+ if (mSv.isScrollOutOfBounds(curStackScroll + deltaY)) {
+ // Scale the touch if we are overscrolling
+ deltaY /= Constants.Values.TaskStackView.TouchOverscrollScaleFactor;
+ }
+ mSv.setStackScroll(curStackScroll + deltaY);
if (mSv.isScrollOutOfBounds()) {
mVelocityTracker.clear();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index f2054a2..cf31b44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -83,7 +83,7 @@
}
public void onScreenTurnedOff() {
- if (mKeyguardView != null && mRoot.getVisibility() == View.VISIBLE) {
+ if (mKeyguardView != null && mRoot != null && mRoot.getVisibility() == View.VISIBLE) {
mKeyguardView.onPause();
}
}
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index bce85ce..7249985 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -106,22 +106,33 @@
.append("Kernel: ")
.append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"))
.append("\n").toString();
+ final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
String recovery = RecoverySystem.handleAftermath();
if (recovery != null && db != null) {
db.addText("SYSTEM_RECOVERY_LOG", headers + recovery);
}
+ String lastKmsgFooter = "";
+ if (bootReason != null) {
+ lastKmsgFooter = new StringBuilder(512)
+ .append("\n")
+ .append("Boot info:\n")
+ .append("Last boot reason: ").append(bootReason).append("\n")
+ .toString();
+ }
+
if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) {
String now = Long.toString(System.currentTimeMillis());
SystemProperties.set("ro.runtime.firstboot", now);
if (db != null) db.addText("SYSTEM_BOOT", headers);
// Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile())
- addFileToDropBox(db, prefs, headers, "/proc/last_kmsg",
- -LOG_SIZE, "SYSTEM_LAST_KMSG");
- addFileToDropBox(db, prefs, headers, "/sys/fs/pstore/console-ramoops",
- -LOG_SIZE, "SYSTEM_LAST_KMSG");
+ addFileWithFootersToDropBox(db, prefs, headers, lastKmsgFooter,
+ "/proc/last_kmsg", -LOG_SIZE, "SYSTEM_LAST_KMSG");
+ addFileWithFootersToDropBox(db, prefs, headers, lastKmsgFooter,
+ "/sys/fs/pstore/console-ramoops", -LOG_SIZE,
+ "SYSTEM_LAST_KMSG");
addFileToDropBox(db, prefs, headers, "/cache/recovery/log",
-LOG_SIZE, "SYSTEM_RECOVERY_LOG");
addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console",
@@ -161,6 +172,14 @@
private static void addFileToDropBox(
DropBoxManager db, SharedPreferences prefs,
String headers, String filename, int maxSize, String tag) throws IOException {
+ addFileWithFootersToDropBox(db, prefs, headers, "", filename, maxSize,
+ tag);
+ }
+
+ private static void addFileWithFootersToDropBox(
+ DropBoxManager db, SharedPreferences prefs,
+ String headers, String footers, String filename, int maxSize,
+ String tag) throws IOException {
if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled
File file = new File(filename);
@@ -176,7 +195,7 @@
}
Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
- db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"));
+ db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n") + footers);
}
private static void addAuditErrorsToDropBox(DropBoxManager db, SharedPreferences prefs,
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 9629bd1..705862a 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -153,7 +153,7 @@
*/
private NativeDaemonConnector mConnector;
- private final Handler mMainHandler = new Handler();
+ private final Handler mFgHandler;
private IBatteryStats mBatteryStats;
@@ -203,6 +203,9 @@
private NetworkManagementService(Context context, String socket) {
mContext = context;
+ // make sure this is on the same looper as our NativeDaemonConnector for sync purposes
+ mFgHandler = new Handler(FgThread.get().getLooper());
+
if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
return;
}
@@ -271,14 +274,17 @@
*/
private void notifyInterfaceStatusChanged(String iface, boolean up) {
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
/**
@@ -287,14 +293,17 @@
*/
private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
/**
@@ -302,14 +311,17 @@
*/
private void notifyInterfaceAdded(String iface) {
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceAdded(iface);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceAdded(iface);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
/**
@@ -322,14 +334,17 @@
mActiveQuotas.remove(iface);
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceRemoved(iface);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceRemoved(iface);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
/**
@@ -337,14 +352,17 @@
*/
private void notifyLimitReached(String limitName, String iface) {
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).limitReached(limitName, iface);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).limitReached(limitName, iface);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
/**
@@ -357,15 +375,18 @@
}
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(
- Integer.toString(type), active, tsNanos);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(
+ Integer.toString(type), active, tsNanos);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
boolean report = false;
synchronized (mIdleTimerLock) {
@@ -456,14 +477,17 @@
*/
private void notifyAddressUpdated(String iface, LinkAddress address) {
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).addressUpdated(iface, address);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).addressUpdated(iface, address);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
/**
@@ -471,14 +495,17 @@
*/
private void notifyAddressRemoved(String iface, LinkAddress address) {
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).addressRemoved(iface, address);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).addressRemoved(iface, address);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
/**
@@ -486,14 +513,18 @@
*/
private void notifyInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) {
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceDnsServerInfo(iface, lifetime, addresses);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceDnsServerInfo(iface, lifetime,
+ addresses);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
//
@@ -509,7 +540,7 @@
mConnectedSignal.countDown();
mConnectedSignal = null;
} else {
- mMainHandler.post(new Runnable() {
+ mFgHandler.post(new Runnable() {
@Override
public void run() {
prepareNativeDaemon();
@@ -1270,7 +1301,7 @@
if (ConnectivityManager.isNetworkTypeMobile(type)) {
mNetworkActive = false;
}
- mMainHandler.post(new Runnable() {
+ mFgHandler.post(new Runnable() {
@Override public void run() {
notifyInterfaceClassActivity(type, true, SystemClock.elapsedRealtimeNanos());
}
@@ -1297,7 +1328,7 @@
throw e.rethrowAsParcelableException();
}
mActiveIdleTimers.remove(iface);
- mMainHandler.post(new Runnable() {
+ mFgHandler.post(new Runnable() {
@Override public void run() {
notifyInterfaceClassActivity(params.type, false,
SystemClock.elapsedRealtimeNanos());
@@ -1880,14 +1911,17 @@
private void reportNetworkActive() {
final int length = mNetworkActivityListeners.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mNetworkActivityListeners.finishBroadcast();
}
- mNetworkActivityListeners.finishBroadcast();
}
/** {@inheritDoc} */
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
new file mode 100644
index 0000000..8a30e50
--- /dev/null
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.Manifest.permission;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.INetworkScoreService;
+import android.net.NetworkKey;
+import android.net.NetworkScorerAppManager;
+import android.net.RssiCurve;
+import android.net.ScoredNetwork;
+import android.text.TextUtils;
+
+import com.android.internal.R;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Backing service for {@link android.net.NetworkScoreManager}.
+ * @hide
+ */
+public class NetworkScoreService extends INetworkScoreService.Stub {
+ private static final String TAG = "NetworkScoreService";
+
+ /** SharedPreference bit set to true after the service is first initialized. */
+ private static final String PREF_SCORING_PROVISIONED = "is_provisioned";
+
+ private final Context mContext;
+
+ // TODO: Delete this temporary class once we have a real place for scores.
+ private final Map<NetworkKey, RssiCurve> mScoredNetworks;
+
+ public NetworkScoreService(Context context) {
+ mContext = context;
+ mScoredNetworks = new HashMap<>();
+ }
+
+ /** Called when the system is ready to run third-party code but before it actually does so. */
+ void systemReady() {
+ SharedPreferences prefs = mContext.getSharedPreferences(TAG, Context.MODE_PRIVATE);
+ if (!prefs.getBoolean(PREF_SCORING_PROVISIONED, false)) {
+ // On first run, we try to initialize the scorer to the one configured at build time.
+ // This will be a no-op if the scorer isn't actually valid.
+ String defaultPackage = mContext.getResources().getString(
+ R.string.config_defaultNetworkScorerPackageName);
+ if (!TextUtils.isEmpty(defaultPackage)) {
+ NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
+ }
+ prefs.edit().putBoolean(PREF_SCORING_PROVISIONED, true).apply();
+ }
+ }
+
+ @Override
+ public boolean updateScores(ScoredNetwork[] networks) {
+ if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) {
+ throw new SecurityException("Caller with UID " + getCallingUid() +
+ " is not the active scorer.");
+ }
+
+ // TODO: Propagate these scores down to the network subsystem layer instead of just holding
+ // them in memory.
+ for (ScoredNetwork network : networks) {
+ mScoredNetworks.put(network.networkKey, network.rssiCurve);
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean clearScores() {
+ // Only the active scorer or the system (who can broadcast BROADCAST_SCORE_NETWORKS) should
+ // be allowed to flush all scores.
+ if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
+ mContext.checkCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS) ==
+ PackageManager.PERMISSION_GRANTED) {
+ clearInternal();
+ return true;
+ } else {
+ throw new SecurityException(
+ "Caller is neither the active scorer nor the scorer manager.");
+ }
+ }
+
+ @Override
+ public boolean setActiveScorer(String packageName) {
+ mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG);
+ // Preemptively clear scores even though the set operation could fail. We do this for safety
+ // as scores should never be compared across apps; in practice, Settings should only be
+ // allowing valid apps to be set as scorers, so failure here should be rare.
+ clearInternal();
+ return NetworkScorerAppManager.setActiveScorer(mContext, packageName);
+ }
+
+ /** Clear scores. Callers are responsible for checking permissions as appropriate. */
+ private void clearInternal() {
+ // TODO: Propagate the flush down to the network subsystem layer.
+ mScoredNetworks.clear();
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
+ String currentScorer = NetworkScorerAppManager.getActiveScorer(mContext);
+ if (currentScorer == null) {
+ writer.println("Scoring is disabled.");
+ return;
+ }
+ writer.println("Current scorer: " + currentScorer);
+ if (mScoredNetworks.isEmpty()) {
+ writer.println("No networks scored.");
+ } else {
+ for (Map.Entry<NetworkKey, RssiCurve> entry : mScoredNetworks.entrySet()) {
+ writer.println(entry.getKey() + ": " + entry.getValue());
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7607419..ecbb0d9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7240,6 +7240,9 @@
if (r == null) {
return null;
}
+ if (callback == null) {
+ throw new IllegalArgumentException("callback must not be null");
+ }
return mStackSupervisor.createActivityContainer(r, callback);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index d596472..60adfb0 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -139,7 +139,7 @@
boolean forceNewConfig; // force re-create with new config next time
int launchCount; // count of launches since last state
long lastLaunchTime; // time of last lauch of this activity
- ArrayList<ActivityStack> mChildContainers = new ArrayList<ActivityStack>();
+ ArrayList<ActivityContainer> mChildContainers = new ArrayList<ActivityContainer>();
String stringName; // for caching of toString().
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8dd4317..0acde09 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -36,6 +36,8 @@
import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES;
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.CONTAINER_STATE_HAS_SURFACE;
+
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService.ItemMatcher;
@@ -1066,9 +1068,9 @@
private void setVisibile(ActivityRecord r, boolean visible) {
r.visible = visible;
mWindowManager.setAppVisibility(r.appToken, visible);
- final ArrayList<ActivityStack> containers = r.mChildContainers;
+ final ArrayList<ActivityContainer> containers = r.mChildContainers;
for (int containerNdx = containers.size() - 1; containerNdx >= 0; --containerNdx) {
- ActivityContainer container = containers.get(containerNdx).mActivityContainer;
+ ActivityContainer container = containers.get(containerNdx);
container.setVisible(visible);
}
}
@@ -1270,6 +1272,7 @@
* occurred and the activity will be notified immediately.
*/
void notifyActivityDrawnLocked(ActivityRecord r) {
+ mActivityContainer.setDrawn();
if ((r == null)
|| (mUndrawnActivitiesBelowTopTranslucent.remove(r) &&
mUndrawnActivitiesBelowTopTranslucent.isEmpty())) {
@@ -1336,7 +1339,8 @@
if (ActivityManagerService.DEBUG_LOCKSCREEN) mService.logLockScreen("");
ActivityRecord parent = mActivityContainer.mParentActivity;
- if (parent != null && parent.state != ActivityState.RESUMED) {
+ if ((parent != null && parent.state != ActivityState.RESUMED) ||
+ !mActivityContainer.isAttached()) {
// Do not resume this stack if its parent is not resumed.
// TODO: If in a loop, make sure that parent stack resumeTopActivity is called 1st.
return false;
@@ -2624,6 +2628,20 @@
return r;
}
+ void finishAllActivitiesLocked() {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (r.finishing) {
+ continue;
+ }
+ Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r);
+ finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
+ }
+ }
+ }
+
final boolean navigateUpToLocked(IBinder token, Intent destIntent, int resultCode,
Intent resultData) {
final ActivityRecord srec = ActivityRecord.forToken(token);
@@ -2862,7 +2880,6 @@
}
if (activityRemoved) {
mStackSupervisor.resumeTopActivitiesLocked();
-
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 63f9d09..111f010 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -96,7 +96,6 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -225,11 +224,11 @@
// TODO: Add listener for removal of references.
/** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
- SparseArray<WeakReference<ActivityContainer>> mActivityContainers =
- new SparseArray<WeakReference<ActivityContainer>>();
+ SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>();
/** Mapping from displayId to display current state */
- private SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<ActivityDisplay>();
+ private final SparseArray<ActivityDisplay> mActivityDisplays =
+ new SparseArray<ActivityDisplay>();
InputManagerInternal mInputManagerInternal;
@@ -265,7 +264,7 @@
mActivityDisplays.put(displayId, activityDisplay);
}
- createStackOnDisplay(null, HOME_STACK_ID, Display.DEFAULT_DISPLAY);
+ createStackOnDisplay(HOME_STACK_ID, Display.DEFAULT_DISPLAY);
mHomeStack = mFocusedStack = mLastFocusedStack = getStack(HOME_STACK_ID);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
@@ -1386,7 +1385,7 @@
}
// Need to create an app stack for this user.
- int stackId = createStackOnDisplay(null, getNextStackId(), Display.DEFAULT_DISPLAY);
+ int stackId = createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY);
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG, "adjustStackFocus: New stack r=" + r +
" stackId=" + stackId);
mFocusedStack = getStack(stackId);
@@ -2154,14 +2153,9 @@
}
ActivityStack getStack(int stackId) {
- WeakReference<ActivityContainer> weakReference = mActivityContainers.get(stackId);
- if (weakReference != null) {
- ActivityContainer activityContainer = weakReference.get();
- if (activityContainer != null) {
- return activityContainer.mStack;
- } else {
- mActivityContainers.remove(stackId);
- }
+ ActivityContainer activityContainer = mActivityContainers.get(stackId);
+ if (activityContainer != null) {
+ return activityContainer.mStack;
}
return null;
}
@@ -2191,49 +2185,26 @@
return null;
}
- ActivityContainer createActivityContainer(ActivityRecord parentActivity, int stackId,
+ ActivityContainer createActivityContainer(ActivityRecord parentActivity,
IActivityContainerCallback callback) {
- ActivityContainer activityContainer = new ActivityContainer(parentActivity, stackId,
- callback);
- mActivityContainers.put(stackId, new WeakReference<ActivityContainer>(activityContainer));
- if (parentActivity != null) {
- parentActivity.mChildContainers.add(activityContainer.mStack);
- }
+ ActivityContainer activityContainer = new VirtualActivityContainer(parentActivity, callback);
+ mActivityContainers.put(activityContainer.mStackId, activityContainer);
+ parentActivity.mChildContainers.add(activityContainer);
return activityContainer;
}
- ActivityContainer createActivityContainer(ActivityRecord parentActivity,
- IActivityContainerCallback callback) {
- return createActivityContainer(parentActivity, getNextStackId(), callback);
- }
-
void removeChildActivityContainers(ActivityRecord parentActivity) {
- for (int ndx = mActivityContainers.size() - 1; ndx >= 0; --ndx) {
- final ActivityContainer container = mActivityContainers.valueAt(ndx).get();
- if (container == null) {
- mActivityContainers.removeAt(ndx);
- continue;
- }
- if (container.mParentActivity != parentActivity) {
- continue;
- }
-
- ActivityStack stack = container.mStack;
- ActivityRecord top = stack.topRunningNonDelayedActivityLocked(null);
- if (top != null) {
- // TODO: Make sure the next activity doesn't start up when top is destroyed.
- stack.destroyActivityLocked(top, true, true, "stack parent destroyed");
- }
- mActivityContainers.removeAt(ndx);
- container.detachLocked();
+ final ArrayList<ActivityContainer> childStacks = parentActivity.mChildContainers;
+ for (int containerNdx = childStacks.size() - 1; containerNdx >= 0; --containerNdx) {
+ ActivityContainer container = childStacks.remove(containerNdx);
+ container.release();
}
}
void deleteActivityContainer(IActivityContainer container) {
ActivityContainer activityContainer = (ActivityContainer)container;
if (activityContainer != null) {
- activityContainer.mStack.destroyActivitiesLocked(null, true,
- "deleteActivityContainer");
+ activityContainer.mStack.finishAllActivitiesLocked();
final ActivityRecord parent = activityContainer.mParentActivity;
if (parent != null) {
parent.mChildContainers.remove(activityContainer);
@@ -2244,14 +2215,14 @@
}
}
- private int createStackOnDisplay(ActivityRecord parentActivity, int stackId, int displayId) {
+ private int createStackOnDisplay(int stackId, int displayId) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
if (activityDisplay == null) {
return -1;
}
- ActivityContainer activityContainer =
- createActivityContainer(parentActivity, stackId, null);
+ ActivityContainer activityContainer = new ActivityContainer(stackId);
+ mActivityContainers.put(stackId, activityContainer);
activityContainer.attachToDisplayLocked(activityDisplay);
return stackId;
}
@@ -2334,9 +2305,9 @@
}
boolean shutdownLocked(int timeout) {
- boolean timedout = false;
goingToSleepLocked();
+ boolean timedout = false;
final long endTime = System.currentTimeMillis() + timeout;
while (true) {
boolean cantShutdown = false;
@@ -3030,24 +3001,26 @@
class ActivityContainer extends IActivityContainer.Stub {
final int mStackId;
- final IActivityContainerCallback mCallback;
+ IActivityContainerCallback mCallback = null;
final ActivityStack mStack;
- final ActivityRecord mParentActivity;
- final String mIdString;
+ ActivityRecord mParentActivity = null;
+ String mIdString;
boolean mVisible = true;
/** Display this ActivityStack is currently on. Null if not attached to a Display. */
ActivityDisplay mActivityDisplay;
- ActivityContainer(ActivityRecord parentActivity, int stackId,
- IActivityContainerCallback callback) {
+ final static int CONTAINER_STATE_HAS_SURFACE = 0;
+ final static int CONTAINER_STATE_NO_SURFACE = 1;
+ final static int CONTAINER_STATE_FINISHING = 2;
+ int mContainerState = CONTAINER_STATE_HAS_SURFACE;
+
+ ActivityContainer(int stackId) {
synchronized (mService) {
mStackId = stackId;
mStack = new ActivityStack(this);
- mParentActivity = parentActivity;
- mCallback = callback;
- mIdString = "ActivtyContainer{" + mStackId + ", parent=" + mParentActivity + "}";
+ mIdString = "ActivtyContainer{" + mStackId + "}";
if (DEBUG_STACK) Slog.d(TAG, "Creating " + this);
}
}
@@ -3097,6 +3070,14 @@
}
}
+ @Override
+ public void release() {
+ mContainerState = CONTAINER_STATE_FINISHING;
+ mStack.finishAllActivitiesLocked();
+ detachLocked();
+ mWindowManager.removeStack(mStackId);
+ }
+
private void detachLocked() {
if (DEBUG_STACK) Slog.d(TAG, "detachLocked: " + this + " from display="
+ mActivityDisplay + " Callers=" + Debug.getCallers(2));
@@ -3110,13 +3091,6 @@
}
@Override
- public void detachFromDisplay() {
- synchronized (mService) {
- detachLocked();
- }
- }
-
- @Override
public final int startActivity(Intent intent) {
mService.enforceNotIsolatedCaller("ActivityContainer.startActivity");
int userId = mService.handleIncomingUser(Binder.getCallingPid(),
@@ -3149,23 +3123,8 @@
}
@Override
- public void attachToSurface(Surface surface, int width, int height, int density) {
+ public void setSurface(Surface surface, int width, int height, int density) {
mService.enforceNotIsolatedCaller("ActivityContainer.attachToSurface");
-
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mService) {
- ActivityDisplay activityDisplay =
- new ActivityDisplay(surface, width, height, density);
- mActivityDisplays.put(activityDisplay.mDisplayId, activityDisplay);
- attachToDisplayLocked(activityDisplay);
- mStack.resumeTopActivityLocked(null);
- }
- if (DEBUG_STACK) Slog.d(TAG, "attachToSurface: " + this + " to display="
- + mActivityDisplay);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
}
ActivityStackSupervisor getOuter() {
@@ -3184,6 +3143,7 @@
}
}
+ // TODO: Make sure every change to ActivityRecord.visible results in a call to this.
void setVisible(boolean visible) {
if (mVisible != visible) {
mVisible = visible;
@@ -3194,52 +3154,114 @@
}
}
+ void setDrawn() {
+ }
+
@Override
public String toString() {
return mIdString + (mActivityDisplay == null ? "N" : "A");
}
}
+ private class VirtualActivityContainer extends ActivityContainer {
+ Surface mSurface;
+ boolean mDrawn = false;
+
+ VirtualActivityContainer(ActivityRecord parent, IActivityContainerCallback callback) {
+ super(getNextStackId());
+ mParentActivity = parent;
+ mCallback = callback;
+ mContainerState = CONTAINER_STATE_NO_SURFACE;
+ mIdString = "VirtualActivtyContainer{" + mStackId + ", parent=" + mParentActivity + "}";
+ }
+
+ @Override
+ public void setSurface(Surface surface, int width, int height, int density) {
+ super.setSurface(surface, width, height, density);
+
+ synchronized (mService) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ setSurfaceLocked(surface, width, height, density);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ private void setSurfaceLocked(Surface surface, int width, int height, int density) {
+ if (mContainerState == CONTAINER_STATE_FINISHING) {
+ return;
+ }
+ VirtualActivityDisplay virtualActivityDisplay =
+ (VirtualActivityDisplay) mActivityDisplay;
+ if (virtualActivityDisplay == null) {
+ virtualActivityDisplay =
+ new VirtualActivityDisplay(width, height, density);
+ mActivityDisplay = virtualActivityDisplay;
+ mActivityDisplays.put(virtualActivityDisplay.mDisplayId, virtualActivityDisplay);
+ attachToDisplayLocked(virtualActivityDisplay);
+ }
+
+ if (mSurface != null) {
+ mSurface.release();
+ }
+
+ mSurface = surface;
+ if (surface != null) {
+ mStack.resumeTopActivityLocked(null);
+ } else {
+ mContainerState = CONTAINER_STATE_NO_SURFACE;
+ ((VirtualActivityDisplay) mActivityDisplay).setSurface(null);
+// if (mStack.mPausingActivity == null && mStack.mResumedActivity != null) {
+// mStack.startPausingLocked(false, true);
+// }
+ }
+
+ setSurfaceIfReady();
+
+ if (DEBUG_STACK) Slog.d(TAG, "setSurface: " + this + " to display="
+ + virtualActivityDisplay);
+ }
+
+ @Override
+ void setDrawn() {
+ synchronized (mService) {
+ mDrawn = true;
+ setSurfaceIfReady();
+ }
+ }
+
+ private void setSurfaceIfReady() {
+ if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReady: mDrawn=" + mDrawn +
+ " mContainerState=" + mContainerState + " mSurface=" + mSurface);
+ if (mDrawn && mSurface != null && mContainerState == CONTAINER_STATE_NO_SURFACE) {
+ ((VirtualActivityDisplay) mActivityDisplay).setSurface(mSurface);
+ mContainerState = CONTAINER_STATE_HAS_SURFACE;
+ }
+ }
+ }
+
/** Exactly one of these classes per Display in the system. Capable of holding zero or more
* attached {@link ActivityStack}s */
- final class ActivityDisplay {
+ class ActivityDisplay {
/** Actual Display this object tracks. */
int mDisplayId;
Display mDisplay;
DisplayInfo mDisplayInfo = new DisplayInfo();
- Surface mSurface;
/** All of the stacks on this display. Order matters, topmost stack is in front of all other
* stacks, bottommost behind. Accessed directly by ActivityManager package classes */
final ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>();
- /** If this display is for an ActivityView then the VirtualDisplay created for it is stored
- * here. */
- VirtualDisplay mVirtualDisplay;
+ ActivityDisplay() {
+ }
ActivityDisplay(int displayId) {
init(mDisplayManager.getDisplay(displayId));
}
- ActivityDisplay(Surface surface, int width, int height, int density) {
- DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
- long ident = Binder.clearCallingIdentity();
- try {
- mVirtualDisplay = dm.createVirtualDisplay(mService.mContext,
- VIRTUAL_DISPLAY_BASE_NAME, width, height, density, surface,
- DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
- DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- init(mVirtualDisplay.getDisplay());
- mSurface = surface;
-
- mWindowManager.handleDisplayAdded(mDisplayId);
- }
-
- private void init(Display display) {
+ void init(Display display) {
mDisplay = display;
mDisplayId = display.getDisplayId();
mDisplay.getDisplayInfo(mDisplayInfo);
@@ -3255,11 +3277,6 @@
if (DEBUG_STACK) Slog.v(TAG, "detachActivitiesLocked: detaching " + stack
+ " from displayId=" + mDisplayId);
mStacks.remove(stack);
- if (mStacks.isEmpty() && mVirtualDisplay != null) {
- mVirtualDisplay.release();
- mVirtualDisplay = null;
- }
- mSurface.release();
}
void getBounds(Point bounds) {
@@ -3270,8 +3287,42 @@
@Override
public String toString() {
- return "ActivityDisplay={" + mDisplayId + (mVirtualDisplay == null ? "" : "V")
- + " numStacks=" + mStacks.size() + "}";
+ return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
+ }
+ }
+
+ class VirtualActivityDisplay extends ActivityDisplay {
+ VirtualDisplay mVirtualDisplay;
+
+ VirtualActivityDisplay(int width, int height, int density) {
+ DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
+ mVirtualDisplay = dm.createVirtualDisplay(mService.mContext, VIRTUAL_DISPLAY_BASE_NAME,
+ width, height, density, null, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+
+ init(mVirtualDisplay.getDisplay());
+
+ mWindowManager.handleDisplayAdded(mDisplayId);
+ }
+
+ void setSurface(Surface surface) {
+ if (mVirtualDisplay != null) {
+ mVirtualDisplay.setSurface(surface);
+ }
+ }
+
+ @Override
+ void detachActivitiesLocked(ActivityStack stack) {
+ super.detachActivitiesLocked(stack);
+ if (mVirtualDisplay != null) {
+ mVirtualDisplay.release();
+ mVirtualDisplay = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "VirtualActivityDisplay={" + mDisplayId + "}";
}
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 75e5857..6700895 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
@@ -42,6 +43,7 @@
import android.tv.TvInputService;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.Surface;
@@ -116,17 +118,19 @@
UserState userState = getUserStateLocked(userId);
userState.inputList.clear();
+ if (DEBUG) Slog.d(TAG, "buildTvInputList");
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> services = pm.queryIntentServices(
new Intent(TvInputService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
for (ResolveInfo ri : services) {
ServiceInfo si = ri.serviceInfo;
if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
- Log.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
+ Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
+ android.Manifest.permission.BIND_TV_INPUT);
continue;
}
TvInputInfo info = new TvInputInfo(ri);
+ if (DEBUG) Slog.d(TAG, "add " + info.getId());
userState.inputList.add(info);
}
}
@@ -161,7 +165,7 @@
try {
state.session.release();
} catch (RemoteException e) {
- Log.e(TAG, "error in release", e);
+ Slog.e(TAG, "error in release", e);
}
}
}
@@ -173,7 +177,7 @@
try {
serviceState.service.unregisterCallback(serviceState.callback);
} catch (RemoteException e) {
- Log.e(TAG, "error in unregisterCallback", e);
+ Slog.e(TAG, "error in unregisterCallback", e);
}
}
serviceState.clients.clear();
@@ -244,7 +248,7 @@
return;
}
if (DEBUG) {
- Log.i(TAG, "bindServiceAsUser(name=" + name.getClassName() + ", userId=" + userId
+ Slog.d(TAG, "bindServiceAsUser(name=" + name.getClassName() + ", userId=" + userId
+ ")");
}
Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(name);
@@ -255,7 +259,7 @@
// This means that the service is already connected but its state indicates that we have
// nothing to do with it. Then, disconnect the service.
if (DEBUG) {
- Log.i(TAG, "unbindService(name=" + name.getClassName() + ")");
+ Slog.d(TAG, "unbindService(name=" + name.getClassName() + ")");
}
mContext.unbindService(serviceState.connection);
userState.serviceStateMap.remove(name);
@@ -267,7 +271,7 @@
final SessionState sessionState =
getUserStateLocked(userId).sessionStateMap.get(sessionToken);
if (DEBUG) {
- Log.d(TAG, "createSessionInternalLocked(name=" + sessionState.name.getClassName()
+ Slog.d(TAG, "createSessionInternalLocked(name=" + sessionState.name.getClassName()
+ ")");
}
// Set up a callback to send the session token.
@@ -275,7 +279,7 @@
@Override
public void onSessionCreated(ITvInputSession session) {
if (DEBUG) {
- Log.d(TAG, "onSessionCreated(name=" + sessionState.name.getClassName() + ")");
+ Slog.d(TAG, "onSessionCreated(name=" + sessionState.name.getClassName() + ")");
}
synchronized (mLock) {
sessionState.session = session;
@@ -295,7 +299,7 @@
try {
service.createSession(callback);
} catch (RemoteException e) {
- Log.e(TAG, "error in createSession", e);
+ Slog.e(TAG, "error in createSession", e);
removeSessionStateLocked(sessionToken, userId);
sendSessionTokenToClientLocked(sessionState.client, sessionState.name, null,
sessionState.seq, userId);
@@ -307,7 +311,7 @@
try {
client.onSessionCreated(name, sessionToken, seq);
} catch (RemoteException exception) {
- Log.e(TAG, "error in onSessionCreated", exception);
+ Slog.e(TAG, "error in onSessionCreated", exception);
}
if (sessionToken == null) {
@@ -396,7 +400,7 @@
try {
serviceState.service.registerCallback(serviceState.callback);
} catch (RemoteException e) {
- Log.e(TAG, "error in registerCallback", e);
+ Slog.e(TAG, "error in registerCallback", e);
}
} else {
updateServiceConnectionLocked(name, resolvedUserId);
@@ -432,7 +436,7 @@
try {
serviceState.service.unregisterCallback(serviceState.callback);
} catch (RemoteException e) {
- Log.e(TAG, "error in unregisterCallback", e);
+ Slog.e(TAG, "error in unregisterCallback", e);
} finally {
serviceState.callback = null;
updateServiceConnectionLocked(name, resolvedUserId);
@@ -493,7 +497,7 @@
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId).release();
} catch (RemoteException e) {
- Log.e(TAG, "error in release", e);
+ Slog.e(TAG, "error in release", e);
}
removeSessionStateLocked(sessionToken, resolvedUserId);
@@ -515,7 +519,7 @@
getSessionLocked(sessionToken, callingUid, resolvedUserId).setSurface(
surface);
} catch (RemoteException e) {
- Log.e(TAG, "error in setSurface", e);
+ Slog.e(TAG, "error in setSurface", e);
}
}
} finally {
@@ -535,7 +539,7 @@
getSessionLocked(sessionToken, callingUid, resolvedUserId).setVolume(
volume);
} catch (RemoteException e) {
- Log.e(TAG, "error in setVolume", e);
+ Slog.e(TAG, "error in setVolume", e);
}
}
} finally {
@@ -551,13 +555,10 @@
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- SessionState sessionState = getUserStateLocked(resolvedUserId)
- .sessionStateMap.get(sessionToken);
- final String serviceName = sessionState.name.getClassName();
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri);
} catch (RemoteException e) {
- Log.e(TAG, "error in tune", e);
+ Slog.e(TAG, "error in tune", e);
return;
}
}
@@ -565,6 +566,67 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
+ public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
+ int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "createOverlayView");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .createOverlayView(windowToken, frame);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in createOverlayView", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "relayoutOverlayView");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .relayoutOverlayView(frame);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in relayoutOverlayView", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void removeOverlayView(IBinder sessionToken, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "removeOverlayView");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .removeOverlayView();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in removeOverlayView", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
private static final class UserState {
@@ -621,7 +683,7 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) {
- Log.d(TAG, "onServiceConnected(name=" + name.getClassName() + ")");
+ Slog.d(TAG, "onServiceConnected(name=" + name.getClassName() + ")");
}
synchronized (mLock) {
ServiceState serviceState = getServiceStateLocked(name, mUserId);
@@ -633,7 +695,7 @@
try {
serviceState.service.registerCallback(serviceState.callback);
} catch (RemoteException e) {
- Log.e(TAG, "error in registerCallback", e);
+ Slog.e(TAG, "error in registerCallback", e);
}
}
@@ -647,7 +709,7 @@
@Override
public void onServiceDisconnected(ComponentName name) {
if (DEBUG) {
- Log.d(TAG, "onServiceDisconnected(name=" + name.getClassName() + ")");
+ Slog.d(TAG, "onServiceDisconnected(name=" + name.getClassName() + ")");
}
}
}
@@ -663,7 +725,7 @@
public void onAvailabilityChanged(ComponentName name, boolean isAvailable)
throws RemoteException {
if (DEBUG) {
- Log.d(TAG, "onAvailabilityChanged(name=" + name.getClassName() + ", isAvailable="
+ Slog.d(TAG, "onAvailabilityChanged(name=" + name.getClassName() + ", isAvailable="
+ isAvailable + ")");
}
synchronized (mLock) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 912ac4d..f08d69f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -311,6 +311,7 @@
NetworkStatsService networkStats = null;
NetworkPolicyManagerService networkPolicy = null;
ConnectivityService connectivity = null;
+ NetworkScoreService networkScore = null;
NsdService serviceDiscovery= null;
IPackageManager pm = null;
WindowManagerService wm = null;
@@ -643,6 +644,14 @@
}
try {
+ Slog.i(TAG, "Network Score Service");
+ networkScore = new NetworkScoreService(context);
+ ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore);
+ } catch (Throwable e) {
+ reportWtf("starting Network Score Service", e);
+ }
+
+ try {
Slog.i(TAG, "Network Service Discovery Service");
serviceDiscovery = NsdService.create(context);
ServiceManager.addService(
@@ -1021,6 +1030,7 @@
final NetworkStatsService networkStatsF = networkStats;
final NetworkPolicyManagerService networkPolicyF = networkPolicy;
final ConnectivityService connectivityF = connectivity;
+ final NetworkScoreService networkScoreF = networkScore;
final DockObserver dockF = dock;
final WallpaperManagerService wallpaperF = wallpaper;
final InputMethodManagerService immF = imm;
@@ -1069,6 +1079,11 @@
reportWtf("making Battery Service ready", e);
}
try {
+ if (networkScoreF != null) networkScoreF.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Network Score Service ready", e);
+ }
+ try {
if (networkManagementF != null) networkManagementF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Managment Service ready", e);