am 4d84e856: Merge "Make QS panel peek below the header." into lmp-preview-dev
* commit '4d84e85610ddc3eb593f85a1db12d8dfe8e15820':
Make QS panel peek below the header.
diff --git a/Android.mk b/Android.mk
index 9c41f9f..9dd9fb0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -78,6 +78,7 @@
core/java/android/app/IServiceConnection.aidl \
core/java/android/app/IStopUserCallback.aidl \
core/java/android/app/task/ITaskCallback.aidl \
+ core/java/android/app/task/ITaskManager.aidl \
core/java/android/app/task/ITaskService.aidl \
core/java/android/app/IThumbnailRetriever.aidl \
core/java/android/app/ITransientNotification.aidl \
@@ -326,6 +327,7 @@
telecomm/java/com/android/internal/telecomm/ICallServiceSelectorAdapter.aidl \
telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl \
telecomm/java/com/android/internal/telecomm/IInCallService.aidl \
+ telecomm/java/com/android/internal/telecomm/ITelecommService.aidl \
telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
telephony/java/com/android/internal/telephony/ITelephony.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index c841338..48a20a4 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -191,6 +191,7 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/tv/)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/media/java/android/media/)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/wearable)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/current.txt b/api/current.txt
index a829bdf..46a8470 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -749,6 +749,7 @@
field public static final int layout_centerVertical = 16843153; // 0x1010191
field public static final int layout_column = 16843084; // 0x101014c
field public static final int layout_columnSpan = 16843645; // 0x101037d
+ field public static final int layout_columnWeight = 16843868; // 0x101045c
field public static final int layout_gravity = 16842931; // 0x10100b3
field public static final int layout_height = 16842997; // 0x10100f5
field public static final int layout_margin = 16842998; // 0x10100f6
@@ -760,6 +761,7 @@
field public static final int layout_marginTop = 16843000; // 0x10100f8
field public static final int layout_row = 16843643; // 0x101037b
field public static final int layout_rowSpan = 16843644; // 0x101037c
+ field public static final int layout_rowWeight = 16843867; // 0x101045b
field public static final int layout_scale = 16843155; // 0x1010193
field public static final int layout_span = 16843085; // 0x101014d
field public static final int layout_toEndOf = 16843704; // 0x10103b8
@@ -1399,8 +1401,6 @@
field public static final int l_resource_pad9 = 17104904; // 0x1050008
field public static final int notification_large_icon_height = 17104902; // 0x1050006
field public static final int notification_large_icon_width = 17104901; // 0x1050005
- field public static final int recents_thumbnail_height = 17104913; // 0x1050011
- field public static final int recents_thumbnail_width = 17104914; // 0x1050012
field public static final int thumbnail_height = 17104897; // 0x1050001
field public static final int thumbnail_width = 17104898; // 0x1050002
}
@@ -4538,13 +4538,22 @@
ctor public Notification.Action.Builder(android.app.Notification.Action);
method public android.app.Notification.Action.Builder addExtras(android.os.Bundle);
method public android.app.Notification.Action.Builder addRemoteInput(android.app.RemoteInput);
- method public android.app.Notification.Action.Builder apply(android.app.Notification.Action.Builder.Extender);
method public android.app.Notification.Action build();
+ method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Extender);
method public android.os.Bundle getExtras();
}
- public static abstract interface Notification.Action.Builder.Extender {
- method public abstract android.app.Notification.Action.Builder applyTo(android.app.Notification.Action.Builder);
+ public static abstract interface Notification.Action.Extender {
+ method public abstract android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
+ }
+
+ public static final class Notification.Action.WearableExtender implements android.app.Notification.Action.Extender {
+ ctor public Notification.Action.WearableExtender();
+ ctor public Notification.Action.WearableExtender(android.app.Notification.Action);
+ method public android.app.Notification.Action.WearableExtender clone();
+ method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
+ method public boolean isAvailableOffline();
+ method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
}
public static class Notification.BigPictureStyle extends android.app.Notification.Style {
@@ -4570,8 +4579,8 @@
method public android.app.Notification.Builder addAction(android.app.Notification.Action);
method public android.app.Notification.Builder addExtras(android.os.Bundle);
method public android.app.Notification.Builder addPerson(java.lang.String);
- method public android.app.Notification.Builder apply(android.app.Notification.Builder.Extender);
method public android.app.Notification build();
+ method public android.app.Notification.Builder extend(android.app.Notification.Extender);
method public android.os.Bundle getExtras();
method public deprecated android.app.Notification getNotification();
method public android.app.Notification.Builder setAutoCancel(boolean);
@@ -4613,8 +4622,8 @@
method public android.app.Notification.Builder setWhen(long);
}
- public static abstract interface Notification.Builder.Extender {
- method public abstract android.app.Notification.Builder applyTo(android.app.Notification.Builder);
+ public static abstract interface Notification.Extender {
+ method public abstract android.app.Notification.Builder extend(android.app.Notification.Builder);
}
public static class Notification.InboxStyle extends android.app.Notification.Style {
@@ -4644,6 +4653,51 @@
field protected android.app.Notification.Builder mBuilder;
}
+ public static final class Notification.WearableExtender implements android.app.Notification.Extender {
+ ctor public Notification.WearableExtender();
+ ctor public Notification.WearableExtender(android.app.Notification);
+ method public android.app.Notification.WearableExtender addAction(android.app.Notification.Action);
+ method public android.app.Notification.WearableExtender addActions(java.util.List<android.app.Notification.Action>);
+ method public android.app.Notification.WearableExtender addPage(android.app.Notification);
+ method public android.app.Notification.WearableExtender addPages(java.util.List<android.app.Notification>);
+ method public android.app.Notification.WearableExtender clearActions();
+ method public android.app.Notification.WearableExtender clearPages();
+ method public android.app.Notification.WearableExtender clone();
+ method public android.app.Notification.Builder extend(android.app.Notification.Builder);
+ method public java.util.List<android.app.Notification.Action> getActions();
+ method public android.graphics.Bitmap getBackground();
+ method public int getContentAction();
+ method public int getContentIcon();
+ method public int getContentIconGravity();
+ method public boolean getContentIntentAvailableOffline();
+ method public int getCustomContentHeight();
+ method public int getCustomSizePreset();
+ method public android.app.PendingIntent getDisplayIntent();
+ method public int getGravity();
+ method public boolean getHintHideIcon();
+ method public boolean getHintShowBackgroundOnly();
+ method public java.util.List<android.app.Notification> getPages();
+ method public boolean getStartScrollBottom();
+ method public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
+ method public android.app.Notification.WearableExtender setContentAction(int);
+ method public android.app.Notification.WearableExtender setContentIcon(int);
+ method public android.app.Notification.WearableExtender setContentIconGravity(int);
+ method public android.app.Notification.WearableExtender setContentIntentAvailableOffline(boolean);
+ method public android.app.Notification.WearableExtender setCustomContentHeight(int);
+ method public android.app.Notification.WearableExtender setCustomSizePreset(int);
+ method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
+ method public android.app.Notification.WearableExtender setGravity(int);
+ method public android.app.Notification.WearableExtender setHintHideIcon(boolean);
+ method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
+ method public android.app.Notification.WearableExtender setStartScrollBottom(boolean);
+ field public static final int SIZE_DEFAULT = 0; // 0x0
+ field public static final int SIZE_LARGE = 4; // 0x4
+ field public static final int SIZE_MEDIUM = 3; // 0x3
+ field public static final int SIZE_SMALL = 2; // 0x2
+ field public static final int SIZE_XSMALL = 1; // 0x1
+ field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+ }
+
public class NotificationManager {
method public void cancel(int);
method public void cancel(java.lang.String, int);
@@ -5298,6 +5352,57 @@
package android.app.task {
+ public class Task implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getBackoffPolicy();
+ method public android.os.Bundle getExtras();
+ method public long getInitialBackoffMillis();
+ method public long getIntervalMillis();
+ method public long getMaxExecutionDelayMillis();
+ method public long getMinLatencyMillis();
+ method public int getNetworkCapabilities();
+ method public android.content.ComponentName getService();
+ method public int getTaskId();
+ method public boolean isPeriodic();
+ method public boolean isRequireCharging();
+ method public boolean isRequireDeviceIdle();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public static abstract interface Task.BackoffPolicy {
+ field public static final int EXPONENTIAL = 1; // 0x1
+ field public static final int LINEAR = 0; // 0x0
+ }
+
+ public final class Task.Builder {
+ ctor public Task.Builder(int, android.content.ComponentName);
+ method public android.app.task.Task build();
+ method public android.app.task.Task.Builder setBackoffCriteria(long, int);
+ method public android.app.task.Task.Builder setExtras(android.os.Bundle);
+ method public android.app.task.Task.Builder setMinimumLatency(long);
+ method public android.app.task.Task.Builder setOverrideDeadline(long);
+ method public android.app.task.Task.Builder setPeriodic(long);
+ method public android.app.task.Task.Builder setRequiredNetworkCapabilities(int);
+ method public android.app.task.Task.Builder setRequiresCharging(boolean);
+ method public android.app.task.Task.Builder setRequiresDeviceIdle(boolean);
+ }
+
+ public static abstract interface Task.NetworkType {
+ field public static final int ANY = 0; // 0x0
+ field public static final int UNMETERED = 1; // 0x1
+ }
+
+ public abstract class TaskManager {
+ ctor public TaskManager();
+ method public abstract void cancel(int);
+ method public abstract void cancelAll();
+ method public abstract java.util.List<android.app.task.Task> getAllPendingTasks();
+ method public abstract int schedule(android.app.task.Task);
+ field public static final int RESULT_INVALID_PARAMETERS = -1; // 0xffffffff
+ field public static final int RESULT_OVER_QUOTA = -2; // 0xfffffffe
+ }
+
public class TaskParams implements android.os.Parcelable {
method public int describeContents();
method public android.os.Bundle getExtras();
@@ -5317,80 +5422,6 @@
}
-package android.app.wearable {
-
- public final class WearableActionExtensions implements android.app.Notification.Action.Builder.Extender android.os.Parcelable {
- method public android.app.Notification.Action.Builder applyTo(android.app.Notification.Action.Builder);
- method public int describeContents();
- method public static android.app.wearable.WearableActionExtensions from(android.app.Notification.Action);
- method public boolean isAvailableOffline();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- }
-
- public static final class WearableActionExtensions.Builder {
- ctor public WearableActionExtensions.Builder();
- ctor public WearableActionExtensions.Builder(android.app.wearable.WearableActionExtensions);
- method public android.app.wearable.WearableActionExtensions build();
- method public android.app.wearable.WearableActionExtensions.Builder setAvailableOffline(boolean);
- }
-
- public final class WearableNotificationExtensions implements android.app.Notification.Builder.Extender android.os.Parcelable {
- method public android.app.Notification.Builder applyTo(android.app.Notification.Builder);
- method public int describeContents();
- method public static android.app.wearable.WearableNotificationExtensions from(android.app.Notification);
- method public android.app.Notification.Action getAction(int);
- method public int getActionCount();
- method public android.app.Notification.Action[] getActions();
- method public android.graphics.Bitmap getBackground();
- method public int getContentAction();
- method public int getContentIcon();
- method public int getContentIconGravity();
- method public boolean getContentIntentAvailableOffline();
- method public int getCustomContentHeight();
- method public int getCustomSizePreset();
- method public android.app.PendingIntent getDisplayIntent();
- method public int getGravity();
- method public boolean getHintHideIcon();
- method public boolean getHintShowBackgroundOnly();
- method public android.app.Notification[] getPages();
- method public boolean getStartScrollBottom();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- field public static final int SIZE_DEFAULT = 0; // 0x0
- field public static final int SIZE_LARGE = 4; // 0x4
- field public static final int SIZE_MEDIUM = 3; // 0x3
- field public static final int SIZE_SMALL = 2; // 0x2
- field public static final int SIZE_XSMALL = 1; // 0x1
- field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
- }
-
- public static final class WearableNotificationExtensions.Builder {
- ctor public WearableNotificationExtensions.Builder();
- ctor public WearableNotificationExtensions.Builder(android.app.wearable.WearableNotificationExtensions);
- method public android.app.wearable.WearableNotificationExtensions.Builder addAction(android.app.Notification.Action);
- method public android.app.wearable.WearableNotificationExtensions.Builder addActions(java.util.List<android.app.Notification.Action>);
- method public android.app.wearable.WearableNotificationExtensions.Builder addPage(android.app.Notification);
- method public android.app.wearable.WearableNotificationExtensions.Builder addPages(java.util.List<android.app.Notification>);
- method public android.app.wearable.WearableNotificationExtensions build();
- method public android.app.wearable.WearableNotificationExtensions.Builder clearActions();
- method public android.app.wearable.WearableNotificationExtensions.Builder clearPages();
- method public android.app.wearable.WearableNotificationExtensions.Builder setBackground(android.graphics.Bitmap);
- method public android.app.wearable.WearableNotificationExtensions.Builder setContentAction(int);
- method public android.app.wearable.WearableNotificationExtensions.Builder setContentIcon(int);
- method public android.app.wearable.WearableNotificationExtensions.Builder setContentIconGravity(int);
- method public android.app.wearable.WearableNotificationExtensions.Builder setContentIntentAvailableOffline(boolean);
- method public android.app.wearable.WearableNotificationExtensions.Builder setCustomContentHeight(int);
- method public android.app.wearable.WearableNotificationExtensions.Builder setCustomSizePreset(int);
- method public android.app.wearable.WearableNotificationExtensions.Builder setDisplayIntent(android.app.PendingIntent);
- method public android.app.wearable.WearableNotificationExtensions.Builder setGravity(int);
- method public android.app.wearable.WearableNotificationExtensions.Builder setHintHideIcon(boolean);
- method public android.app.wearable.WearableNotificationExtensions.Builder setHintShowBackgroundOnly(boolean);
- method public android.app.wearable.WearableNotificationExtensions.Builder setStartScrollBottom(boolean);
- }
-
-}
-
package android.appwidget {
public class AppWidgetHost {
@@ -6995,6 +7026,7 @@
field public static final java.lang.String SEARCH_SERVICE = "search";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String STORAGE_SERVICE = "storage";
+ field public static final java.lang.String TASK_SERVICE = "task";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
field public static final java.lang.String TV_INPUT_SERVICE = "tv_input";
@@ -7933,55 +7965,6 @@
method public abstract void onStatusChanged(int);
}
- public class Task implements android.os.Parcelable {
- method public int describeContents();
- method public int getBackoffPolicy();
- method public android.os.Bundle getExtras();
- method public long getInitialBackoffMillis();
- method public long getIntervalMillis();
- method public long getMaxExecutionDelayMillis();
- method public long getMinLatencyMillis();
- method public int getNetworkCapabilities();
- method public android.content.ComponentName getService();
- method public int getTaskId();
- method public boolean isPeriodic();
- method public boolean isRequireCharging();
- method public boolean isRequireDeviceIdle();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- }
-
- public static abstract interface Task.BackoffPolicy {
- field public static final int EXPONENTIAL = 1; // 0x1
- field public static final int LINEAR = 0; // 0x0
- }
-
- public final class Task.Builder {
- ctor public Task.Builder(int, android.content.ComponentName);
- method public android.content.Task build();
- method public android.content.Task.Builder setBackoffCriteria(long, int);
- method public android.content.Task.Builder setExtras(android.os.Bundle);
- method public android.content.Task.Builder setMinimumLatency(long);
- method public android.content.Task.Builder setOverrideDeadline(long);
- method public android.content.Task.Builder setPeriodic(long);
- method public android.content.Task.Builder setRequiredNetworkCapabilities(int);
- method public android.content.Task.Builder setRequiresCharging(boolean);
- method public android.content.Task.Builder setRequiresDeviceIdle(boolean);
- }
-
- public static abstract interface Task.NetworkType {
- field public static final int ANY = 0; // 0x0
- field public static final int UNMETERED = 1; // 0x1
- }
-
- public abstract class TaskManager {
- ctor public TaskManager();
- method public abstract void cancel(int);
- method public abstract void cancelAll();
- method public abstract java.util.List<android.content.Task> getAllPendingTasks();
- method public abstract int schedule(android.content.Task);
- }
-
public class UriMatcher {
ctor public UriMatcher(int);
method public void addURI(java.lang.String, java.lang.String, int);
@@ -11954,7 +11937,6 @@
method public int getMinDelay();
method public java.lang.String getName();
method public float getPower();
- method public java.lang.String getRequiredPermission();
method public float getResolution();
method public java.lang.String getStringType();
method public int getType();
@@ -13781,7 +13763,6 @@
}
public class AudioFormat {
- ctor public AudioFormat();
field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1
field public static final deprecated int CHANNEL_CONFIGURATION_INVALID = 0; // 0x0
field public static final deprecated int CHANNEL_CONFIGURATION_MONO = 2; // 0x2
@@ -17132,11 +17113,9 @@
}
public static final class WifiEnterpriseConfig.Eap {
- field public static final int AKA = 5; // 0x5
field public static final int NONE = -1; // 0xffffffff
field public static final int PEAP = 0; // 0x0
field public static final int PWD = 3; // 0x3
- field public static final int SIM = 4; // 0x4
field public static final int TLS = 1; // 0x1
field public static final int TTLS = 2; // 0x2
}
@@ -17556,14 +17535,14 @@
package android.net.wifi.passpoint {
public class WifiPasspointCredential implements android.os.Parcelable {
- ctor public WifiPasspointCredential(java.lang.String, android.net.wifi.WifiEnterpriseConfig);
+ ctor public WifiPasspointCredential(java.lang.String, java.lang.String, android.net.wifi.WifiEnterpriseConfig);
method public int describeContents();
- method public java.lang.String getClientCertPath();
- method public int getEapMethod();
+ method public android.net.wifi.WifiEnterpriseConfig getEnterpriseConfig();
method public java.lang.String getFqdn();
- method public java.lang.String getImsi();
method public java.lang.String getRealm();
- method public java.lang.String getUserName();
+ method public void setEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+ method public void setFqdn(java.lang.String);
+ method public void setRealm(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
}
@@ -32917,6 +32896,7 @@
method public void requestLayout();
method public boolean requestRectangleOnScreen(android.graphics.Rect);
method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
+ method public final void requestUnbufferedDispatch(android.view.MotionEvent);
method public static int resolveSize(int, int);
method public static int resolveSizeAndState(int, int, int);
method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>);
@@ -36610,6 +36590,10 @@
method public void setRowCount(int);
method public void setRowOrderPreserved(boolean);
method public void setUseDefaultMargins(boolean);
+ method public static android.widget.GridLayout.Spec spec(int, int, android.widget.GridLayout.Alignment, float);
+ method public static android.widget.GridLayout.Spec spec(int, android.widget.GridLayout.Alignment, float);
+ method public static android.widget.GridLayout.Spec spec(int, int, float);
+ method public static android.widget.GridLayout.Spec spec(int, float);
method public static android.widget.GridLayout.Spec spec(int, int, android.widget.GridLayout.Alignment);
method public static android.widget.GridLayout.Spec spec(int, android.widget.GridLayout.Alignment);
method public static android.widget.GridLayout.Spec spec(int, int);
diff --git a/api/removed.txt b/api/removed.txt
index e69de29..458c422 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -0,0 +1,8 @@
+package android.media {
+
+ public class AudioFormat {
+ ctor public AudioFormat();
+ }
+
+}
+
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5fd288f..d9adba3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3010,8 +3010,8 @@
int h;
if (w < 0) {
Resources res = r.activity.getResources();
- int wId = com.android.internal.R.dimen.recents_thumbnail_width;
- int hId = com.android.internal.R.dimen.recents_thumbnail_height;
+ int wId = com.android.internal.R.dimen.thumbnail_width;
+ int hId = com.android.internal.R.dimen.thumbnail_height;
mThumbnailWidth = w = res.getDimensionPixelSize(wId);
mThumbnailHeight = h = res.getDimensionPixelSize(hId);
} else {
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 7617886..d08978bd 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -189,15 +189,17 @@
final protected SharedElementListener mListener;
protected ResultReceiver mResultReceiver;
final private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
+ final protected boolean mIsReturning;
public ActivityTransitionCoordinator(Window window,
ArrayList<String> allSharedElementNames,
ArrayList<String> accepted, ArrayList<String> localNames,
- SharedElementListener listener) {
+ SharedElementListener listener, boolean isReturning) {
super(new Handler());
mWindow = window;
mListener = listener;
mAllSharedElementNames = allSharedElementNames;
+ mIsReturning = isReturning;
setSharedElements(accepted, localNames);
if (getViewsTransition() != null) {
getDecor().captureTransitioningViews(mTransitioningViews);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1634d11..ff8688d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -133,10 +133,12 @@
import android.accounts.AccountManager;
import android.accounts.IAccountManager;
import android.app.admin.DevicePolicyManager;
+import android.app.task.ITaskManager;
import android.app.trust.TrustManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.appwidget.IAppWidgetService.Stub;
import com.android.internal.os.IDropBoxManagerService;
import java.io.File;
@@ -693,6 +695,12 @@
public Object createService(ContextImpl ctx) {
return new UsageStatsManager(ctx.getOuterContext());
}});
+
+ registerService(TASK_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(TASK_SERVICE);
+ return new TaskManagerImpl(ITaskManager.Stub.asInterface(b));
+ }});
}
static ContextImpl getImpl(Context context) {
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 4cca355..bc97852 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -55,16 +55,14 @@
private boolean mHasStopped;
private Handler mHandler;
private boolean mIsCanceled;
- private boolean mIsReturning;
private ObjectAnimator mBackgroundAnimator;
public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
ArrayList<String> sharedElementNames,
ArrayList<String> acceptedNames, ArrayList<String> mappedNames) {
super(activity.getWindow(), sharedElementNames, acceptedNames, mappedNames,
- getListener(activity, acceptedNames));
+ getListener(activity, acceptedNames), acceptedNames != null);
mActivity = activity;
- mIsReturning = acceptedNames != null;
setResultReceiver(resultReceiver);
prepareEnter();
Bundle resultReceiverBundle = new Bundle();
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index f36c36a..93eb53e 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -58,16 +58,14 @@
private Handler mHandler;
- private boolean mIsReturning;
-
private ObjectAnimator mBackgroundAnimator;
private boolean mIsHidden;
public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
- super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning));
- mIsReturning = isReturning;
+ super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning),
+ isReturning);
mIsBackgroundReady = !isReturning;
mActivity = activity;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index dfd927f..6e23b11 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -35,6 +35,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
+import android.view.Gravity;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.RemoteViews;
@@ -46,7 +47,9 @@
import java.lang.annotation.RetentionPolicy;
import java.text.NumberFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
/**
* A class that represents how a persistent notification is to be presented to
@@ -767,7 +770,7 @@
*/
public static class Action implements Parcelable {
private final Bundle mExtras;
- private RemoteInput[] mRemoteInputs;
+ private final RemoteInput[] mRemoteInputs;
/**
* Small icon representing the action.
@@ -910,25 +913,12 @@
* Apply an extender to this action builder. Extenders may be used to add
* metadata or change options on this builder.
*/
- public Builder apply(Extender extender) {
- extender.applyTo(this);
+ public Builder extend(Extender extender) {
+ extender.extend(this);
return this;
}
/**
- * Extender interface for use with {@link #apply}. Extenders may be used to add
- * metadata or change options on this builder.
- */
- public interface Extender {
- /**
- * Apply this extender to a notification action builder.
- * @param builder the builder to be modified.
- * @return the build object for chaining.
- */
- public Builder applyTo(Builder builder);
- }
-
- /**
* Combine all of the options that have been set and return a new {@link Action}
* object.
* @return the built action
@@ -975,6 +965,121 @@
return new Action[size];
}
};
+
+ /**
+ * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
+ * metadata or change options on an action builder.
+ */
+ public interface Extender {
+ /**
+ * Apply this extender to a notification action builder.
+ * @param builder the builder to be modified.
+ * @return the build object for chaining.
+ */
+ public Builder extend(Builder builder);
+ }
+
+ /**
+ * Wearable extender for notification actions. To add extensions to an action,
+ * create a new {@link android.app.Notification.Action.WearableExtender} object using
+ * the {@code WearableExtender()} constructor and apply it to a
+ * {@link android.app.Notification.Action.Builder} using
+ * {@link android.app.Notification.Action.Builder#extend}.
+ *
+ * <pre class="prettyprint">
+ * Notification.Action action = new Notification.Action.Builder(
+ * R.drawable.archive_all, "Archive all", actionIntent)
+ * .apply(new Notification.Action.WearableExtender()
+ * .setAvailableOffline(false))
+ * .build();
+ * </pre>
+ */
+ public static final class WearableExtender implements Extender {
+ /** Notification action extra which contains wearable extensions */
+ private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
+
+ private static final String KEY_FLAGS = "flags";
+
+ // Flags bitwise-ored to mFlags
+ private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
+
+ // Default value for flags integer
+ private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
+
+ private int mFlags = DEFAULT_FLAGS;
+
+ /**
+ * Create a {@link android.app.Notification.Action.WearableExtender} with default
+ * options.
+ */
+ public WearableExtender() {
+ }
+
+ /**
+ * Create a {@link android.app.Notification.Action.WearableExtender} by reading
+ * wearable options present in an existing notification action.
+ * @param action the notification action to inspect.
+ */
+ public WearableExtender(Action action) {
+ Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
+ if (wearableBundle != null) {
+ mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
+ }
+ }
+
+ /**
+ * Apply wearable extensions to a notification action that is being built. This is
+ * typically called by the {@link android.app.Notification.Action.Builder#extend}
+ * method of {@link android.app.Notification.Action.Builder}.
+ */
+ @Override
+ public Action.Builder extend(Action.Builder builder) {
+ Bundle wearableBundle = new Bundle();
+
+ if (mFlags != DEFAULT_FLAGS) {
+ wearableBundle.putInt(KEY_FLAGS, mFlags);
+ }
+
+ builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
+ return builder;
+ }
+
+ @Override
+ public WearableExtender clone() {
+ WearableExtender that = new WearableExtender();
+ that.mFlags = this.mFlags;
+ return that;
+ }
+
+ /**
+ * Set whether this action is available when the wearable device is not connected to
+ * a companion device. The user can still trigger this action when the wearable device is
+ * offline, but a visual hint will indicate that the action may not be available.
+ * Defaults to true.
+ */
+ public WearableExtender setAvailableOffline(boolean availableOffline) {
+ setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
+ return this;
+ }
+
+ /**
+ * Get whether this action is available when the wearable device is not connected to
+ * a companion device. The user can still trigger this action when the wearable device is
+ * offline, but a visual hint will indicate that the action may not be available.
+ * Defaults to true.
+ */
+ public boolean isAvailableOffline() {
+ return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
+ }
+
+ private void setFlag(int mask, boolean value) {
+ if (value) {
+ mFlags |= mask;
+ } else {
+ mFlags &= ~mask;
+ }
+ }
+ }
}
/**
@@ -2169,24 +2274,11 @@
* Apply an extender to this notification builder. Extenders may be used to add
* metadata or change options on this builder.
*/
- public Builder apply(Extender extender) {
- extender.applyTo(this);
+ public Builder extend(Extender extender) {
+ extender.extend(this);
return this;
}
- /**
- * Extender interface for use with {@link #apply}. Extenders may be used to add
- * metadata or change options on this builder.
- */
- public interface Extender {
- /**
- * Apply this extender to a notification builder.
- * @param builder the builder to be modified.
- * @return the build object for chaining.
- */
- public Builder applyTo(Builder builder);
- }
-
private void setFlag(int mask, boolean value) {
if (value) {
mFlags |= mask;
@@ -3163,4 +3255,634 @@
return big;
}
}
+
+ /**
+ * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
+ * metadata or change options on a notification builder.
+ */
+ public interface Extender {
+ /**
+ * Apply this extender to a notification builder.
+ * @param builder the builder to be modified.
+ * @return the build object for chaining.
+ */
+ public Builder extend(Builder builder);
+ }
+
+ /**
+ * Helper class to add wearable extensions to notifications.
+ * <p class="note"> See
+ * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
+ * for Android Wear</a> for more information on how to use this class.
+ * <p>
+ * To create a notification with wearable extensions:
+ * <ol>
+ * <li>Create a {@link android.app.Notification.Builder}, setting any desired
+ * properties.
+ * <li>Create a {@link android.app.Notification.WearableExtender}.
+ * <li>Set wearable-specific properties using the
+ * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
+ * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
+ * notification.
+ * <li>Post the notification to the notification system with the
+ * {@code NotificationManager.notify(...)} methods.
+ * </ol>
+ *
+ * <pre class="prettyprint">
+ * Notification notif = new Notification.Builder(mContext)
+ * .setContentTitle("New mail from " + sender.toString())
+ * .setContentText(subject)
+ * .setSmallIcon(R.drawable.new_mail)
+ * .extend(new Notification.WearableExtender()
+ * .setContentIcon(R.drawable.new_mail))
+ * .build();
+ * NotificationManager notificationManger =
+ * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ * notificationManger.notify(0, notif);</pre>
+ *
+ * <p>Wearable extensions can be accessed on an existing notification by using the
+ * {@code WearableExtender(Notification)} constructor,
+ * and then using the {@code get} methods to access values.
+ *
+ * <pre class="prettyprint">
+ * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
+ * notification);
+ * List<Notification> pages = wearableExtender.getPages();
+ * </pre>
+ */
+ public static final class WearableExtender implements Extender {
+ /**
+ * Sentinel value for an action index that is unset.
+ */
+ public static final int UNSET_ACTION_INDEX = -1;
+
+ /**
+ * Size value for use with {@link #setCustomSizePreset} to show this notification with
+ * default sizing.
+ * <p>For custom display notifications created using {@link #setDisplayIntent},
+ * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
+ * on their content.
+ */
+ public static final int SIZE_DEFAULT = 0;
+
+ /**
+ * Size value for use with {@link #setCustomSizePreset} to show this notification
+ * with an extra small size.
+ * <p>This value is only applicable for custom display notifications created using
+ * {@link #setDisplayIntent}.
+ */
+ public static final int SIZE_XSMALL = 1;
+
+ /**
+ * Size value for use with {@link #setCustomSizePreset} to show this notification
+ * with a small size.
+ * <p>This value is only applicable for custom display notifications created using
+ * {@link #setDisplayIntent}.
+ */
+ public static final int SIZE_SMALL = 2;
+
+ /**
+ * Size value for use with {@link #setCustomSizePreset} to show this notification
+ * with a medium size.
+ * <p>This value is only applicable for custom display notifications created using
+ * {@link #setDisplayIntent}.
+ */
+ public static final int SIZE_MEDIUM = 3;
+
+ /**
+ * Size value for use with {@link #setCustomSizePreset} to show this notification
+ * with a large size.
+ * <p>This value is only applicable for custom display notifications created using
+ * {@link #setDisplayIntent}.
+ */
+ public static final int SIZE_LARGE = 4;
+
+ /** Notification extra which contains wearable extensions */
+ private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
+
+ // Keys within EXTRA_WEARABLE_OPTIONS for wearable options.
+ private static final String KEY_ACTIONS = "actions";
+ private static final String KEY_FLAGS = "flags";
+ private static final String KEY_DISPLAY_INTENT = "displayIntent";
+ private static final String KEY_PAGES = "pages";
+ private static final String KEY_BACKGROUND = "background";
+ private static final String KEY_CONTENT_ICON = "contentIcon";
+ private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
+ private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
+ private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
+ private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
+ private static final String KEY_GRAVITY = "gravity";
+
+ // Flags bitwise-ored to mFlags
+ private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
+ private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
+ private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
+ private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
+
+ // Default value for flags integer
+ private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
+
+ private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
+ private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
+
+ private ArrayList<Action> mActions = new ArrayList<Action>();
+ private int mFlags = DEFAULT_FLAGS;
+ private PendingIntent mDisplayIntent;
+ private ArrayList<Notification> mPages = new ArrayList<Notification>();
+ private Bitmap mBackground;
+ private int mContentIcon;
+ private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
+ private int mContentActionIndex = UNSET_ACTION_INDEX;
+ private int mCustomSizePreset = SIZE_DEFAULT;
+ private int mCustomContentHeight;
+ private int mGravity = DEFAULT_GRAVITY;
+
+ /**
+ * Create a {@link android.app.Notification.WearableExtender} with default
+ * options.
+ */
+ public WearableExtender() {
+ }
+
+ public WearableExtender(Notification notif) {
+ Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
+ if (wearableBundle != null) {
+ List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
+ if (actions != null) {
+ mActions.addAll(actions);
+ }
+
+ mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
+ mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
+
+ Notification[] pages = getNotificationArrayFromBundle(
+ wearableBundle, KEY_PAGES);
+ if (pages != null) {
+ Collections.addAll(mPages, pages);
+ }
+
+ mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
+ mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
+ mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
+ DEFAULT_CONTENT_ICON_GRAVITY);
+ mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
+ UNSET_ACTION_INDEX);
+ mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
+ SIZE_DEFAULT);
+ mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
+ mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
+ }
+ }
+
+ /**
+ * Apply wearable extensions to a notification that is being built. This is typically
+ * called by the {@link android.app.Notification.Builder#extend} method of
+ * {@link android.app.Notification.Builder}.
+ */
+ @Override
+ public Notification.Builder extend(Notification.Builder builder) {
+ Bundle wearableBundle = new Bundle();
+
+ if (!mActions.isEmpty()) {
+ wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
+ }
+ if (mFlags != DEFAULT_FLAGS) {
+ wearableBundle.putInt(KEY_FLAGS, mFlags);
+ }
+ if (mDisplayIntent != null) {
+ wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
+ }
+ if (!mPages.isEmpty()) {
+ wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
+ new Notification[mPages.size()]));
+ }
+ if (mBackground != null) {
+ wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
+ }
+ if (mContentIcon != 0) {
+ wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
+ }
+ if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
+ wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
+ }
+ if (mContentActionIndex != UNSET_ACTION_INDEX) {
+ wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
+ mContentActionIndex);
+ }
+ if (mCustomSizePreset != SIZE_DEFAULT) {
+ wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
+ }
+ if (mCustomContentHeight != 0) {
+ wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
+ }
+ if (mGravity != DEFAULT_GRAVITY) {
+ wearableBundle.putInt(KEY_GRAVITY, mGravity);
+ }
+
+ builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
+ return builder;
+ }
+
+ @Override
+ public WearableExtender clone() {
+ WearableExtender that = new WearableExtender();
+ that.mActions = new ArrayList<Action>(this.mActions);
+ that.mFlags = this.mFlags;
+ that.mDisplayIntent = this.mDisplayIntent;
+ that.mPages = new ArrayList<Notification>(this.mPages);
+ that.mBackground = this.mBackground;
+ that.mContentIcon = this.mContentIcon;
+ that.mContentIconGravity = this.mContentIconGravity;
+ that.mContentActionIndex = this.mContentActionIndex;
+ that.mCustomSizePreset = this.mCustomSizePreset;
+ that.mCustomContentHeight = this.mCustomContentHeight;
+ that.mGravity = this.mGravity;
+ return that;
+ }
+
+ /**
+ * Add a wearable action to this notification.
+ *
+ * <p>When wearable actions are added using this method, the set of actions that
+ * show on a wearable device splits from devices that only show actions added
+ * using {@link android.app.Notification.Builder#addAction}. This allows for customization
+ * of which actions display on different devices.
+ *
+ * @param action the action to add to this notification
+ * @return this object for method chaining
+ * @see android.app.Notification.Action
+ */
+ public WearableExtender addAction(Action action) {
+ mActions.add(action);
+ return this;
+ }
+
+ /**
+ * Adds wearable actions to this notification.
+ *
+ * <p>When wearable actions are added using this method, the set of actions that
+ * show on a wearable device splits from devices that only show actions added
+ * using {@link android.app.Notification.Builder#addAction}. This allows for customization
+ * of which actions display on different devices.
+ *
+ * @param actions the actions to add to this notification
+ * @return this object for method chaining
+ * @see android.app.Notification.Action
+ */
+ public WearableExtender addActions(List<Action> actions) {
+ mActions.addAll(actions);
+ return this;
+ }
+
+ /**
+ * Clear all wearable actions present on this builder.
+ * @return this object for method chaining.
+ * @see #addAction
+ */
+ public WearableExtender clearActions() {
+ mActions.clear();
+ return this;
+ }
+
+ /**
+ * Get the wearable actions present on this notification.
+ */
+ public List<Action> getActions() {
+ return mActions;
+ }
+
+ /**
+ * Set an intent to launch inside of an activity view when displaying
+ * this notification. This {@link PendingIntent} should be for an activity.
+ *
+ * @param intent the {@link PendingIntent} for an activity
+ * @return this object for method chaining
+ * @see android.app.Notification.WearableExtender#getDisplayIntent
+ */
+ public WearableExtender setDisplayIntent(PendingIntent intent) {
+ mDisplayIntent = intent;
+ return this;
+ }
+
+ /**
+ * Get the intent to launch inside of an activity view when displaying this
+ * notification. This {@code PendingIntent} should be for an activity.
+ */
+ public PendingIntent getDisplayIntent() {
+ return mDisplayIntent;
+ }
+
+ /**
+ * Add an additional page of content to display with this notification. The current
+ * notification forms the first page, and pages added using this function form
+ * subsequent pages. This field can be used to separate a notification into multiple
+ * sections.
+ *
+ * @param page the notification to add as another page
+ * @return this object for method chaining
+ * @see android.app.Notification.WearableExtender#getPages
+ */
+ public WearableExtender addPage(Notification page) {
+ mPages.add(page);
+ return this;
+ }
+
+ /**
+ * Add additional pages of content to display with this notification. The current
+ * notification forms the first page, and pages added using this function form
+ * subsequent pages. This field can be used to separate a notification into multiple
+ * sections.
+ *
+ * @param pages a list of notifications
+ * @return this object for method chaining
+ * @see android.app.Notification.WearableExtender#getPages
+ */
+ public WearableExtender addPages(List<Notification> pages) {
+ mPages.addAll(pages);
+ return this;
+ }
+
+ /**
+ * Clear all additional pages present on this builder.
+ * @return this object for method chaining.
+ * @see #addPage
+ */
+ public WearableExtender clearPages() {
+ mPages.clear();
+ return this;
+ }
+
+ /**
+ * Get the array of additional pages of content for displaying this notification. The
+ * current notification forms the first page, and elements within this array form
+ * subsequent pages. This field can be used to separate a notification into multiple
+ * sections.
+ * @return the pages for this notification
+ */
+ public List<Notification> getPages() {
+ return mPages;
+ }
+
+ /**
+ * Set a background image to be displayed behind the notification content.
+ * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
+ * will work with any notification style.
+ *
+ * @param background the background bitmap
+ * @return this object for method chaining
+ * @see android.app.Notification.WearableExtender#getBackground
+ */
+ public WearableExtender setBackground(Bitmap background) {
+ mBackground = background;
+ return this;
+ }
+
+ /**
+ * Get a background image to be displayed behind the notification content.
+ * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
+ * will work with any notification style.
+ *
+ * @return the background image
+ * @see android.app.Notification.WearableExtender#setBackground
+ */
+ public Bitmap getBackground() {
+ return mBackground;
+ }
+
+ /**
+ * Set an icon that goes with the content of this notification.
+ */
+ public WearableExtender setContentIcon(int icon) {
+ mContentIcon = icon;
+ return this;
+ }
+
+ /**
+ * Get an icon that goes with the content of this notification.
+ */
+ public int getContentIcon() {
+ return mContentIcon;
+ }
+
+ /**
+ * Set the gravity that the content icon should have within the notification display.
+ * Supported values include {@link android.view.Gravity#START} and
+ * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
+ * @see #setContentIcon
+ */
+ public WearableExtender setContentIconGravity(int contentIconGravity) {
+ mContentIconGravity = contentIconGravity;
+ return this;
+ }
+
+ /**
+ * Get the gravity that the content icon should have within the notification display.
+ * Supported values include {@link android.view.Gravity#START} and
+ * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
+ * @see #getContentIcon
+ */
+ public int getContentIconGravity() {
+ return mContentIconGravity;
+ }
+
+ /**
+ * Set an action from this notification's actions to be clickable with the content of
+ * this notification page. This action will no longer display separately from the
+ * notification content. This action's icon will display with optional subtext provided
+ * by the action's title.
+ * @param actionIndex The index of the action to hoist on the current notification page.
+ * If wearable actions are present, this index will apply to that list,
+ * otherwise it will apply to the main notification's actions list.
+ */
+ public WearableExtender setContentAction(int actionIndex) {
+ mContentActionIndex = actionIndex;
+ return this;
+ }
+
+ /**
+ * Get the action index of an action from this notification to show as clickable with
+ * the content of this notification page. When the user clicks this notification page,
+ * this action will trigger. This action will no longer display separately from the
+ * notification content. The action's icon will display with optional subtext provided
+ * by the action's title.
+ *
+ * <p>If wearable specific actions are present, this index will apply to that list,
+ * otherwise it will apply to the main notification's actions list.
+ */
+ public int getContentAction() {
+ return mContentActionIndex;
+ }
+
+ /**
+ * Set the gravity that this notification should have within the available viewport space.
+ * Supported values include {@link android.view.Gravity#TOP},
+ * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
+ * The default value is {@link android.view.Gravity#BOTTOM}.
+ */
+ public WearableExtender setGravity(int gravity) {
+ mGravity = gravity;
+ return this;
+ }
+
+ /**
+ * Get the gravity that this notification should have within the available viewport space.
+ * Supported values include {@link android.view.Gravity#TOP},
+ * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
+ * The default value is {@link android.view.Gravity#BOTTOM}.
+ */
+ public int getGravity() {
+ return mGravity;
+ }
+
+ /**
+ * Set the custom size preset for the display of this notification out of the available
+ * presets found in {@link android.app.Notification.WearableExtender}, e.g.
+ * {@link #SIZE_LARGE}.
+ * <p>Some custom size presets are only applicable for custom display notifications created
+ * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
+ * documentation for the preset in question. See also
+ * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
+ */
+ public WearableExtender setCustomSizePreset(int sizePreset) {
+ mCustomSizePreset = sizePreset;
+ return this;
+ }
+
+ /**
+ * Get the custom size preset for the display of this notification out of the available
+ * presets found in {@link android.app.Notification.WearableExtender}, e.g.
+ * {@link #SIZE_LARGE}.
+ * <p>Some custom size presets are only applicable for custom display notifications created
+ * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
+ * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
+ */
+ public int getCustomSizePreset() {
+ return mCustomSizePreset;
+ }
+
+ /**
+ * Set the custom height in pixels for the display of this notification's content.
+ * <p>This option is only available for custom display notifications created
+ * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
+ * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
+ * {@link #getCustomContentHeight}.
+ */
+ public WearableExtender setCustomContentHeight(int height) {
+ mCustomContentHeight = height;
+ return this;
+ }
+
+ /**
+ * Get the custom height in pixels for the display of this notification's content.
+ * <p>This option is only available for custom display notifications created
+ * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
+ * {@link #setCustomContentHeight}.
+ */
+ public int getCustomContentHeight() {
+ return mCustomContentHeight;
+ }
+
+ /**
+ * Set whether the scrolling position for the contents of this notification should start
+ * at the bottom of the contents instead of the top when the contents are too long to
+ * display within the screen. Default is false (start scroll at the top).
+ */
+ public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
+ setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
+ return this;
+ }
+
+ /**
+ * Get whether the scrolling position for the contents of this notification should start
+ * at the bottom of the contents instead of the top when the contents are too long to
+ * display within the screen. Default is false (start scroll at the top).
+ */
+ public boolean getStartScrollBottom() {
+ return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
+ }
+
+ /**
+ * Set whether the content intent is available when the wearable device is not connected
+ * to a companion device. The user can still trigger this intent when the wearable device
+ * is offline, but a visual hint will indicate that the content intent may not be available.
+ * Defaults to true.
+ */
+ public WearableExtender setContentIntentAvailableOffline(
+ boolean contentIntentAvailableOffline) {
+ setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
+ return this;
+ }
+
+ /**
+ * Get whether the content intent is available when the wearable device is not connected
+ * to a companion device. The user can still trigger this intent when the wearable device
+ * is offline, but a visual hint will indicate that the content intent may not be available.
+ * Defaults to true.
+ */
+ public boolean getContentIntentAvailableOffline() {
+ return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
+ }
+
+ /**
+ * Set a hint that this notification's icon should not be displayed.
+ * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
+ * @return this object for method chaining
+ */
+ public WearableExtender setHintHideIcon(boolean hintHideIcon) {
+ setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
+ return this;
+ }
+
+ /**
+ * Get a hint that this notification's icon should not be displayed.
+ * @return {@code true} if this icon should not be displayed, false otherwise.
+ * The default value is {@code false} if this was never set.
+ */
+ public boolean getHintHideIcon() {
+ return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
+ }
+
+ /**
+ * Set a visual hint that only the background image of this notification should be
+ * displayed, and other semantic content should be hidden. This hint is only applicable
+ * to sub-pages added using {@link #addPage}.
+ */
+ public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
+ setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
+ return this;
+ }
+
+ /**
+ * Get a visual hint that only the background image of this notification should be
+ * displayed, and other semantic content should be hidden. This hint is only applicable
+ * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
+ */
+ public boolean getHintShowBackgroundOnly() {
+ return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
+ }
+
+ private void setFlag(int mask, boolean value) {
+ if (value) {
+ mFlags |= mask;
+ } else {
+ mFlags &= ~mask;
+ }
+ }
+ }
+
+ /**
+ * Get an array of Notification objects from a parcelable array bundle field.
+ * Update the bundle to have a typed array so fetches in the future don't need
+ * to do an array copy.
+ */
+ private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
+ Parcelable[] array = bundle.getParcelableArray(key);
+ if (array instanceof Notification[] || array == null) {
+ return (Notification[]) array;
+ }
+ Notification[] typedArray = Arrays.copyOf(array, array.length,
+ Notification[].class);
+ bundle.putParcelableArray(key, typedArray);
+ return typedArray;
+ }
}
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
index 9cfc541..11420c5 100644
--- a/core/java/android/app/RemoteInput.java
+++ b/core/java/android/app/RemoteInput.java
@@ -64,18 +64,24 @@
/** Extra added to a clip data intent object to hold the results bundle. */
public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+ // Flags bitwise-ored to mFlags
+ private static final int FLAG_ALLOW_FREE_FORM_INPUT = 0x1;
+
+ // Default value for flags integer
+ private static final int DEFAULT_FLAGS = FLAG_ALLOW_FREE_FORM_INPUT;
+
private final String mResultKey;
private final CharSequence mLabel;
private final CharSequence[] mChoices;
- private final boolean mAllowFreeFormInput;
+ private final int mFlags;
private final Bundle mExtras;
private RemoteInput(String resultKey, CharSequence label, CharSequence[] choices,
- boolean allowFreeFormInput, Bundle extras) {
+ int flags, Bundle extras) {
this.mResultKey = resultKey;
this.mLabel = label;
this.mChoices = choices;
- this.mAllowFreeFormInput = allowFreeFormInput;
+ this.mFlags = flags;
this.mExtras = extras;
}
@@ -108,7 +114,7 @@
* if you set this to false and {@link #getChoices} returns {@code null} or empty.
*/
public boolean getAllowFreeFormInput() {
- return mAllowFreeFormInput;
+ return (mFlags & FLAG_ALLOW_FREE_FORM_INPUT) != 0;
}
/**
@@ -125,7 +131,7 @@
private final String mResultKey;
private CharSequence mLabel;
private CharSequence[] mChoices;
- private boolean mAllowFreeFormInput = true;
+ private int mFlags = DEFAULT_FLAGS;
private Bundle mExtras = new Bundle();
/**
@@ -178,7 +184,7 @@
* @return this object for method chaining
*/
public Builder setAllowFreeFormInput(boolean allowFreeFormInput) {
- mAllowFreeFormInput = allowFreeFormInput;
+ setFlag(mFlags, allowFreeFormInput);
return this;
}
@@ -205,12 +211,20 @@
return mExtras;
}
+ private void setFlag(int mask, boolean value) {
+ if (value) {
+ mFlags |= mask;
+ } else {
+ mFlags &= ~mask;
+ }
+ }
+
/**
* Combine all of the options that have been set and return a new {@link RemoteInput}
* object.
*/
public RemoteInput build() {
- return new RemoteInput(mResultKey, mLabel, mChoices, mAllowFreeFormInput, mExtras);
+ return new RemoteInput(mResultKey, mLabel, mChoices, mFlags, mExtras);
}
}
@@ -218,7 +232,7 @@
mResultKey = in.readString();
mLabel = in.readCharSequence();
mChoices = in.readCharSequenceArray();
- mAllowFreeFormInput = in.readInt() != 0;
+ mFlags = in.readInt();
mExtras = in.readBundle();
}
@@ -279,7 +293,7 @@
out.writeString(mResultKey);
out.writeCharSequence(mLabel);
out.writeCharSequenceArray(mChoices);
- out.writeInt(mAllowFreeFormInput ? 1 : 0);
+ out.writeInt(mFlags);
out.writeBundle(mExtras);
}
diff --git a/core/java/android/app/TaskManagerImpl.java b/core/java/android/app/TaskManagerImpl.java
new file mode 100644
index 0000000..f42839e
--- /dev/null
+++ b/core/java/android/app/TaskManagerImpl.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+// in android.app so ContextImpl has package access
+package android.app;
+
+import android.app.task.ITaskManager;
+import android.app.task.Task;
+import android.app.task.TaskManager;
+
+import java.util.List;
+
+
+/**
+ * Concrete implementation of the TaskManager interface
+ * @hide
+ */
+public class TaskManagerImpl extends TaskManager {
+ ITaskManager mBinder;
+
+ /* package */ TaskManagerImpl(ITaskManager binder) {
+ mBinder = binder;
+ }
+
+ @Override
+ public int schedule(Task task) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public void cancel(int taskId) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void cancelAll() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public List<Task> getAllPendingTasks() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/core/java/android/app/task/ITaskManager.aidl b/core/java/android/app/task/ITaskManager.aidl
new file mode 100644
index 0000000..b56c78a
--- /dev/null
+++ b/core/java/android/app/task/ITaskManager.aidl
@@ -0,0 +1,30 @@
+/**
+ * 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.app.task;
+
+import android.app.task.Task;
+
+ /**
+ * IPC interface that supports the app-facing {@link #TaskManager} api.
+ * {@hide}
+ */
+interface ITaskManager {
+ int schedule(in Task task);
+ void cancel(int taskId);
+ void cancelAll();
+ List<Task> getAllPendingTasks();
+}
diff --git a/core/java/android/app/task/Task.aidl b/core/java/android/app/task/Task.aidl
new file mode 100644
index 0000000..1f25439
--- /dev/null
+++ b/core/java/android/app/task/Task.aidl
@@ -0,0 +1,20 @@
+/**
+ * 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.app.task;
+
+parcelable Task;
+
\ No newline at end of file
diff --git a/core/java/android/content/Task.java b/core/java/android/app/task/Task.java
similarity index 96%
rename from core/java/android/content/Task.java
rename to core/java/android/app/task/Task.java
index 407880f..dd184a5 100644
--- a/core/java/android/content/Task.java
+++ b/core/java/android/app/task/Task.java
@@ -14,15 +14,15 @@
* limitations under the License
*/
-package android.content;
+package android.app.task;
-import android.app.task.TaskService;
+import android.content.ComponentName;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
/**
- * Container of data passed to the {@link android.content.TaskManager} fully encapsulating the
+ * Container of data passed to the {@link android.app.task.TaskManager} fully encapsulating the
* parameters required to schedule work against the calling application. These are constructed
* using the {@link Task.Builder}.
*/
@@ -92,7 +92,7 @@
}
/**
- * See {@link android.content.Task.NetworkType} for a description of this value.
+ * See {@link android.app.task.Task.NetworkType} for a description of this value.
*/
public int getNetworkCapabilities() {
return networkCapabilities;
@@ -139,7 +139,7 @@
}
/**
- * See {@link android.content.Task.BackoffPolicy} for an explanation of the values this field
+ * See {@link android.app.task.Task.BackoffPolicy} for an explanation of the values this field
* can take. This defaults to exponential.
*/
public int getBackoffPolicy() {
@@ -255,7 +255,7 @@
/**
* Set some description of the kind of network capabilities you would like to have. This
- * will be a parameter defined in {@link android.content.Task.NetworkType}.
+ * will be a parameter defined in {@link android.app.task.Task.NetworkType}.
* Not calling this function means the network is not necessary.
* Bear in mind that calling this function defines network as a strict requirement for your
* task if the network requested is not available your task will never run. See
@@ -314,7 +314,7 @@
* Specify that this task should be delayed by the provided amount of time.
* Because it doesn't make sense setting this property on a periodic task, doing so will
* throw an {@link java.lang.IllegalArgumentException} when
- * {@link android.content.Task.Builder#build()} is called.
+ * {@link android.app.task.Task.Builder#build()} is called.
* @param minLatencyMillis Milliseconds before which this task will not be considered for
* execution.
*/
@@ -328,7 +328,7 @@
* deadline even if other requirements are not met. Because it doesn't make sense setting
* this property on a periodic task, doing so will throw an
* {@link java.lang.IllegalArgumentException} when
- * {@link android.content.Task.Builder#build()} is called.
+ * {@link android.app.task.Task.Builder#build()} is called.
*/
public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
mMaxExecutionDelayMillis = maxExecutionDelayMillis;
diff --git a/core/java/android/content/TaskManager.java b/core/java/android/app/task/TaskManager.java
similarity index 75%
rename from core/java/android/content/TaskManager.java
rename to core/java/android/app/task/TaskManager.java
index d28d78a..0fbe37d 100644
--- a/core/java/android/content/TaskManager.java
+++ b/core/java/android/app/task/TaskManager.java
@@ -14,14 +14,19 @@
* limitations under the License
*/
-package android.content;
+package android.app.task;
import java.util.List;
+import android.content.Context;
+
/**
* Class for scheduling various types of tasks with the scheduling framework on the device.
*
- * Get an instance of this class through {@link Context#getSystemService(String)}.
+ * <p>You do not
+ * instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.TASK_SERVICE)}.
*/
public abstract class TaskManager {
/*
@@ -29,18 +34,20 @@
* if the run-time for your task is too short, or perhaps the system can't resolve the
* requisite {@link TaskService} in your package.
*/
- static final int RESULT_INVALID_PARAMETERS = -1;
+ public static final int RESULT_INVALID_PARAMETERS = -1;
+
/**
* Returned from {@link #schedule(Task)} if this application has made too many requests for
* work over too short a time.
*/
// TODO: Determine if this is necessary.
- static final int RESULT_OVER_QUOTA = -2;
+ public static final int RESULT_OVER_QUOTA = -2;
- /*
- * @param task The task you wish scheduled. See {@link Task#TaskBuilder} for more detail on
- * the sorts of tasks you can schedule.
- * @return If >0, this int corresponds to the taskId of the successfully scheduled task.
+ /**
+ * @param task The task you wish scheduled. See
+ * {@link android.app.task.Task.Builder Task.Builder} for more detail on the sorts of tasks
+ * you can schedule.
+ * @return If >0, this int returns the taskId of the successfully scheduled task.
* Otherwise you have to compare the return value to the error codes defined in this class.
*/
public abstract int schedule(Task task);
diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/task/TaskParams.java
index 0351082..dacb3480 100644
--- a/core/java/android/app/task/TaskParams.java
+++ b/core/java/android/app/task/TaskParams.java
@@ -47,7 +47,7 @@
/**
* @return The extras you passed in when constructing this task with
- * {@link android.content.Task.Builder#setExtras(android.os.Bundle)}. This will
+ * {@link android.app.task.Task.Builder#setExtras(android.os.Bundle)}. This will
* never be null. If you did not set any extras this will be an empty bundle.
*/
public Bundle getExtras() {
diff --git a/core/java/android/app/task/TaskService.java b/core/java/android/app/task/TaskService.java
index ab1a565..8ce4484 100644
--- a/core/java/android/app/task/TaskService.java
+++ b/core/java/android/app/task/TaskService.java
@@ -28,7 +28,7 @@
import com.android.internal.annotations.GuardedBy;
/**
- * <p>Entry point for the callback from the {@link android.content.TaskManager}.</p>
+ * <p>Entry point for the callback from the {@link android.app.task.TaskManager}.</p>
* <p>This is the base class that handles asynchronous requests that were previously scheduled. You
* are responsible for overriding {@link TaskService#onStartTask(TaskParams)}, which is where
* you will implement your task logic.</p>
@@ -215,9 +215,9 @@
*
* <p>This will happen if the requirements specified at schedule time are no longer met. For
* example you may have requested WiFi with
- * {@link android.content.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your
+ * {@link android.app.task.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your
* task was executing the user toggled WiFi. Another example is if you had specified
- * {@link android.content.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
+ * {@link android.app.task.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
* idle maintenance window. You are solely responsible for the behaviour of your application
* upon receipt of this message; your app will likely start to misbehave if you ignore it. One
* immediate repercussion is that the system will cease holding a wakelock for you.</p>
@@ -237,7 +237,7 @@
* You can specify post-execution behaviour to the scheduler here with
* <code>needsReschedule </code>. This will apply a back-off timer to your task based on
* the default, or what was set with
- * {@link android.content.Task.Builder#setBackoffCriteria(long, int)}. The original
+ * {@link android.app.task.Task.Builder#setBackoffCriteria(long, int)}. The original
* requirements are always honoured even for a backed-off task. Note that a task running in
* idle mode will not be backed-off. Instead what will happen is the task will be re-added
* to the queue and re-executed within a future idle maintenance window.
diff --git a/core/java/android/app/wearable/WearableActionExtensions.java b/core/java/android/app/wearable/WearableActionExtensions.java
deleted file mode 100644
index c296ef2..0000000
--- a/core/java/android/app/wearable/WearableActionExtensions.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * 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.app.wearable;
-
-import android.app.Notification;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Wearable extensions to notification actions. To add extensions to an action,
- * create a new {@link WearableActionExtensions} object using
- * {@link WearableActionExtensions.Builder} and apply it to a
- * {@link android.app.Notification.Action.Builder}.
- *
- * <pre class="prettyprint">
- * Notification.Action action = new Notification.Action.Builder(
- * R.drawable.archive_all, "Archive all", actionIntent)
- * .apply(new WearableActionExtensions.Builder()
- * .setAvailableOffline(false)
- * .build())
- * .build();
- * </pre>
- */
-public final class WearableActionExtensions implements Notification.Action.Builder.Extender,
- Parcelable {
- /** Notification action extra which contains wearable extensions */
- private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
-
- // Flags bitwise-ored to mFlags
- private static final int FLAG_AVAILABLE_OFFLINE = 1 << 0;
-
- // Default value for flags integer
- private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
-
- private final int mFlags;
-
- private WearableActionExtensions(int flags) {
- mFlags = flags;
- }
-
- private WearableActionExtensions(Parcel in) {
- mFlags = in.readInt();
- }
-
- /**
- * Create a {@link WearableActionExtensions} by reading wearable extensions present on an
- * existing notification action.
- * @param action the notification action to inspect.
- * @return a new {@link WearableActionExtensions} object.
- */
- public static WearableActionExtensions from(Notification.Action action) {
- WearableActionExtensions extensions = action.getExtras().getParcelable(
- EXTRA_WEARABLE_EXTENSIONS);
- if (extensions != null) {
- return extensions;
- } else {
- // Return a WearableActionExtensions with default values.
- return new Builder().build();
- }
- }
-
- /**
- * Get whether this action is available when the wearable device is not connected to
- * a companion device. The user can still trigger this action when the wearable device is
- * offline, but a visual hint will indicate that the action may not be available.
- * Defaults to true.
- */
- public boolean isAvailableOffline() {
- return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
- }
-
- @Override
- public Notification.Action.Builder applyTo(Notification.Action.Builder builder) {
- builder.getExtras().putParcelable(EXTRA_WEARABLE_EXTENSIONS, this);
- return builder;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(mFlags);
- }
-
- /**
- * Builder for {@link WearableActionExtensions} objects, which adds wearable extensions to
- * notification actions. To extend an action, create an instance of this class, call the set
- * methods present, call {@link #build}, and finally apply the options to a
- * {@link Notification.Action.Builder} using its
- * {@link android.app.Notification.Action.Builder#apply} method.
- */
- public static final class Builder {
- private int mFlags = DEFAULT_FLAGS;
-
- /**
- * Construct a builder to be used for adding wearable extensions to notification actions.
- *
- * <pre class="prettyprint">
- * Notification.Action action = new Notification.Action.Builder(
- * R.drawable.archive_all, "Archive all", actionIntent)
- * .apply(new WearableActionExtensions.Builder()
- * .setAvailableOffline(false)
- * .build())
- * .build();</pre>
- */
- public Builder() {
- }
-
- /**
- * Create a {@link Builder} by reading wearable extensions present on an
- * existing {@code WearableActionExtensions} object.
- * @param other the existing extensions to inspect.
- */
- public Builder(WearableActionExtensions other) {
- mFlags = other.mFlags;
- }
-
- /**
- * Set whether this action is available when the wearable device is not connected to
- * a companion device. The user can still trigger this action when the wearable device is
- * offline, but a visual hint will indicate that the action may not be available.
- * Defaults to true.
- */
- public Builder setAvailableOffline(boolean availableOffline) {
- setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
- return this;
- }
-
- /**
- * Build a new {@link WearableActionExtensions} object with the extensions
- * currently present on this builder.
- * @return the extensions object.
- */
- public WearableActionExtensions build() {
- return new WearableActionExtensions(mFlags);
- }
-
- private void setFlag(int mask, boolean value) {
- if (value) {
- mFlags |= mask;
- } else {
- mFlags &= ~mask;
- }
- }
- }
-
- public static final Creator<WearableActionExtensions> CREATOR =
- new Creator<WearableActionExtensions>() {
- @Override
- public WearableActionExtensions createFromParcel(Parcel in) {
- return new WearableActionExtensions(in);
- }
-
- @Override
- public WearableActionExtensions[] newArray(int size) {
- return new WearableActionExtensions[size];
- }
- };
-}
diff --git a/core/java/android/app/wearable/WearableNotificationExtensions.java b/core/java/android/app/wearable/WearableNotificationExtensions.java
deleted file mode 100644
index d433613..0000000
--- a/core/java/android/app/wearable/WearableNotificationExtensions.java
+++ /dev/null
@@ -1,702 +0,0 @@
-/*
- * 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.app.wearable;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.graphics.Bitmap;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.Gravity;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Helper class that contains wearable extensions for notifications.
- * <p class="note"> See
- * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
- * for Android Wear</a> for more information on how to use this class.
- * <p>
- * To create a notification with wearable extensions:
- * <ol>
- * <li>Create a {@link Notification.Builder}, setting any desired
- * properties.
- * <li>Create a {@link WearableNotificationExtensions.Builder}.
- * <li>Set wearable-specific properties using the
- * {@code add} and {@code set} methods of {@link WearableNotificationExtensions.Builder}.
- * <li>Call {@link WearableNotificationExtensions.Builder#build} to build the extensions
- * object.
- * <li>Call {@link Notification.Builder#apply} to apply the extensions to a notification.
- * <li>Post the notification to the notification system with the
- * {@code NotificationManager.notify(...)} methods.
- * </ol>
- *
- * <pre class="prettyprint">
- * Notification notif = new Notification.Builder(mContext)
- * .setContentTitle("New mail from " + sender.toString())
- * .setContentText(subject)
- * .setSmallIcon(R.drawable.new_mail)
- * .apply(new new WearableNotificationExtensions.Builder()
- * .setContentIcon(R.drawable.new_mail)
- * .build())
- * .build();
- * NotificationManager notificationManger =
- * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- * notificationManger.notify(0, notif);</pre>
- *
- * <p>Wearable extensions can be accessed on an existing notification by using the
- * {@link WearableNotificationExtensions#from} function.
- *
- * <pre class="prettyprint">
- * WearableNotificationExtensions wearableExtensions = WearableNotificationExtensions.from(
- * notification);
- * Notification[] pages = wearableExtensions.getPages();
- * </pre>
- */
-public final class WearableNotificationExtensions implements Notification.Builder.Extender,
- Parcelable {
- /**
- * Sentinel value for an action index that is unset.
- */
- public static final int UNSET_ACTION_INDEX = -1;
-
- /**
- * Size value for use with {@link Builder#setCustomSizePreset} to show this notification with
- * default sizing.
- * <p>For custom display notifications created using {@link Builder#setDisplayIntent},
- * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
- * on their content.
- */
- public static final int SIZE_DEFAULT = 0;
-
- /**
- * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
- * with an extra small size.
- * <p>This value is only applicable for custom display notifications created using
- * {@link Builder#setDisplayIntent}.
- */
- public static final int SIZE_XSMALL = 1;
-
- /**
- * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
- * with a small size.
- * <p>This value is only applicable for custom display notifications created using
- * {@link Builder#setDisplayIntent}.
- */
- public static final int SIZE_SMALL = 2;
-
- /**
- * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
- * with a medium size.
- * <p>This value is only applicable for custom display notifications created using
- * {@link Builder#setDisplayIntent}.
- */
- public static final int SIZE_MEDIUM = 3;
-
- /**
- * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
- * with a large size.
- * <p>This value is only applicable for custom display notifications created using
- * {@link Builder#setDisplayIntent}.
- */
- public static final int SIZE_LARGE = 4;
-
- /** Notification extra which contains wearable extensions */
- static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
-
- // Flags bitwise-ored to mFlags
- static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 1 << 0;
- static final int FLAG_HINT_HIDE_ICON = 1 << 1;
- static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
- static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
-
- // Default value for flags integer
- static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
-
- private final Notification.Action[] mActions;
- private final int mFlags;
- private final PendingIntent mDisplayIntent;
- private final Notification[] mPages;
- private final Bitmap mBackground;
- private final int mContentIcon;
- private final int mContentIconGravity;
- private final int mContentActionIndex;
- private final int mCustomSizePreset;
- private final int mCustomContentHeight;
- private final int mGravity;
-
- private WearableNotificationExtensions(Notification.Action[] actions, int flags,
- PendingIntent displayIntent, Notification[] pages, Bitmap background,
- int contentIcon, int contentIconGravity, int contentActionIndex,
- int customSizePreset, int customContentHeight, int gravity) {
- mActions = actions;
- mFlags = flags;
- mDisplayIntent = displayIntent;
- mPages = pages;
- mBackground = background;
- mContentIcon = contentIcon;
- mContentIconGravity = contentIconGravity;
- mContentActionIndex = contentActionIndex;
- mCustomSizePreset = customSizePreset;
- mCustomContentHeight = customContentHeight;
- mGravity = gravity;
- }
-
- private WearableNotificationExtensions(Parcel in) {
- mActions = in.createTypedArray(Notification.Action.CREATOR);
- mFlags = in.readInt();
- mDisplayIntent = in.readParcelable(PendingIntent.class.getClassLoader());
- mPages = in.createTypedArray(Notification.CREATOR);
- mBackground = in.readParcelable(Bitmap.class.getClassLoader());
- mContentIcon = in.readInt();
- mContentIconGravity = in.readInt();
- mContentActionIndex = in.readInt();
- mCustomSizePreset = in.readInt();
- mCustomContentHeight = in.readInt();
- mGravity = in.readInt();
- }
-
- /**
- * Create a {@link WearableNotificationExtensions} by reading wearable extensions present on an
- * existing notification.
- * @param notif the notification to inspect.
- * @return a new {@link WearableNotificationExtensions} object.
- */
- public static WearableNotificationExtensions from(Notification notif) {
- WearableNotificationExtensions extensions = notif.extras.getParcelable(
- EXTRA_WEARABLE_EXTENSIONS);
- if (extensions != null) {
- return extensions;
- } else {
- // Return a WearableNotificationExtensions with default values.
- return new Builder().build();
- }
- }
-
- /**
- * Apply wearable extensions to a notification that is being built. This is typically
- * called by {@link Notification.Builder#apply} method of {@link Notification.Builder}.
- */
- @Override
- public Notification.Builder applyTo(Notification.Builder builder) {
- builder.getExtras().putParcelable(EXTRA_WEARABLE_EXTENSIONS, this);
- return builder;
- }
-
- /**
- * Get the number of wearable actions present on this notification.
- *
- * @return the number of wearable actions for this notification
- */
- public int getActionCount() {
- return mActions.length;
- }
-
- /**
- * Get a {@link Notification.Action} for the wearable action at {@code actionIndex}.
- * @param actionIndex the index of the desired wearable action
- */
- public Notification.Action getAction(int actionIndex) {
- return mActions[actionIndex];
- }
-
- /**
- * Get the wearable actions present on this notification.
- */
- public Notification.Action[] getActions() {
- return mActions;
- }
-
- /**
- * Get the intent to launch inside of an activity view when displaying this
- * notification. This {@code PendingIntent} should be for an activity.
- */
- public PendingIntent getDisplayIntent() {
- return mDisplayIntent;
- }
-
- /**
- * Get the array of additional pages of content for displaying this notification. The
- * current notification forms the first page, and elements within this array form
- * subsequent pages. This field can be used to separate a notification into multiple
- * sections.
- * @return the pages for this notification
- */
- public Notification[] getPages() {
- return mPages;
- }
-
- /**
- * Get a background image to be displayed behind the notification content.
- * Contrary to the {@link Notification.BigPictureStyle}, this background
- * will work with any notification style.
- *
- * @return the background image
- * @see Builder#setBackground
- */
- public Bitmap getBackground() {
- return mBackground;
- }
-
- /**
- * Get an icon that goes with the content of this notification.
- */
- public int getContentIcon() {
- return mContentIcon;
- }
-
- /**
- * Get the gravity that the content icon should have within the notification display.
- * Supported values include {@link Gravity#START} and {@link Gravity#END}. The default
- * value is {@link android.view.Gravity#END}.
- * @see #getContentIcon
- */
- public int getContentIconGravity() {
- return mContentIconGravity;
- }
-
- /**
- * Get the action index of an action from this notification to show as clickable with
- * the content of this notification page. When the user clicks this notification page,
- * this action will trigger. This action will no longer display separately from the
- * notification content. The action's icon will display with optional subtext provided
- * by the action's title.
- *
- * <p>If wearable specific actions are present, this index will apply to that list,
- * otherwise it will apply to the main notification's actions list.
- */
- public int getContentAction() {
- return mContentActionIndex;
- }
-
- /**
- * Get the gravity that this notification should have within the available viewport space.
- * Supported values include {@link Gravity#TOP}, {@link Gravity#CENTER_VERTICAL} and
- * {@link android.view.Gravity#BOTTOM}. The default value is
- * {@link android.view.Gravity#BOTTOM}.
- */
- public int getGravity() {
- return mGravity;
- }
-
- /**
- * Get the custom size preset for the display of this notification out of the available
- * presets found in {@link WearableNotificationExtensions}, e.g. {@link #SIZE_LARGE}.
- * <p>Some custom size presets are only applicable for custom display notifications created
- * using {@link Builder#setDisplayIntent}. Check the documentation for the preset in question.
- * See also {@link Builder#setCustomContentHeight} and {@link Builder#setCustomSizePreset}.
- */
- public int getCustomSizePreset() {
- return mCustomSizePreset;
- }
-
- /**
- * Get the custom height in pixels for the display of this notification's content.
- * <p>This option is only available for custom display notifications created
- * using {@link Builder#setDisplayIntent}. See also {@link Builder#setCustomSizePreset} and
- * {@link Builder#setCustomContentHeight}.
- */
- public int getCustomContentHeight() {
- return mCustomContentHeight;
- }
-
- /**
- * Get whether the scrolling position for the contents of this notification should start
- * at the bottom of the contents instead of the top when the contents are too long to
- * display within the screen. Default is false (start scroll at the top).
- */
- public boolean getStartScrollBottom() {
- return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
- }
-
- /**
- * Get whether the content intent is available when the wearable device is not connected
- * to a companion device. The user can still trigger this intent when the wearable device is
- * offline, but a visual hint will indicate that the content intent may not be available.
- * Defaults to true.
- */
- public boolean getContentIntentAvailableOffline() {
- return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
- }
-
- /**
- * Get a hint that this notification's icon should not be displayed.
- * @return {@code true} if this icon should not be displayed, false otherwise.
- * The default value is {@code false} if this was never set.
- */
- public boolean getHintHideIcon() {
- return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
- }
-
- /**
- * Get a visual hint that only the background image of this notification should be
- * displayed, and other semantic content should be hidden. This hint is only applicable
- * to sub-pages added using {@link Builder#addPage}.
- */
- public boolean getHintShowBackgroundOnly() {
- return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeTypedArray(mActions, flags);
- out.writeInt(mFlags);
- out.writeParcelable(mDisplayIntent, flags);
- out.writeTypedArray(mPages, flags);
- out.writeParcelable(mBackground, flags);
- out.writeInt(mContentIcon);
- out.writeInt(mContentIconGravity);
- out.writeInt(mContentActionIndex);
- out.writeInt(mCustomSizePreset);
- out.writeInt(mCustomContentHeight);
- out.writeInt(mGravity);
- }
-
- /**
- * Builder to apply wearable notification extensions to a {@link Notification.Builder}
- * object.
- *
- * <p>You can chain the "set" methods for this builder in any order,
- * but you must call the {@link #build} method and then the {@link Notification.Builder#apply}
- * method to apply your extensions to a notification.
- *
- * <pre class="prettyprint">
- * Notification notif = new Notification.Builder(mContext)
- * .setContentTitle("New mail from " + sender.toString())
- * .setContentText(subject)
- * .setSmallIcon(R.drawable.new_mail);
- * .apply(new WearableNotificationExtensions.Builder()
- * .setContentIcon(R.drawable.new_mail)
- * .build())
- * .build();
- * NotificationManager notificationManger =
- * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- * notificationManager.notify(0, notif);</pre>
- */
- public static final class Builder {
- private final List<Notification.Action> mActions =
- new ArrayList<Notification.Action>();
- private int mFlags = DEFAULT_FLAGS;
- private PendingIntent mDisplayIntent;
- private final List<Notification> mPages = new ArrayList<Notification>();
- private Bitmap mBackground;
- private int mContentIcon;
- private int mContentIconGravity = Gravity.END;
- private int mContentActionIndex = UNSET_ACTION_INDEX;
- private int mCustomContentHeight;
- private int mCustomSizePreset = SIZE_DEFAULT;
- private int mGravity = Gravity.BOTTOM;
-
- /**
- * Construct a builder to be used for adding wearable extensions to notifications.
- *
- * <pre class="prettyprint">
- * Notification notif = new Notification.Builder(mContext)
- * .setContentTitle("New mail from " + sender.toString())
- * .setContentText(subject)
- * .setSmallIcon(R.drawable.new_mail);
- * .apply(new WearableNotificationExtensions.Builder()
- * .setContentIcon(R.drawable.new_mail)
- * .build())
- * .build();
- * NotificationManager notificationManger =
- * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- * notificationManager.notify(0, notif);</pre>
- */
- public Builder() {
- }
-
- /**
- * Create a {@link Builder} by reading wearable extensions present on an
- * existing {@code WearableNotificationExtensions} object.
- * @param other the existing extensions to inspect.
- */
- public Builder(WearableNotificationExtensions other) {
- Collections.addAll(mActions, other.mActions);
- mFlags = other.mFlags;
- mDisplayIntent = other.mDisplayIntent;
- Collections.addAll(mPages, other.mPages);
- mBackground = other.mBackground;
- mContentIcon = other.mContentIcon;
- mContentIconGravity = other.mContentIconGravity;
- mContentActionIndex = other.mContentActionIndex;
- mCustomContentHeight = other.mCustomContentHeight;
- mCustomSizePreset = other.mCustomSizePreset;
- mGravity = other.mGravity;
- }
-
- /**
- * Add a wearable action to this notification.
- *
- * <p>When wearable actions are added using this method, the set of actions that
- * show on a wearable device splits from devices that only show actions added
- * using {@link android.app.Notification.Builder#addAction}. This allows for customization
- * of which actions display on different devices.
- *
- * @param action the action to add to this notification
- * @return this object for method chaining
- * @see Notification.Action
- */
- public Builder addAction(Notification.Action action) {
- mActions.add(action);
- return this;
- }
-
- /**
- * Adds wearable actions to this notification.
- *
- * <p>When wearable actions are added using this method, the set of actions that
- * show on a wearable device splits from devices that only show actions added
- * using {@link android.app.Notification.Builder#addAction}. This allows for customization
- * of which actions display on different devices.
- *
- * @param actions the actions to add to this notification
- * @return this object for method chaining
- * @see Notification.Action
- */
- public Builder addActions(List<Notification.Action> actions) {
- mActions.addAll(actions);
- return this;
- }
-
- /**
- * Clear all wearable actions present on this builder.
- * @return this object for method chaining.
- * @see #addAction
- */
- public Builder clearActions() {
- mActions.clear();
- return this;
- }
-
- /**
- * Set an intent to launch inside of an activity view when displaying
- * this notification. This {@link android.app.PendingIntent} should be for an activity.
- *
- * @param intent the {@link android.app.PendingIntent} for an activity
- * @return this object for method chaining
- * @see WearableNotificationExtensions#getDisplayIntent
- */
- public Builder setDisplayIntent(PendingIntent intent) {
- mDisplayIntent = intent;
- return this;
- }
-
- /**
- * Add an additional page of content to display with this notification. The current
- * notification forms the first page, and pages added using this function form
- * subsequent pages. This field can be used to separate a notification into multiple
- * sections.
- *
- * @param page the notification to add as another page
- * @return this object for method chaining
- * @see WearableNotificationExtensions#getPages
- */
- public Builder addPage(Notification page) {
- mPages.add(page);
- return this;
- }
-
- /**
- * Add additional pages of content to display with this notification. The current
- * notification forms the first page, and pages added using this function form
- * subsequent pages. This field can be used to separate a notification into multiple
- * sections.
- *
- * @param pages a list of notifications
- * @return this object for method chaining
- * @see WearableNotificationExtensions#getPages
- */
- public Builder addPages(List<Notification> pages) {
- mPages.addAll(pages);
- return this;
- }
-
- /**
- * Clear all additional pages present on this builder.
- * @return this object for method chaining.
- * @see #addPage
- */
- public Builder clearPages() {
- mPages.clear();
- return this;
- }
-
- /**
- * Set a background image to be displayed behind the notification content.
- * Contrary to the {@link Notification.BigPictureStyle}, this background
- * will work with any notification style.
- *
- * @param background the background bitmap
- * @return this object for method chaining
- * @see WearableNotificationExtensions#getBackground
- */
- public Builder setBackground(Bitmap background) {
- mBackground = background;
- return this;
- }
-
- /**
- * Set an icon that goes with the content of this notification.
- */
- public Builder setContentIcon(int icon) {
- mContentIcon = icon;
- return this;
- }
-
- /**
- * Set the gravity that the content icon should have within the notification display.
- * Supported values include {@link Gravity#START} and {@link Gravity#END}. The default
- * value is {@link android.view.Gravity#END}.
- * @see #setContentIcon
- */
- public Builder setContentIconGravity(int contentIconGravity) {
- mContentIconGravity = contentIconGravity;
- return this;
- }
-
- /**
- * Set an action from this notification's actions to be clickable with the content of
- * this notification page. This action will no longer display separately from the
- * notification content. This action's icon will display with optional subtext provided
- * by the action's title.
- * @param actionIndex The index of the action to hoist on the current notification page.
- * If wearable actions are present, this index will apply to that list,
- * otherwise it will apply to the main notification's actions list.
- */
- public Builder setContentAction(int actionIndex) {
- mContentActionIndex = actionIndex;
- return this;
- }
-
- /**
- * Set the gravity that this notification should have within the available viewport space.
- * Supported values include {@link Gravity#TOP}, {@link Gravity#CENTER_VERTICAL} and
- * {@link Gravity#BOTTOM}. The default value is {@link Gravity#BOTTOM}.
- */
- public Builder setGravity(int gravity) {
- mGravity = gravity;
- return this;
- }
-
- /**
- * Set the custom size preset for the display of this notification out of the available
- * presets found in {@link WearableNotificationExtensions}, e.g. {@link #SIZE_LARGE}.
- * <p>Some custom size presets are only applicable for custom display notifications created
- * using {@link Builder#setDisplayIntent}. Check the documentation for the preset in
- * question. See also {@link Builder#setCustomContentHeight} and
- * {@link #getCustomSizePreset}.
- */
- public Builder setCustomSizePreset(int sizePreset) {
- mCustomSizePreset = sizePreset;
- return this;
- }
-
- /**
- * Set the custom height in pixels for the display of this notification's content.
- * <p>This option is only available for custom display notifications created
- * using {@link Builder#setDisplayIntent}. See also {@link Builder#setCustomSizePreset} and
- * {@link #getCustomContentHeight}.
- */
- public Builder setCustomContentHeight(int height) {
- mCustomContentHeight = height;
- return this;
- }
-
- /**
- * Set whether the scrolling position for the contents of this notification should start
- * at the bottom of the contents instead of the top when the contents are too long to
- * display within the screen. Default is false (start scroll at the top).
- */
- public Builder setStartScrollBottom(boolean startScrollBottom) {
- setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
- return this;
- }
-
- /**
- * Set whether the content intent is available when the wearable device is not connected
- * to a companion device. The user can still trigger this intent when the wearable device
- * is offline, but a visual hint will indicate that the content intent may not be available.
- * Defaults to true.
- */
- public Builder setContentIntentAvailableOffline(boolean contentIntentAvailableOffline) {
- setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
- return this;
- }
-
- /**
- * Set a hint that this notification's icon should not be displayed.
- * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
- * @return this object for method chaining
- */
- public Builder setHintHideIcon(boolean hintHideIcon) {
- setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
- return this;
- }
-
- /**
- * Set a visual hint that only the background image of this notification should be
- * displayed, and other semantic content should be hidden. This hint is only applicable
- * to sub-pages added using {@link #addPage}.
- */
- public Builder setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
- setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
- return this;
- }
-
- /**
- * Build a new {@link WearableNotificationExtensions} object with the extensions
- * currently present on this builder.
- * @return the extensions object.
- */
- public WearableNotificationExtensions build() {
- return new WearableNotificationExtensions(
- mActions.toArray(new Notification.Action[mActions.size()]), mFlags,
- mDisplayIntent, mPages.toArray(new Notification[mPages.size()]),
- mBackground, mContentIcon, mContentIconGravity, mContentActionIndex,
- mCustomSizePreset, mCustomContentHeight, mGravity);
- }
-
- private void setFlag(int mask, boolean value) {
- if (value) {
- mFlags |= mask;
- } else {
- mFlags &= ~mask;
- }
- }
- }
-
- public static final Creator<WearableNotificationExtensions> CREATOR =
- new Creator<WearableNotificationExtensions>() {
- @Override
- public WearableNotificationExtensions createFromParcel(Parcel in) {
- return new WearableNotificationExtensions(in);
- }
-
- @Override
- public WearableNotificationExtensions[] newArray(int size) {
- return new WearableNotificationExtensions[size];
- }
- };
-}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6ae006c..b0673b5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2023,6 +2023,7 @@
PRINT_SERVICE,
MEDIA_SESSION_SERVICE,
BATTERY_SERVICE,
+ TASK_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -2079,6 +2080,8 @@
* <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads
* <dt> {@link #BATTERY_SERVICE} ("batterymanager")
* <dd> A {@link android.os.BatteryManager} for managing battery state
+ * <dt> {@link #TASK_SERVICE} ("taskmanager")
+ * <dd> A {@link android.app.task.TaskManager} for managing scheduled tasks
* </dl>
*
* <p>Note: System services obtained via this API may be closely associated with
@@ -2134,6 +2137,8 @@
* @see android.app.DownloadManager
* @see #BATTERY_SERVICE
* @see android.os.BatteryManager
+ * @see #TASK_SERVICE
+ * @see android.app.task.TaskManager
*/
public abstract Object getSystemService(@ServiceName @NonNull String name);
@@ -2728,6 +2733,15 @@
public static final String USAGE_STATS_SERVICE = "usagestats";
/**
+ * Use with {@link #getSystemService} to retrieve a {@link
+ * android.app.task.TaskManager} instance for managing occasional
+ * background tasks.
+ * @see #getSystemService
+ * @see android.app.task.TaskManager
+ */
+ public static final String TASK_SERVICE = "task";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index f1391aa..bd07470 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3739,32 +3739,27 @@
*/
public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0x00080000;
/**
- * This flag is used to break out "documents" into separate tasks that can
- * be reached via the Recents mechanism. Such a document is any kind of
- * item for which an application may want to maintain multiple simultaneous
- * instances. Examples might be text files, web pages, spreadsheets, or
- * emails. Each such document will be in a separate task in the Recents list.
+ * This flag is used to open a document into a new task rooted at the activity launched
+ * by this Intent. Through the use of this flag, or its equivalent attribute,
+ * {@link android.R.attr#documentLaunchMode} multiple instances of the same activity
+ * containing different douments will appear in the recent tasks list.
*
- * <p>When set, the activity specified by this Intent will launch into a
- * separate task rooted at that activity. The activity launched must be
- * defined with {@link android.R.attr#launchMode} <code>standard</code>
- * or <code>singleTop</code>.
+ * <p>The use of the activity attribute form of this,
+ * {@link android.R.attr#documentLaunchMode}, is
+ * preferred over the Intent flag described here. The attribute form allows the
+ * Activity to specify multiple document behavior for all launchers of the Activity
+ * whereas using this flag requires each Intent that launches the Activity to specify it.
*
- * <p>If FLAG_ACTIVITY_NEW_DOCUMENT is used without
- * {@link #FLAG_ACTIVITY_MULTIPLE_TASK} then the activity manager will
- * search for an existing task with a matching target activity and Intent
- * data URI and relaunch that task, first finishing all activities down to
- * the root activity and then calling the root activity's
- * {@link android.app.Activity#onNewIntent(Intent)} method. If no existing
- * task's root activity matches the Intent's data URI then a new task will
- * be launched with the target activity as root.
+ * <p>FLAG_ACTIVITY_NEW_DOCUMENT may be used in conjunction with {@link
+ * #FLAG_ACTIVITY_MULTIPLE_TASK}. When used alone it is the
+ * equivalent of the Activity manifest specifying {@link
+ * android.R.attr#documentLaunchMode}="intoExisting". When used with
+ * FLAG_ACTIVITY_MULTIPLE_TASK it is the equivalent of the Activity manifest specifying
+ * {@link android.R.attr#documentLaunchMode}="always".
*
- * <p>When paired with {@link #FLAG_ACTIVITY_MULTIPLE_TASK} this will
- * always create a new task. Thus the same document may be made to appear
- * more than one time in Recents.
+ * Refer to {@link android.R.attr#documentLaunchMode} for more information.
*
- * <p>This is equivalent to the attribute {@link android.R.attr#documentLaunchMode}.
- *
+ * @see android.R.attr#documentLaunchMode
* @see #FLAG_ACTIVITY_MULTIPLE_TASK
*/
public static final int FLAG_ACTIVITY_NEW_DOCUMENT =
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index 6b4404d..46c9234 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -355,7 +355,14 @@
/**
* Registers a callback to be invoked when a change happens to a preference.
- *
+ *
+ * <p class="caution"><strong>Caution:</strong> The preference manager does
+ * not currently store a strong reference to the listener. You must store a
+ * strong reference to the listener, or it will be susceptible to garbage
+ * collection. We recommend you keep a reference to the listener in the
+ * instance data of an object that will exist as long as you need the
+ * listener.</p>
+ *
* @param listener The callback that will run.
* @see #unregisterOnSharedPreferenceChangeListener
*/
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d7bd473..4672015 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -19,9 +19,12 @@
import android.app.PackageInstallObserver;
import android.app.PackageUninstallObserver;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.FileBridge;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import java.io.OutputStream;
+
/** {@hide} */
public class PackageInstaller {
private final PackageManager mPm;
@@ -127,10 +130,17 @@
}
}
- public ParcelFileDescriptor openWrite(String overlayName, long offsetBytes,
- long lengthBytes) {
+ /**
+ * Open an APK file for writing, starting at the given offset. You can
+ * then stream data into the file, periodically calling
+ * {@link OutputStream#flush()} to ensure bytes have been written to
+ * disk.
+ */
+ public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes) {
try {
- return mSession.openWrite(overlayName, offsetBytes, lengthBytes);
+ final ParcelFileDescriptor clientSocket = mSession.openWrite(splitName,
+ offsetBytes, lengthBytes);
+ return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index 220b40d..2dce425 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -22,6 +22,7 @@
import android.util.Log;
import android.os.Debug;
import android.os.UserHandle;
+import dalvik.system.VMRuntime;
import java.nio.ByteBuffer;
@@ -126,8 +127,21 @@
// appName = "unknown";
String appName = DdmHandleAppName.getAppName();
- ByteBuffer out = ByteBuffer.allocate(20
- + vmIdent.length()*2 + appName.length()*2);
+ VMRuntime vmRuntime = VMRuntime.getRuntime();
+ String instructionSetDescription =
+ vmRuntime.is64Bit() ? "64-bit" : "32-bit";
+ String vmInstructionSet = vmRuntime.vmInstructionSet();
+ if (vmInstructionSet != null && vmInstructionSet.length() > 0) {
+ instructionSetDescription += " (" + vmInstructionSet + ")";
+ }
+ String vmFlags = "CheckJNI="
+ + (vmRuntime.isCheckJniEnabled() ? "true" : "false");
+
+ ByteBuffer out = ByteBuffer.allocate(28
+ + vmIdent.length() * 2
+ + appName.length() * 2
+ + instructionSetDescription.length() * 2
+ + vmFlags.length() * 2);
out.order(ChunkHandler.CHUNK_ORDER);
out.putInt(DdmServer.CLIENT_PROTOCOL_VERSION);
out.putInt(android.os.Process.myPid());
@@ -136,6 +150,10 @@
putString(out, vmIdent);
putString(out, appName);
out.putInt(UserHandle.myUserId());
+ out.putInt(instructionSetDescription.length());
+ putString(out, instructionSetDescription);
+ out.putInt(vmFlags.length());
+ putString(out, vmFlags);
Chunk reply = new Chunk(CHUNK_HELO, out);
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 86208fc..c593e9e 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -613,6 +613,7 @@
}
/**
+ * @hide
* @return The permission required to access this sensor. If empty, no permission is required.
*/
public String getRequiredPermission() {
diff --git a/core/java/android/hardware/camera2/CameraAccessException.java b/core/java/android/hardware/camera2/CameraAccessException.java
index 1af575f..ca71e81 100644
--- a/core/java/android/hardware/camera2/CameraAccessException.java
+++ b/core/java/android/hardware/camera2/CameraAccessException.java
@@ -114,7 +114,10 @@
mReason = problem;
}
- private static String getDefaultMessage(int problem) {
+ /**
+ * @hide
+ */
+ public static String getDefaultMessage(int problem) {
switch (problem) {
case CAMERA_IN_USE:
return "The camera device is in use already";
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 03b342c..accceb1 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -84,9 +84,8 @@
try {
CameraBinderDecorator.throwOnError(
CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor());
- } catch(CameraRuntimeException e) {
- throw new IllegalStateException("Failed to setup camera vendor tags",
- e.asChecked());
+ } catch (CameraRuntimeException e) {
+ handleRecoverableSetupErrors(e, "Failed to set up vendor tags");
}
try {
@@ -424,6 +423,18 @@
return mDeviceIdList;
}
+ private void handleRecoverableSetupErrors(CameraRuntimeException e, String msg) {
+ int problem = e.getReason();
+ switch (problem) {
+ case CameraAccessException.CAMERA_DISCONNECTED:
+ String errorMsg = CameraAccessException.getDefaultMessage(problem);
+ Log.w(TAG, msg + ": " + errorMsg);
+ break;
+ default:
+ throw new IllegalStateException(msg, e.asChecked());
+ }
+ }
+
// TODO: this class needs unit tests
// TODO: extract class into top level
private class CameraServiceListener extends ICameraServiceListener.Stub {
diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.java b/core/java/android/hardware/hdmi/HdmiCecMessage.java
index ddaf870..a8aa376 100644
--- a/core/java/android/hardware/hdmi/HdmiCecMessage.java
+++ b/core/java/android/hardware/hdmi/HdmiCecMessage.java
@@ -123,6 +123,7 @@
* @param p HdmiCecMessage object to read the Rating from
* @return a new HdmiCecMessage created from the data in the parcel
*/
+ @Override
public HdmiCecMessage createFromParcel(Parcel p) {
int source = p.readInt();
int destination = p.readInt();
@@ -131,6 +132,7 @@
p.readByteArray(params);
return new HdmiCecMessage(source, destination, opcode, params);
}
+ @Override
public HdmiCecMessage[] newArray(int size) {
return new HdmiCecMessage[size];
}
@@ -139,11 +141,40 @@
@Override
public String toString() {
StringBuffer s = new StringBuffer();
- s.append(String.format("src: %d dst: %d op: %2X params: ", mSource, mDestination, mOpcode));
- for (byte data : mParams) {
- s.append(String.format("%02X ", data));
+ s.append(String.format("<%s> src: %d, dst: %d",
+ opcodeToString(mOpcode), mSource, mDestination));
+ if (mParams.length > 0) {
+ s.append(", params:");
+ for (byte data : mParams) {
+ s.append(String.format(" %02X", data));
+ }
}
return s.toString();
}
+
+ private static String opcodeToString(int opcode) {
+ switch (opcode) {
+ case HdmiCec.MESSAGE_FEATURE_ABORT:
+ return "Feature Abort";
+ case HdmiCec.MESSAGE_CEC_VERSION:
+ return "CEC Version";
+ case HdmiCec.MESSAGE_REQUEST_ARC_INITIATION:
+ return "Request ARC Initiation";
+ case HdmiCec.MESSAGE_REQUEST_ARC_TERMINATION:
+ return "Request ARC Termination";
+ case HdmiCec.MESSAGE_REPORT_ARC_INITIATED:
+ return "Report ARC Initiated";
+ case HdmiCec.MESSAGE_REPORT_ARC_TERMINATED:
+ return "Report ARC Terminated";
+ case HdmiCec.MESSAGE_TEXT_VIEW_ON:
+ return "Text View On";
+ case HdmiCec.MESSAGE_ACTIVE_SOURCE:
+ return "Active Source";
+ case HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS:
+ return "Give Device Power Status";
+ default:
+ return String.format("Opcode: %02X", opcode);
+ }
+ }
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 80a9598..2f2aba3 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -35,15 +35,17 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
+import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.util.Protocol;
+
import java.net.InetAddress;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.HashMap;
-import com.android.internal.util.Protocol;
-
/**
* Class that answers queries about the state of network connectivity. It also
* notifies applications when network connectivity changes. Get an instance
@@ -940,34 +942,18 @@
}
/**
- * Gets the value of the setting for enabling Mobile data.
- *
- * @return Whether mobile data is enabled.
- *
- * <p>This method requires the call to hold the permission
- * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* @hide
+ * @deprecated Talk to TelephonyManager directly
*/
public boolean getMobileDataEnabled() {
- try {
- return mService.getMobileDataEnabled();
- } catch (RemoteException e) {
- return true;
+ IBinder b = ServiceManager.getService(Context.TELEPHONY_SERVICE);
+ if (b != null) {
+ try {
+ ITelephony it = ITelephony.Stub.asInterface(b);
+ return it.getDataEnabled();
+ } catch (RemoteException e) { }
}
- }
-
- /**
- * Sets the persisted value for enabling/disabling Mobile data.
- *
- * @param enabled Whether the user wants the mobile data connection used
- * or not.
- * @hide
- */
- public void setMobileDataEnabled(boolean enabled) {
- try {
- mService.setMobileDataEnabled(enabled);
- } catch (RemoteException e) {
- }
+ return false;
}
/**
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index d97b1e9..baec36a 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -74,9 +74,6 @@
boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress, String packageName);
- boolean getMobileDataEnabled();
- void setMobileDataEnabled(boolean enabled);
-
/** Policy control over specific {@link NetworkStateTracker}. */
void setPolicyDataEnable(int networkType, boolean enabled);
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index e0d69e3..e489e05 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -16,10 +16,13 @@
package android.net;
+import android.net.NetworkUtils;
import android.os.Parcelable;
import android.os.Parcel;
+import java.io.IOException;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.SocketFactory;
@@ -38,6 +41,8 @@
*/
public final int netId;
+ private NetworkBoundSocketFactory mNetworkBoundSocketFactory = null;
+
/**
* @hide
*/
@@ -79,6 +84,59 @@
}
/**
+ * A {@code SocketFactory} that produces {@code Socket}'s bound to this network.
+ */
+ private class NetworkBoundSocketFactory extends SocketFactory {
+ private final int mNetId;
+
+ public NetworkBoundSocketFactory(int netId) {
+ super();
+ mNetId = netId;
+ }
+
+ @Override
+ public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
+ Socket socket = createSocket();
+ socket.bind(new InetSocketAddress(localHost, localPort));
+ socket.connect(new InetSocketAddress(host, port));
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
+ int localPort) throws IOException {
+ Socket socket = createSocket();
+ socket.bind(new InetSocketAddress(localAddress, localPort));
+ socket.connect(new InetSocketAddress(address, port));
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress host, int port) throws IOException {
+ Socket socket = createSocket();
+ socket.connect(new InetSocketAddress(host, port));
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(String host, int port) throws IOException {
+ Socket socket = createSocket();
+ socket.connect(new InetSocketAddress(host, port));
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket() throws IOException {
+ Socket socket = new Socket();
+ // Query a property of the underlying socket to ensure the underlying
+ // socket exists so a file descriptor is available to bind to a network.
+ socket.getReuseAddress();
+ NetworkUtils.bindSocketToNetwork(socket.getFileDescriptor$().getInt$(), mNetId);
+ return socket;
+ }
+ }
+
+ /**
* Returns a {@link SocketFactory} bound to this network. Any {@link Socket} created by
* this factory will have its traffic sent over this {@code Network}. Note that if this
* {@code Network} ever disconnects, this factory and any {@link Socket} it produced in the
@@ -88,7 +146,10 @@
* {@code Network}.
*/
public SocketFactory socketFactory() {
- return null;
+ if (mNetworkBoundSocketFactory == null) {
+ mNetworkBoundSocketFactory = new NetworkBoundSocketFactory(netId);
+ }
+ return mNetworkBoundSocketFactory;
}
/**
@@ -99,6 +160,29 @@
* doesn't accidentally use sockets it thinks are still bound to a particular {@code Network}.
*/
public void bindProcess() {
+ NetworkUtils.bindProcessToNetwork(netId);
+ }
+
+ /**
+ * Binds host resolutions performed by this process to this network. {@link #bindProcess}
+ * takes precedence over this setting.
+ *
+ * @hide
+ * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+ */
+ public void bindProcessForHostResolution() {
+ NetworkUtils.bindProcessToNetworkForHostResolution(netId);
+ }
+
+ /**
+ * Clears any process specific {@link Network} binding for host resolution. This does
+ * not clear bindings enacted via {@link #bindProcess}.
+ *
+ * @hide
+ * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+ */
+ public void unbindProcessForHostResolution() {
+ NetworkUtils.unbindProcessToNetworkForHostResolution();
}
/**
@@ -107,7 +191,7 @@
* @return {@code Network} to which this process is bound.
*/
public static Network getProcessBoundNetwork() {
- return null;
+ return new Network(NetworkUtils.getNetworkBoundToProcess());
}
/**
@@ -115,6 +199,7 @@
* {@link Network#bindProcess}.
*/
public static void unbindProcess() {
+ NetworkUtils.unbindProcessToNetwork();
}
// implement the Parcelable interface
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index b24d396..edb3286 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -109,6 +109,50 @@
public native static void markSocket(int socketfd, int mark);
/**
+ * Binds the current process to the network designated by {@code netId}. All sockets created
+ * in the future (and not explicitly bound via a bound {@link SocketFactory} (see
+ * {@link Network#socketFactory}) will be bound to this network. Note that if this
+ * {@code Network} ever disconnects all sockets created in this way will cease to work. This
+ * is by design so an application doesn't accidentally use sockets it thinks are still bound to
+ * a particular {@code Network}.
+ */
+ public native static void bindProcessToNetwork(int netId);
+
+ /**
+ * Clear any process specific {@code Network} binding. This reverts a call to
+ * {@link #bindProcessToNetwork}.
+ */
+ public native static void unbindProcessToNetwork();
+
+ /**
+ * Return the netId last passed to {@link #bindProcessToNetwork}, or NETID_UNSET if
+ * {@link #unbindProcessToNetwork} has been called since {@link #bindProcessToNetwork}.
+ */
+ public native static int getNetworkBoundToProcess();
+
+ /**
+ * Binds host resolutions performed by this process to the network designated by {@code netId}.
+ * {@link #bindProcessToNetwork} takes precedence over this setting.
+ *
+ * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+ */
+ public native static void bindProcessToNetworkForHostResolution(int netId);
+
+ /**
+ * Clears any process specific {@link Network} binding for host resolution. This does
+ * not clear bindings enacted via {@link #bindProcessToNetwork}.
+ *
+ * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+ */
+ public native static void unbindProcessToNetworkForHostResolution();
+
+ /**
+ * Explicitly binds {@code socketfd} to the network designated by {@code netId}. This
+ * overrides any binding via {@link #bindProcessToNetwork}.
+ */
+ public native static void bindSocketToNetwork(int socketfd, int netId);
+
+ /**
* Convert a IPv4 address from an integer to an InetAddress.
* @param hostAddress an int corresponding to the IPv4 address in network byte order
*/
diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java
new file mode 100644
index 0000000..7f8bc9f
--- /dev/null
+++ b/core/java/android/os/FileBridge.java
@@ -0,0 +1,165 @@
+/*
+ * 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.os;
+
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.SOCK_STREAM;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import libcore.io.IoBridge;
+import libcore.io.IoUtils;
+import libcore.io.Memory;
+import libcore.io.Streams;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.SyncFailedException;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+/**
+ * Simple bridge that allows file access across process boundaries without
+ * returning the underlying {@link FileDescriptor}. This is useful when the
+ * server side needs to strongly assert that a client side is completely
+ * hands-off.
+ *
+ * @hide
+ */
+public class FileBridge extends Thread {
+ private static final String TAG = "FileBridge";
+
+ // TODO: consider extending to support bidirectional IO
+
+ private static final int MSG_LENGTH = 8;
+
+ /** CMD_WRITE [len] [data] */
+ private static final int CMD_WRITE = 1;
+ /** CMD_FSYNC */
+ private static final int CMD_FSYNC = 2;
+
+ private FileDescriptor mTarget;
+
+ private final FileDescriptor mServer = new FileDescriptor();
+ private final FileDescriptor mClient = new FileDescriptor();
+
+ private volatile boolean mClosed;
+
+ public FileBridge() {
+ try {
+ Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
+ } catch (ErrnoException e) {
+ throw new RuntimeException("Failed to create bridge");
+ }
+ }
+
+ public boolean isClosed() {
+ return mClosed;
+ }
+
+ public void setTargetFile(FileDescriptor target) {
+ mTarget = target;
+ }
+
+ public FileDescriptor getClientSocket() {
+ return mClient;
+ }
+
+ @Override
+ public void run() {
+ final byte[] temp = new byte[8192];
+ try {
+ while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
+ final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
+
+ if (cmd == CMD_WRITE) {
+ // Shuttle data into local file
+ int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
+ while (len > 0) {
+ int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
+ IoBridge.write(mTarget, temp, 0, n);
+ len -= n;
+ }
+
+ } else if (cmd == CMD_FSYNC) {
+ // Sync and echo back to confirm
+ Os.fsync(mTarget);
+ IoBridge.write(mServer, temp, 0, MSG_LENGTH);
+ }
+ }
+
+ // Client was closed; one last fsync
+ Os.fsync(mTarget);
+
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Failed during bridge: ", e);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed during bridge: ", e);
+ } finally {
+ IoUtils.closeQuietly(mTarget);
+ IoUtils.closeQuietly(mServer);
+ IoUtils.closeQuietly(mClient);
+ mClosed = true;
+ }
+ }
+
+ public static class FileBridgeOutputStream extends OutputStream {
+ private final FileDescriptor mClient;
+ private final byte[] mTemp = new byte[MSG_LENGTH];
+
+ public FileBridgeOutputStream(FileDescriptor client) {
+ mClient = client;
+ }
+
+ @Override
+ public void close() throws IOException {
+ IoBridge.closeAndSignalBlockedThreads(mClient);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ Memory.pokeInt(mTemp, 0, CMD_FSYNC, ByteOrder.BIG_ENDIAN);
+ IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
+
+ // Wait for server to ack
+ if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) {
+ if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == CMD_FSYNC) {
+ return;
+ }
+ }
+
+ throw new SyncFailedException("Failed to fsync() across bridge");
+ }
+
+ @Override
+ public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+ Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount);
+ Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN);
+ Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN);
+ IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
+ IoBridge.write(mClient, buffer, byteOffset, byteCount);
+ }
+
+ @Override
+ public void write(int oneByte) throws IOException {
+ Streams.writeSingleByte(this, oneByte);
+ }
+ }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0f21c1d..6396781 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9236,6 +9236,30 @@
}
/**
+ * Request unbuffered dispatch of the given stream of MotionEvents to this View.
+ *
+ * Until this View receives a corresponding {@link MotionEvent#ACTION_UP}, ask that the input
+ * system not batch {@link MotionEvent}s but instead deliver them as soon as they're
+ * available. This method should only be called for touch events.
+ *
+ * <p class="note">This api is not intended for most applications. Buffered dispatch
+ * provides many of benefits, and just requesting unbuffered dispatch on most MotionEvent
+ * streams will not improve your input latency. Side effects include: increased latency,
+ * jittery scrolls and inability to take advantage of system resampling. Talk to your input
+ * professional to see if {@link #requestUnbufferedDispatch(MotionEvent)} is right for
+ * you.</p>
+ */
+ public final void requestUnbufferedDispatch(MotionEvent event) {
+ final int action = event.getAction();
+ if (mAttachInfo == null
+ || action != MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_MOVE
+ || !event.isTouchEvent()) {
+ return;
+ }
+ mAttachInfo.mUnbufferedDispatchRequested = true;
+ }
+
+ /**
* Set flags controlling behavior of this view.
*
* @param flags Constant indicating the value which should be set
@@ -19760,6 +19784,12 @@
boolean mInTouchMode;
/**
+ * Indicates whether the view has requested unbuffered input dispatching for the current
+ * event stream.
+ */
+ boolean mUnbufferedDispatchRequested;
+
+ /**
* Indicates that ViewAncestor should trigger a global layout change
* the next time it performs a traversal
*/
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fc7bf0e..c3bf295 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -230,6 +230,7 @@
QueuedInputEvent mPendingInputEventTail;
int mPendingInputEventCount;
boolean mProcessInputEventsScheduled;
+ boolean mUnbufferedInputDispatch;
String mPendingInputEventQueueLengthCounterName = "pq";
InputStage mFirstInputStage;
@@ -1016,7 +1017,9 @@
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
- scheduleConsumeBatchedInput();
+ if (!mUnbufferedInputDispatch) {
+ scheduleConsumeBatchedInput();
+ }
notifyRendererOfFramePending();
}
}
@@ -2616,7 +2619,7 @@
}
final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
- final Rect bounds = mView.mAttachInfo.mTmpInvalRect;
+ final Rect bounds = mAttachInfo.mTmpInvalRect;
if (provider == null) {
host.getBoundsOnScreen(bounds);
} else if (mAccessibilityFocusedVirtualView != null) {
@@ -3898,6 +3901,18 @@
}
}
+ @Override
+ protected void onDeliverToNext(QueuedInputEvent q) {
+ if (mUnbufferedInputDispatch
+ && q.mEvent instanceof MotionEvent
+ && ((MotionEvent)q.mEvent).isTouchEvent()
+ && isTerminalInputEvent(q.mEvent)) {
+ mUnbufferedInputDispatch = false;
+ scheduleConsumeBatchedInput();
+ }
+ super.onDeliverToNext(q);
+ }
+
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
@@ -4010,10 +4025,15 @@
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
- if (mView.dispatchPointerEvent(event)) {
- return FINISH_HANDLED;
+ mAttachInfo.mUnbufferedDispatchRequested = false;
+ boolean handled = mView.dispatchPointerEvent(event);
+ if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
+ mUnbufferedInputDispatch = true;
+ if (mConsumeBatchedInputScheduled) {
+ scheduleConsumeBatchedInputImmediately();
+ }
}
- return FORWARD;
+ return handled ? FINISH_HANDLED : FORWARD;
}
private int processTrackballEvent(QueuedInputEvent q) {
@@ -5278,6 +5298,8 @@
writer.print(" mRemoved="); writer.println(mRemoved);
writer.print(innerPrefix); writer.print("mConsumeBatchedInputScheduled=");
writer.println(mConsumeBatchedInputScheduled);
+ writer.print(innerPrefix); writer.print("mConsumeBatchedInputImmediatelyScheduled=");
+ writer.println(mConsumeBatchedInputImmediatelyScheduled);
writer.print(innerPrefix); writer.print("mPendingInputEventCount=");
writer.println(mPendingInputEventCount);
writer.print(innerPrefix); writer.print("mProcessInputEventsScheduled=");
@@ -5688,6 +5710,7 @@
private void finishInputEvent(QueuedInputEvent q) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
+
if (q.mReceiver != null) {
boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
q.mReceiver.finishInputEvent(q.mEvent, handled);
@@ -5727,15 +5750,25 @@
}
}
+ void scheduleConsumeBatchedInputImmediately() {
+ if (!mConsumeBatchedInputImmediatelyScheduled) {
+ unscheduleConsumeBatchedInput();
+ mConsumeBatchedInputImmediatelyScheduled = true;
+ mHandler.post(mConsumeBatchedInputImmediatelyRunnable);
+ }
+ }
+
void doConsumeBatchedInput(long frameTimeNanos) {
if (mConsumeBatchedInputScheduled) {
mConsumeBatchedInputScheduled = false;
if (mInputEventReceiver != null) {
- if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)) {
+ if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)
+ && frameTimeNanos != -1) {
// If we consumed a batch here, we want to go ahead and schedule the
// consumption of batched input events on the next frame. Otherwise, we would
// wait until we have more input events pending and might get starved by other
- // things occurring in the process.
+ // things occurring in the process. If the frame time is -1, however, then
+ // we're in a non-batching mode, so there's no need to schedule this.
scheduleConsumeBatchedInput();
}
}
@@ -5763,7 +5796,11 @@
@Override
public void onBatchedInputEventPending() {
- scheduleConsumeBatchedInput();
+ if (mUnbufferedInputDispatch) {
+ super.onBatchedInputEventPending();
+ } else {
+ scheduleConsumeBatchedInput();
+ }
}
@Override
@@ -5784,6 +5821,16 @@
new ConsumeBatchedInputRunnable();
boolean mConsumeBatchedInputScheduled;
+ final class ConsumeBatchedInputImmediatelyRunnable implements Runnable {
+ @Override
+ public void run() {
+ doConsumeBatchedInput(-1);
+ }
+ }
+ final ConsumeBatchedInputImmediatelyRunnable mConsumeBatchedInputImmediatelyRunnable =
+ new ConsumeBatchedInputImmediatelyRunnable();
+ boolean mConsumeBatchedInputImmediatelyScheduled;
+
final class InvalidateOnAnimationRunnable implements Runnable {
private boolean mPosted;
private final ArrayList<View> mViews = new ArrayList<View>();
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 8511601..defc26c 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -104,14 +104,16 @@
*
* <h4>Excess Space Distribution</h4>
*
- * GridLayout's distribution of excess space is based on <em>priority</em>
- * rather than <em>weight</em>.
+ * As of API 21, GridLayout's distribution of excess space accomodates the principle of weight.
+ * In the event that no weights are specified, the previous conventions are respected and
+ * columns and rows are taken as flexible if their views specify some form of alignment
+ * within their groups.
* <p>
- * A child's ability to stretch is inferred from the alignment properties of
- * its row and column groups (which are typically set by setting the
- * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters).
- * If alignment was defined along a given axis then the component
- * is taken as <em>flexible</em> in that direction. If no alignment was set,
+ * The flexibility of a view is therefore influenced by its alignment which is,
+ * in turn, typically defined by setting the
+ * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters.
+ * If either a weight or alignment were defined along a given axis then the component
+ * is taken as <em>flexible</em> in that direction. If no weight or alignment was set,
* the component is instead assumed to be <em>inflexible</em>.
* <p>
* Multiple components in the same row or column group are
@@ -122,12 +124,16 @@
* elements is flexible if <em>one</em> of its elements is flexible.
* <p>
* To make a column stretch, make sure all of the components inside it define a
- * gravity. To prevent a column from stretching, ensure that one of the components
- * in the column does not define a gravity.
+ * weight or a gravity. To prevent a column from stretching, ensure that one of the components
+ * in the column does not define a weight or a gravity.
* <p>
* When the principle of flexibility does not provide complete disambiguation,
* GridLayout's algorithms favour rows and columns that are closer to its <em>right</em>
- * and <em>bottom</em> edges.
+ * and <em>bottom</em> edges. To be more precise, GridLayout treats each of its layout
+ * parameters as a constraint in the a set of variables that define the grid-lines along a
+ * given axis. During layout, GridLayout solves the constraints so as to return the unique
+ * solution to those constraints for which all variables are less-than-or-equal-to
+ * the corresponding value in any other valid solution.
*
* <h4>Interpretation of GONE</h4>
*
@@ -140,18 +146,6 @@
* had never been added to it.
* These statements apply equally to rows as well as columns, and to groups of rows or columns.
*
- * <h5>Limitations</h5>
- *
- * GridLayout does not provide support for the principle of <em>weight</em>, as defined in
- * {@link LinearLayout.LayoutParams#weight}. In general, it is not therefore possible
- * to configure a GridLayout to distribute excess space between multiple components.
- * <p>
- * Some common use-cases may nevertheless be accommodated as follows.
- * To place equal amounts of space around a component in a cell group;
- * use {@link #CENTER} alignment (or {@link LayoutParams#setGravity(int) gravity}).
- * For complete control over excess space distribution in a row or column;
- * use a {@link LinearLayout} subview to hold the components in the associated cell group.
- * When using either of these techniques, bear in mind that cell groups may be defined to overlap.
* <p>
* See {@link GridLayout.LayoutParams} for a full description of the
* layout parameters used by GridLayout.
@@ -1018,6 +1012,8 @@
LayoutParams lp = getLayoutParams(c);
if (firstPass) {
measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
+ mHorizontalAxis.recordOriginalMeasurement(i);
+ mVerticalAxis.recordOriginalMeasurement(i);
} else {
boolean horizontal = (mOrientation == HORIZONTAL);
Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
@@ -1245,6 +1241,11 @@
public int[] locations;
public boolean locationsValid = false;
+ public boolean hasWeights;
+ public boolean hasWeightsValid = false;
+ public int[] originalMeasurements;
+ public int[] deltas;
+
boolean orderPreserved = DEFAULT_ORDER_PRESERVED;
private MutableInt parentMin = new MutableInt(0);
@@ -1321,7 +1322,10 @@
// we must include views that are GONE here, see introductory javadoc
LayoutParams lp = getLayoutParams(c);
Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
- groupBounds.getValue(i).include(GridLayout.this, c, spec, this);
+ int size = (spec.weight == 0) ?
+ getMeasurementIncludingMargin(c, horizontal) :
+ getOriginalMeasurements()[i] + getDeltas()[i];
+ groupBounds.getValue(i).include(GridLayout.this, c, spec, this, size);
}
}
@@ -1693,8 +1697,94 @@
return trailingMargins;
}
- private void computeLocations(int[] a) {
+ private void solve(int[] a) {
solve(getArcs(), a);
+ }
+
+ private boolean computeHasWeights() {
+ for (int i = 0, N = getChildCount(); i < N; i++) {
+ LayoutParams lp = getLayoutParams(getChildAt(i));
+ Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
+ if (spec.weight != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean hasWeights() {
+ if (!hasWeightsValid) {
+ hasWeights = computeHasWeights();
+ hasWeightsValid = true;
+ }
+ return hasWeights;
+ }
+
+ public int[] getOriginalMeasurements() {
+ if (originalMeasurements == null) {
+ originalMeasurements = new int[getChildCount()];
+ }
+ return originalMeasurements;
+ }
+
+ private void recordOriginalMeasurement(int i) {
+ if (hasWeights()) {
+ getOriginalMeasurements()[i] = getMeasurementIncludingMargin(getChildAt(i), horizontal);
+ }
+ }
+
+ public int[] getDeltas() {
+ if (deltas == null) {
+ deltas = new int[getChildCount()];
+ }
+ return deltas;
+ }
+
+ private void shareOutDelta() {
+ int totalDelta = 0;
+ float totalWeight = 0;
+ for (int i = 0, N = getChildCount(); i < N; i++) {
+ View c = getChildAt(i);
+ LayoutParams lp = getLayoutParams(c);
+ Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
+ float weight = spec.weight;
+ if (weight != 0) {
+ int delta = getMeasurement(c, horizontal) - getOriginalMeasurements()[i];
+ totalDelta += delta;
+ totalWeight += weight;
+ }
+ }
+ for (int i = 0, N = getChildCount(); i < N; i++) {
+ LayoutParams lp = getLayoutParams(getChildAt(i));
+ Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
+ float weight = spec.weight;
+ if (weight != 0) {
+ int delta = Math.round((weight * totalDelta / totalWeight));
+ deltas[i] = delta;
+ // the two adjustments below are to counter the above rounding and avoid off-by-ones at the end
+ totalDelta -= delta;
+ totalWeight -= weight;
+ }
+ }
+ }
+
+ private void solveAndDistributeSpace(int[] a) {
+ Arrays.fill(getDeltas(), 0);
+ solve(a);
+ shareOutDelta();
+ arcsValid = false;
+ forwardLinksValid = false;
+ backwardLinksValid = false;
+ groupBoundsValid = false;
+ solve(a);
+ }
+
+ private void computeLocations(int[] a) {
+ if (!hasWeights()) {
+ solve(a);
+ } else {
+ solveAndDistributeSpace(a);
+ }
if (!orderPreserved) {
// Solve returns the smallest solution to the constraint system for which all
// values are positive. One value is therefore zero - though if the row/col
@@ -1777,6 +1867,10 @@
locations = null;
+ originalMeasurements = null;
+ deltas = null;
+ hasWeightsValid = false;
+
invalidateValues();
}
@@ -1810,6 +1904,9 @@
* both aspects of alignment within the cell group. It is also possible to specify a child's
* alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
* method.
+ * <p>
+ * The weight property is also included in Spec and specifies the proportion of any
+ * excess space that is due to the associated view.
*
* <h4>WRAP_CONTENT and MATCH_PARENT</h4>
*
@@ -1851,9 +1948,11 @@
* <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li>
* <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li>
* <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
+ * <li>{@link #rowSpec}<code>.weight</code> = 0 </li>
* <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
* <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
* <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li>
+ * <li>{@link #columnSpec}<code>.weight</code> = 0 </li>
* </ul>
*
* See {@link GridLayout} for a more complete description of the conventions
@@ -1861,8 +1960,10 @@
*
* @attr ref android.R.styleable#GridLayout_Layout_layout_row
* @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
+ * @attr ref android.R.styleable#GridLayout_Layout_layout_rowWeight
* @attr ref android.R.styleable#GridLayout_Layout_layout_column
* @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
+ * @attr ref android.R.styleable#GridLayout_Layout_layout_columnWeight
* @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
*/
public static class LayoutParams extends MarginLayoutParams {
@@ -1889,9 +1990,11 @@
private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
+ private static final int COLUMN_WEIGHT = R.styleable.GridLayout_Layout_layout_columnWeight;
private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
+ private static final int ROW_WEIGHT = R.styleable.GridLayout_Layout_layout_rowWeight;
private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
@@ -2034,11 +2137,13 @@
int column = a.getInt(COLUMN, DEFAULT_COLUMN);
int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
- this.columnSpec = spec(column, colSpan, getAlignment(gravity, true));
+ float colWeight = a.getFloat(COLUMN_WEIGHT, Spec.DEFAULT_WEIGHT);
+ this.columnSpec = spec(column, colSpan, getAlignment(gravity, true), colWeight);
int row = a.getInt(ROW, DEFAULT_ROW);
int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
- this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false));
+ float rowWeight = a.getFloat(ROW_WEIGHT, Spec.DEFAULT_WEIGHT);
+ this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false), rowWeight);
} finally {
a.recycle();
}
@@ -2273,10 +2378,9 @@
return before - a.getAlignmentValue(c, size, gl.getLayoutMode());
}
- protected final void include(GridLayout gl, View c, Spec spec, Axis axis) {
+ protected final void include(GridLayout gl, View c, Spec spec, Axis axis, int size) {
this.flexibility &= spec.getFlexibility();
boolean horizontal = axis.horizontal;
- int size = gl.getMeasurementIncludingMargin(c, horizontal);
Alignment alignment = gl.getAlignment(spec.alignment, horizontal);
// todo test this works correctly when the returned value is UNDEFINED
int before = alignment.getAlignmentValue(c, size, gl.getLayoutMode());
@@ -2401,36 +2505,43 @@
* <li>{@link #spec(int, int)}</li>
* <li>{@link #spec(int, Alignment)}</li>
* <li>{@link #spec(int, int, Alignment)}</li>
+ * <li>{@link #spec(int, float)}</li>
+ * <li>{@link #spec(int, int, float)}</li>
+ * <li>{@link #spec(int, Alignment, float)}</li>
+ * <li>{@link #spec(int, int, Alignment, float)}</li>
* </ul>
*
*/
public static class Spec {
static final Spec UNDEFINED = spec(GridLayout.UNDEFINED);
+ static final float DEFAULT_WEIGHT = 0;
final boolean startDefined;
final Interval span;
final Alignment alignment;
+ final float weight;
- private Spec(boolean startDefined, Interval span, Alignment alignment) {
+ private Spec(boolean startDefined, Interval span, Alignment alignment, float weight) {
this.startDefined = startDefined;
this.span = span;
this.alignment = alignment;
+ this.weight = weight;
}
- private Spec(boolean startDefined, int start, int size, Alignment alignment) {
- this(startDefined, new Interval(start, start + size), alignment);
+ private Spec(boolean startDefined, int start, int size, Alignment alignment, float weight) {
+ this(startDefined, new Interval(start, start + size), alignment, weight);
}
final Spec copyWriteSpan(Interval span) {
- return new Spec(startDefined, span, alignment);
+ return new Spec(startDefined, span, alignment, weight);
}
final Spec copyWriteAlignment(Alignment alignment) {
- return new Spec(startDefined, span, alignment);
+ return new Spec(startDefined, span, alignment, weight);
}
final int getFlexibility() {
- return (alignment == UNDEFINED_ALIGNMENT) ? INFLEXIBLE : CAN_STRETCH;
+ return (alignment == UNDEFINED_ALIGNMENT && weight == 0) ? INFLEXIBLE : CAN_STRETCH;
}
/**
@@ -2478,6 +2589,7 @@
* <ul>
* <li> {@code spec.span = [start, start + size]} </li>
* <li> {@code spec.alignment = alignment} </li>
+ * <li> {@code spec.weight = weight} </li>
* </ul>
* <p>
* To leave the start index undefined, use the value {@link #UNDEFINED}.
@@ -2485,9 +2597,55 @@
* @param start the start
* @param size the size
* @param alignment the alignment
+ * @param weight the weight
+ */
+ public static Spec spec(int start, int size, Alignment alignment, float weight) {
+ return new Spec(start != UNDEFINED, start, size, alignment, weight);
+ }
+
+ /**
+ * Equivalent to: {@code spec(start, 1, alignment, weight)}.
+ *
+ * @param start the start
+ * @param alignment the alignment
+ * @param weight the weight
+ */
+ public static Spec spec(int start, Alignment alignment, float weight) {
+ return spec(start, 1, alignment, weight);
+ }
+
+ /**
+ * Equivalent to: {@code spec(start, 1, default_alignment, weight)} -
+ * where {@code default_alignment} is specified in
+ * {@link android.widget.GridLayout.LayoutParams}.
+ *
+ * @param start the start
+ * @param size the size
+ * @param weight the weight
+ */
+ public static Spec spec(int start, int size, float weight) {
+ return spec(start, size, UNDEFINED_ALIGNMENT, weight);
+ }
+
+ /**
+ * Equivalent to: {@code spec(start, 1, weight)}.
+ *
+ * @param start the start
+ * @param weight the weight
+ */
+ public static Spec spec(int start, float weight) {
+ return spec(start, 1, weight);
+ }
+
+ /**
+ * Equivalent to: {@code spec(start, size, alignment, 0f)}.
+ *
+ * @param start the start
+ * @param size the size
+ * @param alignment the alignment
*/
public static Spec spec(int start, int size, Alignment alignment) {
- return new Spec(start != UNDEFINED, start, size, alignment);
+ return spec(start, size, alignment, Spec.DEFAULT_WEIGHT);
}
/**
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 591267e..183dd05 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -484,8 +484,7 @@
mList.clear();
if (mBaseResolveList != null) {
- currentResolveList = mBaseResolveList;
- mOrigResolveList = null;
+ currentResolveList = mOrigResolveList = mBaseResolveList;
} else {
currentResolveList = mOrigResolveList = mPm.queryIntentActivities(
mIntent, PackageManager.MATCH_DEFAULT_ONLY
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index 495d5c688..70c77b8 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Slog;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -211,54 +212,111 @@
}
}
- private final InputMethodSettings mSettings;
- private InputMethodAndSubtypeList mSubtypeList;
+ private static int calculateSubtypeId(InputMethodInfo imi, InputMethodSubtype subtype) {
+ return subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi,
+ subtype.hashCode()) : NOT_A_SUBTYPE_ID;
+ }
- @VisibleForTesting
- public static ImeSubtypeListItem getNextInputMethodLockedImpl(List<ImeSubtypeListItem> imList,
- boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
- if (imi == null) {
- return null;
+ private static class StaticRotationList {
+ private final List<ImeSubtypeListItem> mImeSubtypeList;
+ public StaticRotationList(final List<ImeSubtypeListItem> imeSubtypeList) {
+ mImeSubtypeList = imeSubtypeList;
}
- if (imList.size() <= 1) {
- return null;
- }
- // Here we have two rotation groups, depending on the returned boolean value of
- // {@link InputMethodInfo#supportsSwitchingToNextInputMethod()}.
- final boolean expectedValueOfSupportsSwitchingToNextInputMethod =
- imi.supportsSwitchingToNextInputMethod();
- final int N = imList.size();
- final int currentSubtypeId =
- subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi,
- subtype.hashCode()) : NOT_A_SUBTYPE_ID;
- for (int i = 0; i < N; ++i) {
- final ImeSubtypeListItem isli = imList.get(i);
- // Skip until the current IME/subtype is found.
- if (!isli.mImi.equals(imi) || isli.mSubtypeId != currentSubtypeId) {
- continue;
- }
- // Found the current IME/subtype. Start searching the next IME/subtype from here.
- for (int j = 0; j < N - 1; ++j) {
- final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N);
- // Skip if the candidate doesn't belong to the expected rotation group.
- if (expectedValueOfSupportsSwitchingToNextInputMethod !=
- candidate.mImi.supportsSwitchingToNextInputMethod()) {
- continue;
+
+ /**
+ * Returns the index of the specified input method and subtype in the given list.
+ * @param imi The {@link InputMethodInfo} to be searched.
+ * @param subtype The {@link InputMethodSubtype} to be searched. null if the input method
+ * does not have a subtype.
+ * @return The index in the given list. -1 if not found.
+ */
+ private int getIndex(InputMethodInfo imi, InputMethodSubtype subtype) {
+ final int currentSubtypeId = calculateSubtypeId(imi, subtype);
+ final int N = mImeSubtypeList.size();
+ for (int i = 0; i < N; ++i) {
+ final ImeSubtypeListItem isli = mImeSubtypeList.get(i);
+ // Skip until the current IME/subtype is found.
+ if (imi.equals(isli.mImi) && isli.mSubtypeId == currentSubtypeId) {
+ return i;
}
+ }
+ return -1;
+ }
+
+ public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
+ InputMethodInfo imi, InputMethodSubtype subtype) {
+ if (imi == null) {
+ return null;
+ }
+ if (mImeSubtypeList.size() <= 1) {
+ return null;
+ }
+ final int currentIndex = getIndex(imi, subtype);
+ if (currentIndex < 0) {
+ return null;
+ }
+ final int N = mImeSubtypeList.size();
+ for (int offset = 1; offset < N; ++offset) {
+ // Start searching the next IME/subtype from the next of the current index.
+ final int candidateIndex = (currentIndex + offset) % N;
+ final ImeSubtypeListItem candidate = mImeSubtypeList.get(candidateIndex);
// Skip if searching inside the current IME only, but the candidate is not
// the current IME.
- if (onlyCurrentIme && !candidate.mImi.equals(imi)) {
+ if (onlyCurrentIme && !imi.equals(candidate.mImi)) {
continue;
}
return candidate;
}
- // No appropriate IME/subtype is found in the list. Give up.
return null;
}
- // The current IME/subtype is not found in the list. Give up.
- return null;
}
+ @VisibleForTesting
+ public static class ControllerImpl {
+ private final StaticRotationList mSwitchingAwareSubtypeList;
+ private final StaticRotationList mSwitchingUnawareSubtypeList;
+
+ public ControllerImpl(final List<ImeSubtypeListItem> sortedItems) {
+ mSwitchingAwareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems,
+ true /* supportsSwitchingToNextInputMethod */));
+ mSwitchingUnawareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems,
+ false /* supportsSwitchingToNextInputMethod */));
+ }
+
+ public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi,
+ InputMethodSubtype subtype) {
+ if (imi == null) {
+ return null;
+ }
+ if (imi.supportsSwitchingToNextInputMethod()) {
+ return mSwitchingAwareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi,
+ subtype);
+ } else {
+ return mSwitchingUnawareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi,
+ subtype);
+ }
+ }
+
+ private static List<ImeSubtypeListItem> filterImeSubtypeList(
+ final List<ImeSubtypeListItem> items,
+ final boolean supportsSwitchingToNextInputMethod) {
+ final ArrayList<ImeSubtypeListItem> result = new ArrayList<>();
+ final int ALL_ITEMS_COUNT = items.size();
+ for (int i = 0; i < ALL_ITEMS_COUNT; i++) {
+ final ImeSubtypeListItem item = items.get(i);
+ if (item.mImi.supportsSwitchingToNextInputMethod() ==
+ supportsSwitchingToNextInputMethod) {
+ result.add(item);
+ }
+ }
+ return result;
+ }
+ }
+
+ private final InputMethodSettings mSettings;
+ private InputMethodAndSubtypeList mSubtypeList;
+ private ControllerImpl mController;
+
private InputMethodSubtypeSwitchingController(InputMethodSettings settings, Context context) {
mSettings = settings;
resetCircularListLocked(context);
@@ -276,12 +334,18 @@
public void resetCircularListLocked(Context context) {
mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
+ mController = new ControllerImpl(mSubtypeList.getSortedInputMethodAndSubtypeList());
}
- public ImeSubtypeListItem getNextInputMethodLocked(
- boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
- return getNextInputMethodLockedImpl(mSubtypeList.getSortedInputMethodAndSubtypeList(),
- onlyCurrentIme, imi, subtype);
+ public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
+ InputMethodSubtype subtype) {
+ if (mController == null) {
+ if (DEBUG) {
+ Log.e(TAG, "mController shouldn't be null.");
+ }
+ return null;
+ }
+ return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
}
public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(boolean showSubtypes,
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index a159715..f446c3a 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -173,8 +173,10 @@
$(call include-path-for, bluedroid) \
$(call include-path-for, libhardware)/hardware \
$(call include-path-for, libhardware_legacy)/hardware_legacy \
+ $(TOP)/bionic/libc/dns/include \
$(TOP)/frameworks/av/include \
$(TOP)/system/media/camera/include \
+ $(TOP)/system/netd/include \
external/pdfium/core/include/fpdfapi \
external/pdfium/core/include/fpdfdoc \
external/pdfium/fpdfsdk/include \
@@ -233,6 +235,7 @@
libaudioutils \
libpdfium \
libimg_utils \
+ libnetd_client \
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 6d23c32..bc5e1b3 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -18,6 +18,8 @@
#include "jni.h"
#include "JNIHelp.h"
+#include "NetdClient.h"
+#include "resolv_netid.h"
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
@@ -250,6 +252,36 @@
}
}
+static void android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
+{
+ setNetworkForProcess(netId);
+}
+
+static void android_net_utils_unbindProcessToNetwork(JNIEnv *env, jobject thiz)
+{
+ setNetworkForProcess(NETID_UNSET);
+}
+
+static jint android_net_utils_getNetworkBoundToProcess(JNIEnv *env, jobject thiz)
+{
+ return getNetworkForProcess();
+}
+
+static void android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz, jint netId)
+{
+ setNetworkForResolv(netId);
+}
+
+static void android_net_utils_unbindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz)
+{
+ setNetworkForResolv(NETID_UNSET);
+}
+
+static void android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket, jint netId)
+{
+ setNetworkForSocket(netId, socket);
+}
+
// ----------------------------------------------------------------------------
/*
@@ -267,6 +299,12 @@
{ "releaseDhcpLease", "(Ljava/lang/String;)Z", (void *)android_net_utils_releaseDhcpLease },
{ "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
{ "markSocket", "(II)V", (void*) android_net_utils_markSocket },
+ { "bindProcessToNetwork", "(I)V", (void*) android_net_utils_bindProcessToNetwork },
+ { "getNetworkBoundToProcess", "()I", (void*) android_net_utils_getNetworkBoundToProcess },
+ { "unbindProcessToNetwork", "()V", (void*) android_net_utils_unbindProcessToNetwork },
+ { "bindProcessToNetworkForHostResolution", "(I)V", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
+ { "unbindProcessToNetworkForHostResolution", "()V", (void*) android_net_utils_unbindProcessToNetworkForHostResolution },
+ { "bindSocketToNetwork", "(II)V", (void*) android_net_utils_bindSocketToNetwork },
};
int register_android_net_NetworkUtils(JNIEnv* env)
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 6ae02e0..a590dbf 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -211,8 +211,9 @@
outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
- uint64_t bits = env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits);
- if (bits) {
+ BitSet64 bits =
+ BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits));
+ if (!bits.isEmpty()) {
jfloatArray valuesArray = jfloatArray(env->GetObjectField(pointerCoordsObj,
gPointerCoordsClassInfo.mPackedAxisValues));
if (valuesArray) {
@@ -221,11 +222,9 @@
uint32_t index = 0;
do {
- uint32_t axis = __builtin_ctzll(bits);
- uint64_t axisBit = 1LL << axis;
- bits &= ~axisBit;
+ uint32_t axis = bits.clearFirstMarkedBit();
outRawPointerCoords->setAxisValue(axis, values[index++]);
- } while (bits);
+ } while (!bits.isEmpty());
env->ReleasePrimitiveArrayCritical(valuesArray, values, JNI_ABORT);
env->DeleteLocalRef(valuesArray);
@@ -275,21 +274,19 @@
env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation,
rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
- const uint64_t unpackedAxisBits = 0
- | (1LL << AMOTION_EVENT_AXIS_X)
- | (1LL << AMOTION_EVENT_AXIS_Y)
- | (1LL << AMOTION_EVENT_AXIS_PRESSURE)
- | (1LL << AMOTION_EVENT_AXIS_SIZE)
- | (1LL << AMOTION_EVENT_AXIS_TOUCH_MAJOR)
- | (1LL << AMOTION_EVENT_AXIS_TOUCH_MINOR)
- | (1LL << AMOTION_EVENT_AXIS_TOOL_MAJOR)
- | (1LL << AMOTION_EVENT_AXIS_TOOL_MINOR)
- | (1LL << AMOTION_EVENT_AXIS_ORIENTATION);
-
uint64_t outBits = 0;
- uint64_t remainingBits = rawPointerCoords->bits & ~unpackedAxisBits;
- if (remainingBits) {
- uint32_t packedAxesCount = __builtin_popcountll(remainingBits);
+ BitSet64 bits = BitSet64(rawPointerCoords->bits);
+ bits.clearBit(AMOTION_EVENT_AXIS_X);
+ bits.clearBit(AMOTION_EVENT_AXIS_Y);
+ bits.clearBit(AMOTION_EVENT_AXIS_PRESSURE);
+ bits.clearBit(AMOTION_EVENT_AXIS_SIZE);
+ bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
+ bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MINOR);
+ bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MAJOR);
+ bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MINOR);
+ bits.clearBit(AMOTION_EVENT_AXIS_ORIENTATION);
+ if (!bits.isEmpty()) {
+ uint32_t packedAxesCount = bits.count();
jfloatArray outValuesArray = obtainPackedAxisValuesArray(env, packedAxesCount,
outPointerCoordsObj);
if (!outValuesArray) {
@@ -302,12 +299,10 @@
const float* values = rawPointerCoords->values;
uint32_t index = 0;
do {
- uint32_t axis = __builtin_ctzll(remainingBits);
- uint64_t axisBit = 1LL << axis;
- remainingBits &= ~axisBit;
- outBits |= axisBit;
+ uint32_t axis = bits.clearFirstMarkedBit();
+ outBits |= BitSet64::valueForBit(axis);
outValues[index++] = rawPointerCoords->getAxisValue(axis);
- } while (remainingBits);
+ } while (!bits.isEmpty());
env->ReleasePrimitiveArrayCritical(outValuesArray, outValues, 0);
env->DeleteLocalRef(outValuesArray);
diff --git a/core/res/res/anim/slide_in_micro.xml b/core/res/res/anim/slide_in_micro.xml
new file mode 100644
index 0000000..6320e80
--- /dev/null
+++ b/core/res/res/anim/slide_in_micro.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/slide_in_micro.xml
+**
+** Copyright 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate android:fromXDelta="100%p" android:toXDelta="0"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
+</set>
diff --git a/core/res/res/anim/slide_out_micro.xml b/core/res/res/anim/slide_out_micro.xml
new file mode 100644
index 0000000..4cb6df0
--- /dev/null
+++ b/core/res/res/anim/slide_out_micro.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/slide_out_micro.xml
+**
+** Copyright 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate android:fromXDelta="0" android:toXDelta="100%p"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
+</set>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 8f83ab2..3180e58 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -19,13 +19,9 @@
-->
<resources>
<!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_width">200dp</dimen>
+ <dimen name="thumbnail_width">512dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_height">177dp</dimen>
- <!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="recents_thumbnail_width">512dp</dimen>
- <!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="recents_thumbnail_height">512dp</dimen>
+ <dimen name="thumbnail_height">512dp</dimen>
<!-- The maximum number of action buttons that should be permitted within
an action bar/action mode. This will be used to determine how many
showAsAction="ifRoom" items can fit. "always" items can override this. -->
diff --git a/core/res/res/values-sw720dp/dimens.xml b/core/res/res/values-sw720dp/dimens.xml
index 040bb5b..21235ec 100644
--- a/core/res/res/values-sw720dp/dimens.xml
+++ b/core/res/res/values-sw720dp/dimens.xml
@@ -35,13 +35,9 @@
<item type="dimen" name="dialog_fixed_height_minor">90%</item>
<!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_width">230dp</dimen>
+ <dimen name="thumbnail_width">640dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_height">135dp</dimen>
- <!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="recents_thumbnail_width">512dp</dimen>
- <!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="recents_thumbnail_height">512dp</dimen>
+ <dimen name="thumbnail_height">640dp</dimen>
<!-- Preference activity, vertical padding for the header list -->
<dimen name="preference_screen_header_vertical_padding">32dp</dimen>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index c05dfca..9eb6f74 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4057,6 +4057,9 @@
The default is one.
See {@link android.widget.GridLayout.Spec}. -->
<attr name="layout_rowSpan" format="integer" min="1" />
+ <!-- The relative proportion of horizontal space that should be allocated to this view
+ during excess space distribution. -->
+ <attr name="layout_rowWeight" format="float" />
<!-- The column boundary delimiting the left of the group of cells
occupied by this view. -->
<attr name="layout_column" />
@@ -4065,6 +4068,9 @@
The default is one.
See {@link android.widget.GridLayout.Spec}. -->
<attr name="layout_columnSpan" format="integer" min="1" />
+ <!-- The relative proportion of vertical space that should be allocated to this view
+ during excess space distribution. -->
+ <attr name="layout_columnWeight" format="float" />
<!-- Gravity specifies how a component should be placed in its group of cells.
The default is LEFT | BASELINE.
See {@link android.widget.GridLayout.LayoutParams#setGravity(int)}. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b1f256e..acfbe2d 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -885,41 +885,57 @@
be passed a persistable Bundle in their Intent.extras. -->
<attr name="persistable" format="boolean" />
- <!-- Specify whether this activity should always be launched in doc-centric mode. For
- values other than <code>none</code> the activity must be defined with
- {@link android.R.attr#launchMode} <code>standard</code> or <code>singleTop</code>.
- This attribute can be overridden by {@link
- android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}.
+ <!-- This attribute specifies that an activity shall become the root activity of a
+ new task each time it is launched. Using this attribute permits the user to
+ have multiple documents from the same applications appear in the recent tasks list.
- <p>If this attribute is not specified, <code>none</code> will be used.
- Note that this launch behavior can be changed in some ways at runtime
- through the {@link android.content.Intent} flags
- {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}. -->
+ <p>Such a document is any kind of item for which an application may want to
+ maintain multiple simultaneous instances. Examples might be text files, web
+ pages, spreadsheets, or emails. Each such document will be in a separate
+ task in the recent taskss list.
+
+ <p>This attribute is equivalent to adding the flag {@link
+ android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} to every Intent used to launch
+ the activity.
+
+ <p>The documentLaunchMode attribute may be assigned one of three values, "none",
+ "intoExisting" and "always", described in detail below. For values other than
+ <code>none</code> the activity must be defined with
+ {@link android.R.attr#launchMode} <code>standard</code> or <code>singleTop</code>.
+ If this attribute is not specified, <code>none</code> will be used.
+ Note that <code>none</code> can be overridden at run time if the Intent used
+ to launch it contains the flag {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}.
+ Similarly <code>intoExisting</code> will be overridden by the flag
+ {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} combined with
+ {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}. -->
<attr name="documentLaunchMode">
<!-- The default mode, which will create a new task only when
{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK
Intent.FLAG_ACTIVITY_NEW_TASK} is set. -->
<enum name="none" value="0" />
- <!-- All tasks will be searched for a matching Intent. If one is found
- That task will cleared and restarted with the root activity receiving a call
+ <!-- All tasks will be searched for one whose base Intent's ComponentName and
+ data URI match those of the launching Intent. If such a task is found
+ that task will be cleared and restarted with the root activity receiving a call
to {@link android.app.Activity#onNewIntent Activity.onNewIntent}. If no
such task is found a new task will be created.
- This is the equivalent of with {@link
+ <p>This is the equivalent of launching an activity with {@link
android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_DOCUMENT}
- without {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
- Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. -->
+ set and without {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK} set. -->
<enum name="intoExisting" value="1" />
- <!-- A new task rooted at this activity will be created.
- This is the equivalent of with {@link
+ <!-- A new task rooted at this activity will be created. This will happen whether or
+ not there is an existing task whose ComponentName and data URI match
+ that of the launcing intent This is the equivalent of launching an activity
+ with {@link
android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_DOCUMENT}
- paired with {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
- Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. -->
+ and {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK} both set. -->
<enum name="always" value="2" />
</attr>
- <!-- Tasks launched by activities with this attribute will remain in the recent task
+ <!-- Tasks launched by activities with this attribute will remain in the recent tasks
list until the last activity in the task is completed. When that happens the task
- will be automatically removed from the recent task list.
+ will be automatically removed from the recent tasks list.
This attribute is the equivalent of {@link
android.content.Intent#FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 52b021f..38f00fe 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -19,13 +19,9 @@
-->
<resources>
<!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_width">164dp</dimen>
+ <dimen name="thumbnail_width">256dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_height">145dp</dimen>
- <!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="recents_thumbnail_width">256dp</dimen>
- <!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="recents_thumbnail_height">256dp</dimen>
+ <dimen name="thumbnail_height">256dp</dimen>
<!-- The standard size (both width and height) of an application icon that
will be displayed in the app launcher and elsewhere. -->
<dimen name="app_icon_size">48dip</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ce0d2d5..42ed318 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2178,12 +2178,11 @@
<public type="attr" name="contentInsetLeft" />
<public type="attr" name="contentInsetRight" />
<public type="attr" name="paddingMode" />
+ <public type="attr" name="layout_rowWeight" />
+ <public type="attr" name="layout_columnWeight" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
- <public type="dimen" name="recents_thumbnail_height" />
- <public type="dimen" name="recents_thumbnail_width" />
-
<public-padding type="id" name="l_resource_pad" end="0x01020040" />
<public type="id" name="mask" />
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index f6cd9e8..1f4c88d 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -231,14 +231,6 @@
<item name="windowExitAnimation">@anim/fast_fade_out</item>
</style>
- <!-- Window animations for swipe-dismissable windows. {@hide} -->
- <style name="Animation.SwipeDismiss">
- <item name="taskOpenEnterAnimation">@anim/swipe_window_enter</item>
- <item name="taskOpenExitAnimation">@anim/swipe_window_exit</item>
- <item name="taskCloseEnterAnimation">@anim/swipe_window_enter</item>
- <item name="taskCloseExitAnimation">@anim/swipe_window_exit</item>
- </style>
-
<!-- Status Bar Styles -->
<style name="TextAppearance.StatusBar">
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml
index 5bac1f9..0c854d3 100644
--- a/core/res/res/values/styles_micro.xml
+++ b/core/res/res/values/styles_micro.xml
@@ -14,6 +14,19 @@
limitations under the License.
-->
<resources>
+ <style name="Animation.Micro"/>
+
+ <style name="Animation.Micro.Activity" parent="Animation.Holo.Activity">
+ <item name="activityOpenEnterAnimation">@anim/slide_in_micro</item>
+ <item name="activityOpenExitAnimation">@null</item>
+ <item name="activityCloseEnterAnimation">@null</item>
+ <item name="activityCloseExitAnimation">@anim/slide_out_micro</item>
+ <item name="taskOpenEnterAnimation">@anim/slide_in_micro</item>
+ <item name="taskOpenExitAnimation">@null</item>
+ <item name="taskCloseEnterAnimation">@null</item>
+ <item name="taskCloseExitAnimation">@anim/slide_out_micro</item>
+ </style>
+
<style name="AlertDialog.Micro" parent="AlertDialog.Holo.Light">
<item name="fullDark">@null</item>
<item name="topDark">@null</item>
diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml
index ebdab5b..7e0467b 100644
--- a/core/res/res/values/themes_micro.xml
+++ b/core/res/res/values/themes_micro.xml
@@ -19,15 +19,14 @@
<item name="alertDialogStyle">@style/AlertDialog.Micro</item>
<item name="dialogTheme">@style/Theme.Micro.Dialog</item>
<item name="textViewStyle">@style/Widget.Micro.TextView</item>
-
<item name="numberPickerStyle">@style/Widget.Micro.NumberPicker</item>
- <item name="windowAnimationStyle">@style/Animation.SwipeDismiss</item>
+ <item name="windowAnimationStyle">@style/Animation.Micro.Activity</item>
<item name="windowBackground">@color/black</item>
<item name="windowContentOverlay">@null</item>
<item name="windowIsFloating">false</item>
<item name="windowIsTranslucent">true</item>
<item name="windowSwipeToDismiss">true</item>
- </style>
+ </style>
<style name="Theme.Micro.Light" parent="Theme.Holo.Light.NoActionBar">
<item name="alertDialogTheme">@style/Theme.Micro.Dialog.Alert</item>
@@ -35,7 +34,7 @@
<item name="dialogTheme">@style/Theme.Micro.Dialog</item>
<item name="textViewStyle">@style/Widget.Micro.TextView</item>
<item name="numberPickerStyle">@style/Widget.Micro.NumberPicker</item>
- <item name="windowAnimationStyle">@style/Animation.SwipeDismiss</item>
+ <item name="windowAnimationStyle">@style/Animation.Micro.Activity</item>
<item name="windowBackground">@color/white</item>
<item name="windowContentOverlay">@null</item>
<item name="windowIsFloating">false</item>
diff --git a/core/tests/coretests/src/android/os/FileBridgeTest.java b/core/tests/coretests/src/android/os/FileBridgeTest.java
new file mode 100644
index 0000000..d4f6b1f
--- /dev/null
+++ b/core/tests/coretests/src/android/os/FileBridgeTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.os;
+
+import android.os.FileBridge.FileBridgeOutputStream;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+
+import libcore.io.Streams;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Random;
+
+public class FileBridgeTest extends AndroidTestCase {
+
+ private File file;
+ private FileOutputStream fileOs;
+ private FileBridge bridge;
+ private FileBridgeOutputStream client;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ file = getContext().getFileStreamPath("meow.dat");
+ file.delete();
+
+ fileOs = new FileOutputStream(file);
+
+ bridge = new FileBridge();
+ bridge.setTargetFile(fileOs.getFD());
+ bridge.start();
+ client = new FileBridgeOutputStream(bridge.getClientSocket());
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ fileOs.close();
+ file.delete();
+ }
+
+ private void assertOpen() throws Exception {
+ assertFalse("expected open", bridge.isClosed());
+ }
+
+ private void closeAndAssertClosed() throws Exception {
+ client.close();
+
+ // Wait a beat for things to settle down
+ SystemClock.sleep(200);
+ assertTrue("expected closed", bridge.isClosed());
+ }
+
+ private void assertContents(byte[] expected) throws Exception {
+ MoreAsserts.assertEquals(expected, Streams.readFully(new FileInputStream(file)));
+ }
+
+ public void testNoWriteNoSync() throws Exception {
+ assertOpen();
+ closeAndAssertClosed();
+ }
+
+ public void testNoWriteSync() throws Exception {
+ assertOpen();
+ client.flush();
+ closeAndAssertClosed();
+ }
+
+ public void testWriteNoSync() throws Exception {
+ assertOpen();
+ client.write("meow".getBytes(StandardCharsets.UTF_8));
+ closeAndAssertClosed();
+ assertContents("meow".getBytes(StandardCharsets.UTF_8));
+ }
+
+ public void testWriteSync() throws Exception {
+ assertOpen();
+ client.write("cake".getBytes(StandardCharsets.UTF_8));
+ client.flush();
+ closeAndAssertClosed();
+ assertContents("cake".getBytes(StandardCharsets.UTF_8));
+ }
+
+ public void testWriteSyncWrite() throws Exception {
+ assertOpen();
+ client.write("meow".getBytes(StandardCharsets.UTF_8));
+ client.flush();
+ client.write("cake".getBytes(StandardCharsets.UTF_8));
+ closeAndAssertClosed();
+ assertContents("meowcake".getBytes(StandardCharsets.UTF_8));
+ }
+
+ public void testEmptyWrite() throws Exception {
+ assertOpen();
+ client.write(new byte[0]);
+ closeAndAssertClosed();
+ assertContents(new byte[0]);
+ }
+
+ public void testWriteAfterClose() throws Exception {
+ assertOpen();
+ client.write("meow".getBytes(StandardCharsets.UTF_8));
+ closeAndAssertClosed();
+ try {
+ client.write("cake".getBytes(StandardCharsets.UTF_8));
+ fail("wrote after close!");
+ } catch (IOException expected) {
+ }
+ assertContents("meow".getBytes(StandardCharsets.UTF_8));
+ }
+
+ public void testRandomWrite() throws Exception {
+ final Random r = new Random();
+ final ByteArrayOutputStream result = new ByteArrayOutputStream();
+
+ for (int i = 0; i < 512; i++) {
+ final byte[] test = new byte[r.nextInt(24169)];
+ r.nextBytes(test);
+ result.write(test);
+ client.write(test);
+ client.flush();
+ }
+
+ closeAndAssertClosed();
+ assertContents(result.toByteArray());
+ }
+
+ public void testGiantWrite() throws Exception {
+ final byte[] test = new byte[263401];
+ new Random().nextBytes(test);
+
+ assertOpen();
+ client.write(test);
+ closeAndAssertClosed();
+ assertContents(test);
+ }
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
new file mode 100644
index 0000000..d649154
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
@@ -0,0 +1,42 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+
+## The application with a minimal main dex
+include $(CLEAR_VARS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := MultiDexLegacyAndException
+
+mainDexList:= \
+ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
+
+LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
+
+include $(BUILD_PACKAGE)
+
+$(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
+ $(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
+ echo "com/android/multidexlegacyandexception/Test.class" >> $@
+
+$(built_dex_intermediate): $(mainDexList)
+
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml
new file mode 100644
index 0000000..7fff711
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.multidexlegacyandexception"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18"/>
+
+ <application
+ android:name="com.android.multidexlegacyandexception.TestApplication"
+ android:label="multidexlegacyandexception"
+ >
+ <activity
+ android:name="com.android.multidexlegacyandexception.MainActivity"
+ android:label="multidexlegacyandexception" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.multidexlegacyandexception"
+ android:label="Test for MultiDexLegacyAndException" />
+</manifest>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/layout/activity_main.xml b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/layout/activity_main.xml
new file mode 100644
index 0000000..37eb613
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/layout/activity_main.xml
@@ -0,0 +1,13 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ tools:context=".MainActivity" >
+
+ <TextView
+ android:id="@+id/label_nb"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/label_nb" />
+
+</RelativeLayout>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/values/strings.xml b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/values/strings.xml
new file mode 100644
index 0000000..e56e049
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/values/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">MultidexLegacyAndException</string>
+ <string name="action_settings">Settings</string>
+ <string name="label_nb">Here\'s the count: </string>
+
+</resources>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyByIntermediateException.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyByIntermediateException.java
new file mode 100644
index 0000000..d6883ec
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyByIntermediateException.java
@@ -0,0 +1,21 @@
+/*
+ * 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.multidexlegacyandexception;
+
+public class CaughtOnlyByIntermediateException extends RuntimeException {
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyException.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyException.java
new file mode 100644
index 0000000..4903e01
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyException.java
@@ -0,0 +1,21 @@
+/*
+ * 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.multidexlegacyandexception;
+
+public class CaughtOnlyException extends RuntimeException {
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ClassInSecondaryDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ClassInSecondaryDex.java
new file mode 100644
index 0000000..b08a11a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ClassInSecondaryDex.java
@@ -0,0 +1,84 @@
+/*
+ * 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.multidexlegacyandexception;
+
+public class ClassInSecondaryDex {
+ private boolean condition;
+
+ public ClassInSecondaryDex(boolean condition) {
+ this.condition = condition;
+ }
+
+ public void canThrow1() throws ExceptionInMainDex, ExceptionInMainDex2,
+ ExceptionInSecondaryDexWithSuperInMain {
+ if (condition) {
+ throw new ExceptionInMainDex();
+ }
+ }
+
+ public void canThrow2() throws ExceptionInSecondaryDex, ExceptionInSecondaryDex2,
+ ExceptionInSecondaryDexWithSuperInMain {
+ if (condition) {
+ throw new ExceptionInSecondaryDex();
+ }
+ }
+
+ public static void canThrowAll(Throwable toThrow) throws Throwable {
+ if (toThrow != null) {
+ throw toThrow;
+ }
+ }
+
+ public int get1() {
+ try {
+ canThrow1();
+ canThrow2();
+ return 1;
+ } catch (ExceptionInMainDex e) {
+ return 10;
+ } catch (ExceptionInSecondaryDex e) {
+ return 11;
+ } catch (OutOfMemoryError e) {
+ return 12;
+ } catch (CaughtOnlyException e) {
+ return 17;
+ } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex e) {
+ return 23;
+ }
+ }
+
+ public int get2() {
+ try {
+ canThrow2();
+ canThrow1();
+ return 1;
+ } catch (ExceptionInMainDex e) {
+ return 10;
+ } catch (ExceptionInSecondaryDex e) {
+ return 11;
+ } catch (OutOfMemoryError e) {
+ return 12;
+ } catch (CaughtOnlyException e) {
+ return 17;
+ } catch (SuperExceptionInSecondaryDex e) {
+ return 23;
+ } catch (SuperExceptionInMainDex e) {
+ return 27;
+ }
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex.java
new file mode 100644
index 0000000..7fc3d73
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex.java
@@ -0,0 +1,20 @@
+/*
+ * 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.multidexlegacyandexception;
+
+public class ExceptionInMainDex extends SuperExceptionInMainDex {
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex2.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex2.java
new file mode 100644
index 0000000..3fbeac6
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex2.java
@@ -0,0 +1,20 @@
+/*
+ * 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.multidexlegacyandexception;
+
+public class ExceptionInMainDex2 extends SuperExceptionInMainDex {
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex.java
new file mode 100644
index 0000000..9401c05
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex.java
@@ -0,0 +1,21 @@
+/*
+ * 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.multidexlegacyandexception;
+
+public class ExceptionInSecondaryDex extends SuperExceptionInSecondaryDex {
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex2.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex2.java
new file mode 100644
index 0000000..d1aa103
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex2.java
@@ -0,0 +1,21 @@
+/*
+ * 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.multidexlegacyandexception;
+
+public class ExceptionInSecondaryDex2 extends SuperExceptionInSecondaryDex {
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDexWithSuperInMain.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDexWithSuperInMain.java
new file mode 100644
index 0000000..9327882
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDexWithSuperInMain.java
@@ -0,0 +1,21 @@
+/*
+ * 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.multidexlegacyandexception;
+
+public class ExceptionInSecondaryDexWithSuperInMain extends SuperExceptionInMainDex {
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/IntermediateClass.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/IntermediateClass.java
new file mode 100644
index 0000000..dfdc4af
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/IntermediateClass.java
@@ -0,0 +1,107 @@
+/*
+ * 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.multidexlegacyandexception;
+
+public class IntermediateClass {
+
+ public static int get1(boolean condition) {
+ return new ClassInSecondaryDex(condition).get1();
+ }
+
+ public static int get2(boolean condition) {
+ return new ClassInSecondaryDex(condition).get2();
+ }
+
+ public static int get3(boolean condition) {
+ ClassInSecondaryDex thrower = new ClassInSecondaryDex(condition);
+ try {
+ thrower.canThrow2();
+ thrower.canThrow1();
+ return 1;
+ } catch (ExceptionInMainDex e) {
+ return 10;
+ } catch (ExceptionInSecondaryDex e) {
+ return 11;
+ } catch (ExceptionInMainDex2 e) {
+ return 10;
+ } catch (ExceptionInSecondaryDex2 e) {
+ return 11;
+ } catch (OutOfMemoryError e) {
+ return 12;
+ } catch (CaughtOnlyException e) {
+ return 17;
+ } catch (ExceptionInSecondaryDexWithSuperInMain e) {
+ return 39;
+ } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex|CaughtOnlyByIntermediateException e) {
+ return 23;
+ }
+ }
+
+ public static int get4(boolean condition) {
+ ClassInSecondaryDex thrower = new ClassInSecondaryDex(condition);
+ try {
+ thrower.canThrow2();
+ thrower.canThrow1();
+ return 1;
+ } catch (ExceptionInSecondaryDexWithSuperInMain e) {
+ return 39;
+ } catch (ExceptionInSecondaryDex e) {
+ return 11;
+ } catch (ExceptionInSecondaryDex2 e) {
+ return 11;
+ } catch (OutOfMemoryError e) {
+ return 12;
+ } catch (ExceptionInMainDex2 e) {
+ return 10;
+ } catch (ExceptionInMainDex e) {
+ return 10;
+ } catch (CaughtOnlyException e) {
+ return 17;
+ } catch (SuperExceptionInSecondaryDex e) {
+ } catch (SuperExceptionInMainDex e) {
+ } catch (CaughtOnlyByIntermediateException e) {
+ return 35;
+ }
+ return 39;
+ }
+
+
+ public static int get5(Throwable thrown) {
+ try {
+ ClassInSecondaryDex.canThrowAll(thrown);
+ return 1;
+ } catch (ExceptionInMainDex e) {
+ return 10;
+ } catch (ExceptionInSecondaryDex e) {
+ return 11;
+ } catch (ExceptionInMainDex2 e) {
+ return 12;
+ } catch (ExceptionInSecondaryDex2 e) {
+ return 13;
+ } catch (OutOfMemoryError e) {
+ return 14;
+ } catch (CaughtOnlyException e) {
+ return 17;
+ } catch (ExceptionInSecondaryDexWithSuperInMain e) {
+ return 39;
+ } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex|CaughtOnlyByIntermediateException e) {
+ return 23;
+ } catch (Throwable e) {
+ return 37;
+ }
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MainActivity.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MainActivity.java
new file mode 100644
index 0000000..dd2ce7a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MainActivity.java
@@ -0,0 +1,44 @@
+/*
+ * 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.multidexlegacyandexception;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class MainActivity extends Activity {
+
+ public MainActivity() {
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ }
+
+ public int get1(boolean condition) {
+ return IntermediateClass.get1(condition);
+ }
+
+ public int get2(boolean condition) {
+ return IntermediateClass.get2(condition);
+ }
+ public int get3(boolean condition) {
+ return MiniIntermediateClass.get3(condition);
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MiniIntermediateClass.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MiniIntermediateClass.java
new file mode 100644
index 0000000..5957662
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MiniIntermediateClass.java
@@ -0,0 +1,35 @@
+/*
+ * 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.multidexlegacyandexception;
+
+public class MiniIntermediateClass {
+
+ public static int get3(boolean condition) {
+ ClassInSecondaryDex thrower = new ClassInSecondaryDex(condition);
+ try {
+ thrower.canThrow2();
+ thrower.canThrow1();
+ return 1;
+ } catch (ExceptionInMainDex e) {
+ return 10;
+ } catch (ExceptionInSecondaryDex e) {
+ return 11;
+ } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex e) {
+ return 23;
+ }
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInMainDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInMainDex.java
new file mode 100644
index 0000000..c94b30a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInMainDex.java
@@ -0,0 +1,21 @@
+/*
+ * 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.multidexlegacyandexception;
+
+public class SuperExceptionInMainDex extends Exception {
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInSecondaryDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInSecondaryDex.java
new file mode 100644
index 0000000..6366fae
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInSecondaryDex.java
@@ -0,0 +1,21 @@
+/*
+ * 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.multidexlegacyandexception;
+
+public class SuperExceptionInSecondaryDex extends Exception {
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/Test.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/Test.java
new file mode 100644
index 0000000..5e931bc
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/Test.java
@@ -0,0 +1,57 @@
+/*
+ * 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.multidexlegacyandexception;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+ * Run the tests with: <code>adb shell am instrument -w
+ com.android.multidexlegacyandexception/android.test.InstrumentationTestRunner
+</code>
+ */
+public class Test extends ActivityInstrumentationTestCase2<MainActivity> {
+ public Test() {
+ super(MainActivity.class);
+ }
+
+ public void testExceptionInMainDex() {
+ assertEquals(10, TestApplication.get(true));
+ }
+
+ public void testExceptionInSecondaryDex() {
+ assertEquals(10, getActivity().get1(true));
+ assertEquals(11, getActivity().get2(true));
+ }
+
+ public void testExceptionInIntermediate() {
+ assertEquals(11, IntermediateClass.get3(true));
+ assertEquals(11, MiniIntermediateClass.get3(true));
+ assertEquals(11, IntermediateClass.get4(true));
+ assertEquals(1, IntermediateClass.get5(null));
+ assertEquals(10, IntermediateClass.get5(new ExceptionInMainDex()));
+ assertEquals(11, IntermediateClass.get5(new ExceptionInSecondaryDex()));
+ assertEquals(12, IntermediateClass.get5(new ExceptionInMainDex2()));
+ assertEquals(13, IntermediateClass.get5(new ExceptionInSecondaryDex2()));
+ assertEquals(14, IntermediateClass.get5(new OutOfMemoryError()));
+ assertEquals(17, IntermediateClass.get5(new CaughtOnlyException()));
+ assertEquals(39, IntermediateClass.get5(new ExceptionInSecondaryDexWithSuperInMain()));
+ assertEquals(23, IntermediateClass.get5(new SuperExceptionInSecondaryDex()));
+ assertEquals(23, IntermediateClass.get5(new SuperExceptionInMainDex()));
+ assertEquals(23, IntermediateClass.get5(new CaughtOnlyByIntermediateException()));
+ assertEquals(37, IntermediateClass.get5(new ArrayIndexOutOfBoundsException()));
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java
new file mode 100644
index 0000000..dece9a4
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java
@@ -0,0 +1,45 @@
+/*
+ * 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.multidexlegacyandexception;
+
+import android.support.multidex.MultiDexApplication;
+
+public class TestApplication extends MultiDexApplication {
+
+ private static void canThrow1(boolean condition) throws ExceptionInMainDex {
+ if (condition) {
+ throw new ExceptionInMainDex();
+ }
+ }
+
+
+ public static int get(boolean condition) {
+ try {
+ canThrow1(condition);
+ return 1;
+ } catch (ExceptionInMainDex e) {
+ return 10;
+ } catch (OutOfMemoryError e) {
+ return 12;
+ } catch (CaughtOnlyException e) {
+ return 17;
+ } catch (SuperExceptionInMainDex e) {
+ return 27;
+ }
+ }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
index 20fe465..7b83999 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
@@ -31,7 +31,7 @@
* Empty service for testing legacy multidex. Access more than 64k methods but some are required at
* init, some only at verification and others during execution.
*/
-public abstract class AbstractService extends Service {
+public abstract class AbstractService extends Service implements Runnable {
private final String TAG = "MultidexLegacyTestService" + getId();
private int instanceFieldNotInited;
@@ -47,6 +47,12 @@
@Override
public void onCreate() {
Log.i(TAG, "onCreate");
+ new Thread(this).start();
+
+ }
+
+ @Override
+ public void run() {
Context applicationContext = getApplicationContext();
File resultFile = new File(applicationContext.getFilesDir(), getId());
try {
@@ -84,7 +90,6 @@
} catch (IOException e) {
e.printStackTrace();
}
-
}
@Override
diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
index 0f343b1..459bed5 100644
--- a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
@@ -25,8 +25,9 @@
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
-import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
+import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl;
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
+import com.android.internal.inputmethod.InputMethodUtils;
import java.util.ArrayList;
import java.util.Arrays;
@@ -39,6 +40,7 @@
private static final boolean DUMMY_FORCE_DEFAULT = false;
private static final int DUMMY_IS_DEFAULT_RES_ID = 0;
private static final String SYSTEM_LOCALE = "en_US";
+ private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
private static InputMethodSubtype createDummySubtype(final String locale) {
final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder();
@@ -64,142 +66,141 @@
si.exported = true;
si.nonLocalizedLabel = imeLabel;
ri.serviceInfo = si;
- final List<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
- for (String subtypeLocale : subtypeLocales) {
- subtypes.add(createDummySubtype(subtypeLocale));
+ List<InputMethodSubtype> subtypes = null;
+ if (subtypeLocales != null) {
+ subtypes = new ArrayList<InputMethodSubtype>();
+ for (String subtypeLocale : subtypeLocales) {
+ subtypes.add(createDummySubtype(subtypeLocale));
+ }
}
final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod);
- for (int i = 0; i < subtypes.size(); ++i) {
- final String subtypeLocale = subtypeLocales.get(i);
- items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale,
- SYSTEM_LOCALE));
+ if (subtypes == null) {
+ items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi,
+ NOT_A_SUBTYPE_ID, null, SYSTEM_LOCALE));
+ } else {
+ for (int i = 0; i < subtypes.size(); ++i) {
+ final String subtypeLocale = subtypeLocales.get(i);
+ items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale,
+ SYSTEM_LOCALE));
+ }
}
}
- private static List<ImeSubtypeListItem> createTestData() {
+ private static List<ImeSubtypeListItem> createEnabledImeSubtypes() {
final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
- addDummyImeSubtypeListItems(items, "switchAwareLatinIme", "switchAwareLatinIme",
- Arrays.asList("en_US", "es_US", "fr"),
+ addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme", Arrays.asList("en_US", "fr"),
true /* supportsSwitchingToNextInputMethod*/);
- addDummyImeSubtypeListItems(items, "nonSwitchAwareLatinIme", "nonSwitchAwareLatinIme",
+ addDummyImeSubtypeListItems(items, "switchUnawareLatinIme", "switchUnawareLatinIme",
Arrays.asList("en_UK", "hi"),
false /* supportsSwitchingToNextInputMethod*/);
- addDummyImeSubtypeListItems(items, "switchAwareJapaneseIme", "switchAwareJapaneseIme",
- Arrays.asList("ja_JP"),
+ addDummyImeSubtypeListItems(items, "subtypeUnawareIme", "subtypeUnawareIme", null,
+ false /* supportsSwitchingToNextInputMethod*/);
+ addDummyImeSubtypeListItems(items, "JapaneseIme", "JapaneseIme", Arrays.asList("ja_JP"),
true /* supportsSwitchingToNextInputMethod*/);
- addDummyImeSubtypeListItems(items, "nonSwitchAwareJapaneseIme", "nonSwitchAwareJapaneseIme",
- Arrays.asList("ja_JP"),
+ addDummyImeSubtypeListItems(items, "switchUnawareJapaneseIme", "switchUnawareJapaneseIme",
+ Arrays.asList("ja_JP"), false /* supportsSwitchingToNextInputMethod*/);
+ return items;
+ }
+
+ private static List<ImeSubtypeListItem> createDisabledImeSubtypes() {
+ final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
+ addDummyImeSubtypeListItems(items,
+ "UnknownIme", "UnknownIme",
+ Arrays.asList("en_US", "hi"),
+ true /* supportsSwitchingToNextInputMethod*/);
+ addDummyImeSubtypeListItems(items,
+ "UnknownSwitchingUnawareIme", "UnknownSwitchingUnawareIme",
+ Arrays.asList("en_US"),
+ false /* supportsSwitchingToNextInputMethod*/);
+ addDummyImeSubtypeListItems(items, "UnknownSubtypeUnawareIme",
+ "UnknownSubtypeUnawareIme", null,
false /* supportsSwitchingToNextInputMethod*/);
return items;
}
- @SmallTest
- public void testGetNextInputMethodImplWithNotOnlyCurrentIme() throws Exception {
- final List<ImeSubtypeListItem> imList = createTestData();
-
- final boolean ONLY_CURRENT_IME = false;
- ImeSubtypeListItem currentIme;
- ImeSubtypeListItem nextIme;
-
- // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US"
- currentIme = imList.get(0);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(1), nextIme);
- // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr"
- currentIme = imList.get(1);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(2), nextIme);
- // "switchAwareLatinIme/fr" -> "switchAwareJapaneseIme/ja_JP"
- currentIme = imList.get(2);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(5), nextIme);
- // "switchAwareJapaneseIme/ja_JP" -> "switchAwareLatinIme/en_US"
- currentIme = imList.get(5);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(0), nextIme);
-
- // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi"
- currentIme = imList.get(3);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(4), nextIme);
- // "nonSwitchAwareLatinIme/hi" -> "nonSwitchAwareJapaneseIme/ja_JP"
- currentIme = imList.get(4);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(6), nextIme);
- // "nonSwitchAwareJapaneseIme/ja_JP" -> "nonSwitchAwareLatinIme/en_UK"
- currentIme = imList.get(6);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(3), nextIme);
+ private void assertNextInputMethod(final ControllerImpl controller,
+ final boolean onlyCurrentIme,
+ final ImeSubtypeListItem currentItem, final ImeSubtypeListItem nextItem) {
+ InputMethodSubtype subtype = null;
+ if (currentItem.mSubtypeName != null) {
+ subtype = createDummySubtype(currentItem.mSubtypeName.toString());
+ }
+ final ImeSubtypeListItem nextIme = controller.getNextInputMethod(onlyCurrentIme,
+ currentItem.mImi, subtype);
+ assertEquals(nextItem, nextIme);
}
@SmallTest
- public void testGetNextInputMethodImplWithOnlyCurrentIme() throws Exception {
- final List<ImeSubtypeListItem> imList = createTestData();
+ public void testControllerImpl() throws Exception {
+ final List<ImeSubtypeListItem> disabledItems = createDisabledImeSubtypes();
+ final ImeSubtypeListItem disabledIme_en_US = disabledItems.get(0);
+ final ImeSubtypeListItem disabledIme_hi = disabledItems.get(1);
+ final ImeSubtypeListItem disabledSwitchingUnawareIme = disabledItems.get(2);
+ final ImeSubtypeListItem disabledSubtypeUnawareIme = disabledItems.get(3);
- final boolean ONLY_CURRENT_IME = true;
- ImeSubtypeListItem currentIme;
- ImeSubtypeListItem nextIme;
+ final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
+ final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
+ final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
+ final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2);
+ final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3);
+ final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4);
+ final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
+ final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
- // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US"
- currentIme = imList.get(0);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(1), nextIme);
- // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr"
- currentIme = imList.get(1);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(2), nextIme);
- // "switchAwareLatinIme/fr" -> "switchAwareLatinIme/en_US"
- currentIme = imList.get(2);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(0), nextIme);
+ final ControllerImpl controller = new ControllerImpl(enabledItems);
- // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi"
- currentIme = imList.get(3);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(4), nextIme);
- // "nonSwitchAwareLatinIme/hi" -> "switchAwareLatinIme/en_UK"
- currentIme = imList.get(4);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertEquals(imList.get(3), nextIme);
+ // switching-aware loop
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ latinIme_en_US, latinIme_fr);
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ latinIme_fr, japaneseIme_ja_JP);
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ japaneseIme_ja_JP, latinIme_en_US);
- // "switchAwareJapaneseIme/ja_JP" -> null
- currentIme = imList.get(5);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertNull(nextIme);
+ // switching-unaware loop
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ switchingUnawarelatinIme_hi, subtypeUnawareIme);
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ subtypeUnawareIme, switchUnawareJapaneseIme_ja_JP);
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ switchUnawareJapaneseIme_ja_JP, switchingUnawarelatinIme_en_UK);
- // "nonSwitchAwareJapaneseIme/ja_JP" -> null
- currentIme = imList.get(6);
- nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
- imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
- currentIme.mSubtypeName.toString()));
- assertNull(nextIme);
+ // test onlyCurrentIme == true
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ latinIme_en_US, latinIme_fr);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ latinIme_fr, latinIme_en_US);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ switchingUnawarelatinIme_hi, switchingUnawarelatinIme_en_UK);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ subtypeUnawareIme, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ japaneseIme_ja_JP, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ switchUnawareJapaneseIme_ja_JP, null);
+
+ // Make sure that disabled IMEs are not accepted.
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ disabledIme_en_US, null);
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ disabledIme_hi, null);
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ disabledSwitchingUnawareIme, null);
+ assertNextInputMethod(controller, false /* onlyCurrentIme */,
+ disabledSubtypeUnawareIme, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ disabledIme_en_US, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ disabledIme_hi, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ disabledSwitchingUnawareIme, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ disabledSubtypeUnawareIme, null);
}
}
diff --git a/docs/html/community/index.html b/docs/html/community/index.html
index eeb1c51..e3834ba 100644
--- a/docs/html/community/index.html
+++ b/docs/html/community/index.html
@@ -34,6 +34,9 @@
</script>
<style>
+#header {
+ padding: 2.2em 0 0.2em 0;
+}
#header-wrap h1 {
margin:0;
padding:0;
diff --git a/docs/html/distribute/tools/promote/brand.jd b/docs/html/distribute/tools/promote/brand.jd
index 2116c0f..9b9f9a3 100644
--- a/docs/html/distribute/tools/promote/brand.jd
+++ b/docs/html/distribute/tools/promote/brand.jd
@@ -9,6 +9,11 @@
promotional materials. You can use the icons and other assets on this page
provided that you follow the guidelines.</p>
+<p>Use of the Android or Google Play brands must be reviewed by the Android
+Partner Marketing team. Use the <a
+href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android and Google Play Brand Permissions Inquiry form</a> to submit your
+marketing for review.</p>
+
<h2 id="brand-android">Android</h2>
<p>The following are guidelines for the Android brand
@@ -29,10 +34,12 @@
<li><span style="color:red">Incorrect</span>: "Android MediaPlayer"</li>
<li><span style="color:green">Correct</span>: "MediaPlayer for Android"</li>
</ul>
- <p>If used with your logo, "for Android" needs to be smaller in size than your logo.
+ <p>If used with your logo, "for Android" should be no larger than 90% of your logo’s size.
First instance of this use should be followed by a TM symbol, "for Android™".</p>
</li>
- <li>Android may be used as a descriptor, as long as it is followed by a proper generic term.
+ <li>Android may be used as a descriptor, as long as it is followed by a
+ proper generic term. (Think of "Android" as a term used in place of
+ "the Android platform.")
<ul>
<li><span style="color:red">Incorrect</span>: "Android MediaPlayer" or "Android XYZ app"</li>
<li><span style="color:green">Correct</span>: "Android features" or "Android applications"</li>
@@ -62,14 +69,14 @@
<p>When using the Android Robot or any modification of it, proper attribution is
required under the terms of the <a href="http://creativecommons.org/licenses/by/3.0/">Creative
-Commons Attribution</a> license:</p>
+ Commons Attribution 3.0</a> license:</p>
<blockquote><em>The Android robot is reproduced or modified from work created and shared by Google and
used according to terms described in the Creative Commons 3.0 Attribution License.</em></blockquote>
- <p>You may not file trademark applications incorporating the Android robot logo or
-derivatives thereof. We want to ensure that the Android robot remains available
-for all to use.</p>
+ <p>You may not file trademark applications incorporating the Android robot
+ logo or derivatives thereof within your company logo or business name. We
+ want to ensure that the Android robot remains available for all to use.</p>
<h4 style="clear:right">Android logo</h4>
@@ -78,12 +85,10 @@
<img alt="" src="{@docRoot}images/brand/android_logo_no.png">
</div>
-<p>The Android logo may not be used. Nor can this be used with the Android robot.</p>
+<p>The Android logo may not be used.</p>
+
<p>The custom typeface may not be used.</p>
-
-
-
<h2 id="brand-google_play">Google Play</h2>
@@ -98,7 +103,7 @@
<p>When referring to the mobile experience, use "Google Play" unless the text is clearly
instructional for the user. For example, a marketing headline might read "Download our
games on Google Play™," but instructional text would read "Download our games using the Google
-Play™ Store app."
+Play™ store app."
<p>Any use of the Google Play name or icon needs to include this
attribution in your communication:</p>
@@ -111,16 +116,16 @@
<p style="text-align:center">
<a href="{@docRoot}images/brand/Google_Play_Store_48.png">48x48</a> |
<a href="{@docRoot}images/brand/Google_Play_Store_96.png">96x96</a><br>
- <a href="{@docRoot}downloads/brand/Google_Play_Store.ai">Illustrator (.ai)</a>
+ <a href="{@docRoot}images/brand/Google_Play_Store_600.png">600x576</a>
</p>
</div>
-<h4>Google Play Store icon</h4>
+<h4>Google Play store icon</h4>
-<p>You may use the Google Play Store icon, but you may not modify it.</p>
+<p>You may use the Google Play store icon, but you may not modify it.</p>
-<p>As mentioned above, when referring to the Google Play Store app in copy, use the full name:
-"Google Play Store." However, when labeling the Google Play Store icon directly, it's OK to use
+<p>As mentioned above, when referring to the Google Play store app in copy, use the full name:
+"Google Play store." However, when labeling the Google Play store icon directly, it's OK to use
"Play Store" alone to accurately reflect the icon label as it appears on a device.</p>
@@ -140,9 +145,14 @@
<a href="{@docRoot}images/brand/en_generic_rgb_wo_60.png">172x60</a></p>
</div>
- <p>The "Get it on Google Play" and "Android App on Google Play" logos are badges that you
- can use on your website and promotional materials, to point to your products on Google
- Play.</p>
+ <p>The "Get it on Google Play" and "Android App on Google Play" logos are
+ badges that you can use on your website and promotional materials, to point
+ to your products on Google Play. Additional Google Play badge formats and
+ badges for music, books, magazines, movies, and TV shows are also available.
+ Use the <a
+ href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android
+ and Google Play Brand Permissions Inquiry form</a> to request
+ those badges.</p>
<ul>
<li>Don't modify the color, proportions, spacing, or any other aspect of the badge image.
@@ -163,7 +173,7 @@
<p>To quickly create a badge that links to your apps on Google Play,
use the <a
- href="{@docRoot}distribute/tools/promote/badges.html">Googe Play badge generator</a>
+ href="{@docRoot}distribute/tools/promote/badges.html">Google Play badge generator</a>
(provides the badge in over 40 languages).</p>
<p>To create your own size, download an Adobe® Illustrator® (.ai) file for the
@@ -171,25 +181,11 @@
badge in over 40 languages</a>.</p>
<p>For details on all the ways that you can link to your product details page in Google Play,
- see <a href="{@docRoot}distribute/tools/promote/linking.html">Linking to your products</a></p>
+ see <a href="{@docRoot}distribute/tools/promote/linking.html">Linking to your products</a>.</p>
+<h2 id="Marketing_Review">Marketing Reviews and Brand Inquiries</h2>
-
-<h2 id="Questions">Questions</h2>
-
-<p>To view our full guidelines or for any further brand usage questions, please contact our
-Android Partner Marketing team:</p>
-<ul>
- <li>For North and South America, please contact <a
- href="mailto:android-brand-approvals@google.com?Subject=Brand%20Approval%20Questions"
- >android-brand-approvals@google.com</a></li>
-
- <li>For Europe and Emerging Markets, please contact <a
- href="mailto:emea-android-brand@google.com?Subject=Brand%20Approval%20Questions"
- >emea-android-brand@google.com</a></li>
-
- <li>For Asia and Pacific-America, please contact <a
- href="mailto:apac-android-brand-approvals@google.com?Subject=Brand%20Approval%20Questions"
- >apac-android-brand-approvals@google.com</a></li>
-</ul>
-
+<p>Use the <a
+href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android
+and Google Play Brand Permissions Inquiry form</a> to submit any marketing
+reviews or brand inquires. Typical response time is at least one week.</p>
diff --git a/docs/html/guide/topics/ui/settings.jd b/docs/html/guide/topics/ui/settings.jd
index 1d36430..f454c4e 100644
--- a/docs/html/guide/topics/ui/settings.jd
+++ b/docs/html/guide/topics/ui/settings.jd
@@ -820,7 +820,8 @@
public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType";
...
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+ String key) {
if (key.equals(KEY_PREF_SYNC_CONN)) {
Preference connectionPref = findPreference(key);
// Set summary to be the user-description for the selected value
@@ -863,7 +864,40 @@
}
</pre>
+<p class="caution"><strong>Caution:</strong> When you call {@link
+android.content.SharedPreferences#registerOnSharedPreferenceChangeListener
+registerOnSharedPreferenceChangeListener()}, the preference manager does not
+currently store a strong reference to the listener. You must store a strong
+reference to the listener, or it will be susceptible to garbage collection. We
+recommend you keep a reference to the listener in the instance data of an object
+that will exist as long as you need the listener.</p>
+<p>For example, in the following code, the caller does not keep a reference to
+the listener. As a result, the listener will be subject to garbage collection,
+and it will fail at some indeterminate time in the future:</p>
+
+<pre>
+prefs.registerOnSharedPreferenceChangeListener(
+ // Bad! The listener is subject to garbage collection!
+ new SharedPreferences.OnSharedPreferenceChangeListener() {
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ // listener implementation
+ }
+});
+</pre>
+
+<p>Instead, store a reference to the listener in an instance data field of an
+object that will exist as long as the listener is needed:</p>
+
+<pre>
+SharedPreferences.OnSharedPreferenceChangeListener listener =
+ new SharedPreferences.OnSharedPreferenceChangeListener() {
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ // listener implementation
+ }
+};
+prefs.registerOnSharedPreferenceChangeListener(listener);
+</pre>
<h2 id="NetworkUsage">Managing Network Usage</h2>
@@ -1142,13 +1176,15 @@
final Parcelable superState = super.onSaveInstanceState();
// Check whether this Preference is persistent (continually saved)
if (isPersistent()) {
- // No need to save instance state since it's persistent, use superclass state
+ // No need to save instance state since it's persistent,
+ // use superclass state
return superState;
}
// Create instance of custom BaseSavedState
final SavedState myState = new SavedState(superState);
- // Set the state's value with the class member that holds current setting value
+ // Set the state's value with the class member that holds current
+ // setting value
myState.value = mNewValue;
return myState;
}
diff --git a/docs/html/images/brand/Google_Play_Store_600.png b/docs/html/images/brand/Google_Play_Store_600.png
new file mode 100644
index 0000000..f748652
--- /dev/null
+++ b/docs/html/images/brand/Google_Play_Store_600.png
Binary files differ
diff --git a/docs/image_sources/brand/Google_Play_Store.ai b/docs/image_sources/brand/Google_Play_Store.ai
deleted file mode 100644
index 51f07c6..0000000
--- a/docs/image_sources/brand/Google_Play_Store.ai
+++ /dev/null
Binary files differ
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 4407ab0..bf0ab5c 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -592,7 +592,7 @@
}
FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text,
- uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
+ uint32_t startIndex, uint32_t len, int numGlyphs, float radius, const float* positions) {
checkInit();
DropShadow image;
@@ -613,8 +613,9 @@
Rect bounds;
mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
- uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
- uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
+ uint32_t intRadius = Blur::convertRadiusToInt(radius);
+ uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius;
+ uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius;
uint32_t maxSize = Caches::getInstance().maxTextureSize;
if (paddedWidth > maxSize || paddedHeight > maxSize) {
@@ -635,8 +636,8 @@
memset(dataBuffer, 0, size);
- int penX = radius - bounds.left;
- int penY = radius - bounds.bottom;
+ int penX = intRadius - bounds.left;
+ int penY = intRadius - bounds.bottom;
if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
// text has non-whitespace, so draw and blur to create the shadow
@@ -727,9 +728,10 @@
}
}
-void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
+void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
+ uint32_t intRadius = Blur::convertRadiusToInt(radius);
#ifdef ANDROID_ENABLE_RENDERSCRIPT
- if (width * height * radius >= RS_MIN_INPUT_CUTOFF) {
+ if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) {
uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
if (mRs == 0) {
@@ -768,12 +770,12 @@
}
#endif
- float *gaussian = new float[2 * radius + 1];
- Blur::generateGaussianWeights(gaussian, radius);
+ float *gaussian = new float[2 * intRadius + 1];
+ Blur::generateGaussianWeights(gaussian, intRadius);
uint8_t* scratch = new uint8_t[width * height];
- Blur::horizontal(gaussian, radius, *image, scratch, width, height);
- Blur::vertical(gaussian, radius, scratch, *image, width, height);
+ Blur::horizontal(gaussian, intRadius, *image, scratch, width, height);
+ Blur::vertical(gaussian, intRadius, scratch, *image, width, height);
delete[] gaussian;
delete[] scratch;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 9259028..8ce22b0 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -129,7 +129,7 @@
// After renderDropShadow returns, the called owns the memory in DropShadow.image
// and is responsible for releasing it when it's done with it
DropShadow renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex,
- uint32_t len, int numGlyphs, uint32_t radius, const float* positions);
+ uint32_t len, int numGlyphs, float radius, const float* positions);
void setTextureFiltering(bool linearFiltering) {
mLinearFiltering = linearFiltering;
@@ -218,7 +218,7 @@
int32_t width, int32_t height);
// the input image handle may have its pointer replaced (to avoid copies)
- void blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius);
+ void blurImage(uint8_t** image, int32_t width, int32_t height, float radius);
};
}; // namespace uirenderer
diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp
index c020b40..877a422 100644
--- a/libs/hwui/utils/Blur.cpp
+++ b/libs/hwui/utils/Blur.cpp
@@ -19,6 +19,7 @@
#include <math.h>
#include "Blur.h"
+#include "MathUtils.h"
namespace android {
namespace uirenderer {
@@ -35,6 +36,17 @@
return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f;
}
+// if the original radius was on an integer boundary and the resulting radius
+// is within the conversion error tolerance then we attempt to snap to the
+// original integer boundary.
+uint32_t Blur::convertRadiusToInt(float radius) {
+ const float radiusCeil = ceilf(radius);
+ if (MathUtils::areEqual(radiusCeil, radius)) {
+ return radiusCeil;
+ }
+ return radius;
+}
+
/**
* HWUI has used a slightly different equation than Skia to generate the value
* for sigma and to preserve compatibility we have kept that logic.
diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h
index 79aff65..b145333 100644
--- a/libs/hwui/utils/Blur.h
+++ b/libs/hwui/utils/Blur.h
@@ -27,8 +27,12 @@
public:
// If radius > 0, return the corresponding sigma, else return 0
ANDROID_API static float convertRadiusToSigma(float radius);
- // If sigma > 0.6, return the corresponding radius, else return 0
+ // If sigma > 0.5, return the corresponding radius, else return 0
ANDROID_API static float convertSigmaToRadius(float sigma);
+ // If the original radius was on an integer boundary then after the sigma to
+ // radius conversion a small rounding error may be introduced. This function
+ // accounts for that error and snaps to the appropriate integer boundary.
+ static uint32_t convertRadiusToInt(float radius);
static void generateGaussianWeights(float* weights, int32_t radius);
static void horizontal(float* weights, int32_t radius, const uint8_t* source,
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 57274ee..b07d2c5 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -156,4 +156,10 @@
}
}
+ /** @removed */
+ public AudioFormat()
+ {
+ throw new UnsupportedOperationException("There is no valid usage of this constructor");
+ }
+
}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index c736fc7..5a7d815 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -2909,54 +2909,56 @@
return name + "_" + suffix;
}
- public synchronized void readSettings() {
- // force maximum volume on all streams if fixed volume property is set
- if (mUseFixedVolume) {
- mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
- return;
- }
- // do not read system stream volume from settings: this stream is always aliased
- // to another stream type and its volume is never persisted. Values in settings can
- // only be stale values
- if ((mStreamType == AudioSystem.STREAM_SYSTEM) ||
- (mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) {
- int index = 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
- synchronized (mCameraSoundForced) {
- if (mCameraSoundForced) {
- index = mIndexMax;
+ public void readSettings() {
+ synchronized (VolumeStreamState.class) {
+ // force maximum volume on all streams if fixed volume property is set
+ if (mUseFixedVolume) {
+ mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
+ return;
+ }
+ // do not read system stream volume from settings: this stream is always aliased
+ // to another stream type and its volume is never persisted. Values in settings can
+ // only be stale values
+ if ((mStreamType == AudioSystem.STREAM_SYSTEM) ||
+ (mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) {
+ int index = 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
+ synchronized (mCameraSoundForced) {
+ if (mCameraSoundForced) {
+ index = mIndexMax;
+ }
}
- }
- mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
- return;
- }
-
- int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
-
- for (int i = 0; remainingDevices != 0; i++) {
- int device = (1 << i);
- if ((device & remainingDevices) == 0) {
- continue;
- }
- remainingDevices &= ~device;
-
- // retrieve current volume for device
- String name = getSettingNameForDevice(device);
- // if no volume stored for current stream and device, use default volume if default
- // device, continue otherwise
- int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
- AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
- int index = Settings.System.getIntForUser(
- mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
- if (index == -1) {
- continue;
+ mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
+ return;
}
- // ignore settings for fixed volume devices: volume should always be at max or 0
- if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) &&
- ((device & mFixedVolumeDevices) != 0)) {
- mIndex.put(device, (index != 0) ? mIndexMax : 0);
- } else {
- mIndex.put(device, getValidIndex(10 * index));
+ int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
+
+ for (int i = 0; remainingDevices != 0; i++) {
+ int device = (1 << i);
+ if ((device & remainingDevices) == 0) {
+ continue;
+ }
+ remainingDevices &= ~device;
+
+ // retrieve current volume for device
+ String name = getSettingNameForDevice(device);
+ // if no volume stored for current stream and device, use default volume if default
+ // device, continue otherwise
+ int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
+ AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
+ int index = Settings.System.getIntForUser(
+ mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
+ if (index == -1) {
+ continue;
+ }
+
+ // ignore settings for fixed volume devices: volume should always be at max or 0
+ if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) &&
+ ((device & mFixedVolumeDevices) != 0)) {
+ mIndex.put(device, (index != 0) ? mIndexMax : 0);
+ } else {
+ mIndex.put(device, getValidIndex(10 * index));
+ }
}
}
}
@@ -2974,32 +2976,34 @@
AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
}
- public synchronized void applyAllVolumes() {
- // apply default volume first: by convention this will reset all
- // devices volumes in audio policy manager to the supplied value
- int index;
- if (isMuted()) {
- index = 0;
- } else {
- index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
- }
- AudioSystem.setStreamVolumeIndex(mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT);
- // then apply device specific volumes
- Set set = mIndex.entrySet();
- Iterator i = set.iterator();
- while (i.hasNext()) {
- Map.Entry entry = (Map.Entry)i.next();
- int device = ((Integer)entry.getKey()).intValue();
- if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
- if (isMuted()) {
- index = 0;
- } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
- mAvrcpAbsVolSupported) {
- index = (mIndexMax + 5)/10;
- } else {
- index = ((Integer)entry.getValue() + 5)/10;
+ public void applyAllVolumes() {
+ synchronized (VolumeStreamState.class) {
+ // apply default volume first: by convention this will reset all
+ // devices volumes in audio policy manager to the supplied value
+ int index;
+ if (isMuted()) {
+ index = 0;
+ } else {
+ index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
+ }
+ AudioSystem.setStreamVolumeIndex(mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT);
+ // then apply device specific volumes
+ Set set = mIndex.entrySet();
+ Iterator i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ int device = ((Integer)entry.getKey()).intValue();
+ if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
+ if (isMuted()) {
+ index = 0;
+ } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
+ mAvrcpAbsVolSupported) {
+ index = (mIndexMax + 5)/10;
+ } else {
+ index = ((Integer)entry.getValue() + 5)/10;
+ }
+ AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
}
- AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
}
}
}
@@ -3009,94 +3013,104 @@
device);
}
- public synchronized boolean setIndex(int index, int device) {
- int oldIndex = getIndex(device);
- index = getValidIndex(index);
- synchronized (mCameraSoundForced) {
- if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
- index = mIndexMax;
- }
- }
- mIndex.put(device, index);
-
- if (oldIndex != index) {
- // Apply change to all streams using this one as alias
- // if changing volume of current device, also change volume of current
- // device on aliased stream
- boolean currentDevice = (device == getDeviceForStream(mStreamType));
- int numStreamTypes = AudioSystem.getNumStreamTypes();
- for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
- if (streamType != mStreamType &&
- mStreamVolumeAlias[streamType] == mStreamType) {
- int scaledIndex = rescaleIndex(index, mStreamType, streamType);
- mStreamStates[streamType].setIndex(scaledIndex,
- device);
- if (currentDevice) {
- mStreamStates[streamType].setIndex(scaledIndex,
- getDeviceForStream(streamType));
- }
+ public boolean setIndex(int index, int device) {
+ synchronized (VolumeStreamState.class) {
+ int oldIndex = getIndex(device);
+ index = getValidIndex(index);
+ synchronized (mCameraSoundForced) {
+ if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
+ index = mIndexMax;
}
}
- return true;
- } else {
- return false;
+ mIndex.put(device, index);
+
+ if (oldIndex != index) {
+ // Apply change to all streams using this one as alias
+ // if changing volume of current device, also change volume of current
+ // device on aliased stream
+ boolean currentDevice = (device == getDeviceForStream(mStreamType));
+ int numStreamTypes = AudioSystem.getNumStreamTypes();
+ for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
+ if (streamType != mStreamType &&
+ mStreamVolumeAlias[streamType] == mStreamType) {
+ int scaledIndex = rescaleIndex(index, mStreamType, streamType);
+ mStreamStates[streamType].setIndex(scaledIndex,
+ device);
+ if (currentDevice) {
+ mStreamStates[streamType].setIndex(scaledIndex,
+ getDeviceForStream(streamType));
+ }
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
}
}
- public synchronized int getIndex(int device) {
- Integer index = mIndex.get(device);
- if (index == null) {
- // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
- index = mIndex.get(AudioSystem.DEVICE_OUT_DEFAULT);
+ public int getIndex(int device) {
+ synchronized (VolumeStreamState.class) {
+ Integer index = mIndex.get(device);
+ if (index == null) {
+ // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
+ index = mIndex.get(AudioSystem.DEVICE_OUT_DEFAULT);
+ }
+ return index.intValue();
}
- return index.intValue();
}
public int getMaxIndex() {
return mIndexMax;
}
- public synchronized void setAllIndexes(VolumeStreamState srcStream) {
- int srcStreamType = srcStream.getStreamType();
- // apply default device volume from source stream to all devices first in case
- // some devices are present in this stream state but not in source stream state
- int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
- index = rescaleIndex(index, srcStreamType, mStreamType);
- Set set = mIndex.entrySet();
- Iterator i = set.iterator();
- while (i.hasNext()) {
- Map.Entry entry = (Map.Entry)i.next();
- entry.setValue(index);
- }
- // Now apply actual volume for devices in source stream state
- set = srcStream.mIndex.entrySet();
- i = set.iterator();
- while (i.hasNext()) {
- Map.Entry entry = (Map.Entry)i.next();
- int device = ((Integer)entry.getKey()).intValue();
- index = ((Integer)entry.getValue()).intValue();
+ public void setAllIndexes(VolumeStreamState srcStream) {
+ synchronized (VolumeStreamState.class) {
+ int srcStreamType = srcStream.getStreamType();
+ // apply default device volume from source stream to all devices first in case
+ // some devices are present in this stream state but not in source stream state
+ int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
index = rescaleIndex(index, srcStreamType, mStreamType);
+ Set set = mIndex.entrySet();
+ Iterator i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ entry.setValue(index);
+ }
+ // Now apply actual volume for devices in source stream state
+ set = srcStream.mIndex.entrySet();
+ i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ int device = ((Integer)entry.getKey()).intValue();
+ index = ((Integer)entry.getValue()).intValue();
+ index = rescaleIndex(index, srcStreamType, mStreamType);
- setIndex(index, device);
+ setIndex(index, device);
+ }
}
}
- public synchronized void setAllIndexesToMax() {
- Set set = mIndex.entrySet();
- Iterator i = set.iterator();
- while (i.hasNext()) {
- Map.Entry entry = (Map.Entry)i.next();
- entry.setValue(mIndexMax);
+ public void setAllIndexesToMax() {
+ synchronized (VolumeStreamState.class) {
+ Set set = mIndex.entrySet();
+ Iterator i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ entry.setValue(mIndexMax);
+ }
}
}
- public synchronized void mute(IBinder cb, boolean state) {
- VolumeDeathHandler handler = getDeathHandler(cb, state);
- if (handler == null) {
- Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
- return;
+ public void mute(IBinder cb, boolean state) {
+ synchronized (VolumeStreamState.class) {
+ VolumeDeathHandler handler = getDeathHandler(cb, state);
+ if (handler == null) {
+ Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
+ return;
+ }
+ handler.mute(state);
}
- handler.mute(state);
}
public int getStreamType() {
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index acfcd83..3f37ed1 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -171,8 +171,3 @@
{
return static_cast<Sensor const*>(sensor)->getStringType().string();
}
-
-const char* ASensor_getRequiredPermission(ASensor const* sensor)
-{
- return static_cast<Sensor const*>(sensor)->getRequiredPermission().string();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index ef7fb89..a1e70b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -83,7 +83,7 @@
boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null);
boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.enabledDesc == null);
- state.enabled = wifiConnected;
+ state.enabled = cb.enabled;
state.connected = wifiConnected;
state.activityIn = cb.enabled && cb.activityIn;
state.activityOut = cb.enabled && cb.activityOut;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index f4db625..cda84cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -180,6 +180,7 @@
*/
protected int mState;
protected boolean mBouncerShowing;
+ protected boolean mShowLockscreenNotifications;
protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
@@ -199,6 +200,9 @@
final int mode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
setZenMode(mode);
+ final boolean show = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1) != 0;
+ setShowLockscreenNotifications(show);
}
};
@@ -344,6 +348,9 @@
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
mSettingsObserver);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
+ mSettingsObserver);
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
@@ -1250,6 +1257,10 @@
updateNotificationIcons();
}
+ protected void setShowLockscreenNotifications(boolean show) {
+ mShowLockscreenNotifications = show;
+ }
+
protected abstract void haltTicker();
protected abstract void setAreThereNotifications();
protected abstract void updateNotificationIcons();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 28367d0..4121abd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -500,6 +500,12 @@
}
@Override
+ protected void setShowLockscreenNotifications(boolean show) {
+ super.setShowLockscreenNotifications(show);
+ updateStackScrollerState();
+ }
+
+ @Override
public void start() {
mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
@@ -2787,7 +2793,10 @@
}
public void updateStackScrollerState() {
+ if (mStackScroller == null) return;
mStackScroller.setDimmed(mState == StatusBarState.KEYGUARD, false /* animate */);
+ mStackScroller.setVisibility(!mShowLockscreenNotifications && mState == StatusBarState.KEYGUARD
+ ? View.INVISIBLE : View.VISIBLE);
}
public void userActivity() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 966c0b0..56402a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -339,7 +339,7 @@
boolean wifiOut = wifiEnabled && mWifiSsid != null
&& (mWifiActivity == WifiManager.DATA_ACTIVITY_INOUT
|| mWifiActivity == WifiManager.DATA_ACTIVITY_OUT);
- cb.onWifiSignalChanged(wifiEnabled, mQSWifiIconId, wifiIn, wifiOut,
+ cb.onWifiSignalChanged(mWifiEnabled, mQSWifiIconId, wifiIn, wifiOut,
mContentDescriptionWifi, wifiDesc);
boolean mobileIn = mDataConnected && (mDataActivity == TelephonyManager.DATA_ACTIVITY_INOUT
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index e178773..3c37902 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -66,6 +66,7 @@
import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
+import android.telephony.TelephonyManager;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -1925,9 +1926,8 @@
ServiceManager.checkService(DreamService.DREAM_SERVICE));
}
- static ITelephony getTelephonyService() {
- return ITelephony.Stub.asInterface(
- ServiceManager.checkService(Context.TELEPHONY_SERVICE));
+ TelephonyManager getTelephonyService() {
+ return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
}
static IAudioService getAudioService() {
@@ -2010,14 +2010,10 @@
// If an incoming call is ringing, HOME is totally disabled.
// (The user is already on the InCallScreen at this point,
// and his ONLY options are to answer or reject the call.)
- try {
- ITelephony telephonyService = getTelephonyService();
- if (telephonyService != null && telephonyService.isRinging()) {
- Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
- return -1;
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
+ TelephonyManager telephonyManager = getTelephonyService();
+ if (telephonyManager != null && telephonyManager.isRinging()) {
+ Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
+ return -1;
}
// Delay handling home if a double-tap is possible.
@@ -3967,37 +3963,33 @@
}
}
if (down) {
- ITelephony telephonyService = getTelephonyService();
- if (telephonyService != null) {
- try {
- if (telephonyService.isRinging()) {
- // If an incoming call is ringing, either VOLUME key means
- // "silence ringer". We handle these keys here, rather than
- // in the InCallScreen, to make sure we'll respond to them
- // even if the InCallScreen hasn't come to the foreground yet.
- // Look for the DOWN event here, to agree with the "fallback"
- // behavior in the InCallScreen.
- Log.i(TAG, "interceptKeyBeforeQueueing:"
- + " VOLUME key-down while ringing: Silence ringer!");
+ TelephonyManager telephonyManager = getTelephonyService();
+ if (telephonyManager != null) {
+ if (telephonyManager.isRinging()) {
+ // If an incoming call is ringing, either VOLUME key means
+ // "silence ringer". We handle these keys here, rather than
+ // in the InCallScreen, to make sure we'll respond to them
+ // even if the InCallScreen hasn't come to the foreground yet.
+ // Look for the DOWN event here, to agree with the "fallback"
+ // behavior in the InCallScreen.
+ Log.i(TAG, "interceptKeyBeforeQueueing:"
+ + " VOLUME key-down while ringing: Silence ringer!");
- // Silence the ringer. (It's safe to call this
- // even if the ringer has already been silenced.)
- telephonyService.silenceRinger();
+ // Silence the ringer. (It's safe to call this
+ // even if the ringer has already been silenced.)
+ telephonyManager.silenceRinger();
- // And *don't* pass this key thru to the current activity
- // (which is probably the InCallScreen.)
- result &= ~ACTION_PASS_TO_USER;
- break;
- }
- if (telephonyService.isOffhook()
- && (result & ACTION_PASS_TO_USER) == 0) {
- // If we are in call but we decided not to pass the key to
- // the application, handle the volume change here.
- handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
- break;
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
+ // And *don't* pass this key thru to the current activity
+ // (which is probably the InCallScreen.)
+ result &= ~ACTION_PASS_TO_USER;
+ break;
+ }
+ if (telephonyManager.isOffhook()
+ && (result & ACTION_PASS_TO_USER) == 0) {
+ // If we are in call but we decided not to pass the key to
+ // the application, handle the volume change here.
+ handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
+ break;
}
}
@@ -4014,14 +4006,10 @@
case KeyEvent.KEYCODE_ENDCALL: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
- ITelephony telephonyService = getTelephonyService();
+ TelephonyManager telephonyManager = getTelephonyService();
boolean hungUp = false;
- if (telephonyService != null) {
- try {
- hungUp = telephonyService.endCall();
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
- }
+ if (telephonyManager != null) {
+ hungUp = telephonyManager.endCall();
}
interceptPowerKeyDown(!interactive || hungUp);
} else {
@@ -4057,23 +4045,19 @@
interceptScreenshotChord();
}
- ITelephony telephonyService = getTelephonyService();
+ TelephonyManager telephonyManager = getTelephonyService();
boolean hungUp = false;
- if (telephonyService != null) {
- try {
- if (telephonyService.isRinging()) {
- // Pressing Power while there's a ringing incoming
- // call should silence the ringer.
- telephonyService.silenceRinger();
- } else if ((mIncallPowerBehavior
- & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
- && telephonyService.isOffhook() && interactive) {
- // Otherwise, if "Power button ends call" is enabled,
- // the Power button will hang up any current active call.
- hungUp = telephonyService.endCall();
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
+ if (telephonyManager != null) {
+ if (telephonyManager.isRinging()) {
+ // Pressing Power while there's a ringing incoming
+ // call should silence the ringer.
+ telephonyManager.silenceRinger();
+ } else if ((mIncallPowerBehavior
+ & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
+ && telephonyManager.isOffhook() && interactive) {
+ // Otherwise, if "Power button ends call" is enabled,
+ // the Power button will hang up any current active call.
+ hungUp = telephonyManager.endCall();
}
}
interceptPowerKeyDown(!interactive || hungUp
@@ -4106,16 +4090,12 @@
case KeyEvent.KEYCODE_MEDIA_PAUSE:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
if (down) {
- ITelephony telephonyService = getTelephonyService();
- if (telephonyService != null) {
- try {
- if (!telephonyService.isIdle()) {
- // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
- // to avoid music playback.
- break;
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
+ TelephonyManager telephonyManager = getTelephonyService();
+ if (telephonyManager != null) {
+ if (!telephonyManager.isIdle()) {
+ // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
+ // to avoid music playback.
+ break;
}
}
}
@@ -4145,20 +4125,16 @@
case KeyEvent.KEYCODE_CALL: {
if (down) {
- ITelephony telephonyService = getTelephonyService();
- if (telephonyService != null) {
- try {
- if (telephonyService.isRinging()) {
- Log.i(TAG, "interceptKeyBeforeQueueing:"
- + " CALL key-down while ringing: Answer the call!");
- telephonyService.answerRingingCall();
+ TelephonyManager telephonyManager = getTelephonyService();
+ if (telephonyManager != null) {
+ if (telephonyManager.isRinging()) {
+ Log.i(TAG, "interceptKeyBeforeQueueing:"
+ + " CALL key-down while ringing: Answer the call!");
+ telephonyManager.answerRingingCall();
- // And *don't* pass this key thru to the current activity
- // (which is presumably the InCallScreen.)
- result &= ~ACTION_PASS_TO_USER;
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException", ex);
+ // And *don't* pass this key thru to the current activity
+ // (which is presumably the InCallScreen.)
+ result &= ~ACTION_PASS_TO_USER;
}
}
}
@@ -5575,8 +5551,13 @@
pw.print(prefix); pw.print("mDemoHdmiRotation="); pw.print(mDemoHdmiRotation);
pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock);
pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation);
+
mStatusBarController.dump(pw, prefix);
mNavigationBarController.dump(pw, prefix);
PolicyControl.dump(prefix, pw);
+
+ if (mOrientationListener != null) {
+ mOrientationListener.dump(pw, prefix);
+ }
}
}
diff --git a/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java b/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java
index 0c77556..2cc33b5f 100644
--- a/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java
+++ b/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java
@@ -26,6 +26,9 @@
import android.util.FloatMath;
import android.util.Log;
import android.util.Slog;
+import android.util.TimeUtils;
+
+import java.io.PrintWriter;
/**
* A special helper class used by the WindowManager
@@ -181,6 +184,19 @@
*/
public abstract void onProposedRotationChanged(int rotation);
+ public void dump(PrintWriter pw, String prefix) {
+ synchronized (mLock) {
+ pw.println(prefix + TAG);
+ prefix += " ";
+ pw.println(prefix + "mEnabled=" + mEnabled);
+ pw.println(prefix + "mCurrentRotation=" + mCurrentRotation);
+ pw.println(prefix + "mSensor=" + mSensor);
+ pw.println(prefix + "mRate=" + mRate);
+
+ mSensorEventListener.dumpLocked(pw, prefix);
+ }
+ }
+
/**
* This class filters the raw accelerometer data and tries to detect actual changes in
* orientation. This is a very ill-defined problem so there are a lot of tweakable parameters,
@@ -342,6 +358,14 @@
/* ROTATION_270 */ { -25, 65 }
};
+ // The tilt angle below which we conclude that the user is holding the device
+ // overhead reading in bed and lock into that state.
+ private final int TILT_OVERHEAD_ENTER = -40;
+
+ // The tilt angle above which we conclude that the user would like a rotation
+ // change to occur and unlock from the overhead state.
+ private final int TILT_OVERHEAD_EXIT = -15;
+
// The gap angle in degrees between adjacent orientation angles for hysteresis.
// This creates a "dead zone" between the current orientation and a proposed
// adjacent orientation. No orientation proposal is made when the orientation
@@ -364,12 +388,18 @@
// Timestamp when the device last appeared to be flat for sure (the flat delay elapsed).
private long mFlatTimestampNanos;
+ private boolean mFlat;
// Timestamp when the device last appeared to be swinging.
private long mSwingTimestampNanos;
+ private boolean mSwinging;
// Timestamp when the device last appeared to be undergoing external acceleration.
private long mAccelerationTimestampNanos;
+ private boolean mAccelerating;
+
+ // Whether we are locked into an overhead usage mode.
+ private boolean mOverhead;
// History of observed tilt angles.
private static final int TILT_HISTORY_SIZE = 40;
@@ -381,6 +411,19 @@
return mProposedRotation;
}
+ public void dumpLocked(PrintWriter pw, String prefix) {
+ pw.println(prefix + "mProposedRotation=" + mProposedRotation);
+ pw.println(prefix + "mPredictedRotation=" + mPredictedRotation);
+ pw.println(prefix + "mLastFilteredX=" + mLastFilteredX);
+ pw.println(prefix + "mLastFilteredY=" + mLastFilteredY);
+ pw.println(prefix + "mLastFilteredZ=" + mLastFilteredZ);
+ pw.println(prefix + "mTiltHistory={last: " + getLastTiltLocked() + "}");
+ pw.println(prefix + "mFlat=" + mFlat);
+ pw.println(prefix + "mSwinging=" + mSwinging);
+ pw.println(prefix + "mAccelerating=" + mAccelerating);
+ pw.println(prefix + "mOverhead=" + mOverhead);
+ }
+
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@@ -478,7 +521,18 @@
// If the tilt angle is too close to horizontal then we cannot determine
// the orientation angle of the screen.
- if (Math.abs(tiltAngle) > MAX_TILT) {
+ if (tiltAngle <= TILT_OVERHEAD_ENTER) {
+ mOverhead = true;
+ } else if (tiltAngle >= TILT_OVERHEAD_EXIT) {
+ mOverhead = false;
+ }
+ if (mOverhead) {
+ if (LOG) {
+ Slog.v(TAG, "Ignoring sensor data, device is overhead: "
+ + "tiltAngle=" + tiltAngle);
+ }
+ clearPredictedRotationLocked();
+ } else if (Math.abs(tiltAngle) > MAX_TILT) {
if (LOG) {
Slog.v(TAG, "Ignoring sensor data, tilt angle too high: "
+ "tiltAngle=" + tiltAngle);
@@ -526,6 +580,9 @@
}
}
}
+ mFlat = isFlat;
+ mSwinging = isSwinging;
+ mAccelerating = isAccelerating;
// Determine new proposed rotation.
oldProposedRotation = mProposedRotation;
@@ -543,6 +600,7 @@
+ ", isAccelerating=" + isAccelerating
+ ", isFlat=" + isFlat
+ ", isSwinging=" + isSwinging
+ + ", isOverhead=" + mOverhead
+ ", timeUntilSettledMS=" + remainingMS(now,
mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS)
+ ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now,
@@ -660,8 +718,12 @@
mLastFilteredTimestampNanos = Long.MIN_VALUE;
mProposedRotation = -1;
mFlatTimestampNanos = Long.MIN_VALUE;
+ mFlat = false;
mSwingTimestampNanos = Long.MIN_VALUE;
+ mSwinging = false;
mAccelerationTimestampNanos = Long.MIN_VALUE;
+ mAccelerating = false;
+ mOverhead = false;
clearPredictedRotationLocked();
clearTiltHistoryLocked();
}
@@ -726,6 +788,11 @@
return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1;
}
+ private float getLastTiltLocked() {
+ int index = nextTiltHistoryIndexLocked(mTiltHistoryIndex);
+ return index >= 0 ? mTiltHistory[index] : Float.NaN;
+ }
+
private float remainingMS(long now, long until) {
return now >= until ? 0 : (until - now) * 0.000001f;
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7ecf248..1e21e1c 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -343,12 +343,6 @@
private static final int EVENT_INET_CONDITION_HOLD_END = 5;
/**
- * used internally to set enable/disable cellular data
- * arg1 = ENBALED or DISABLED
- */
- private static final int EVENT_SET_MOBILE_DATA = 7;
-
- /**
* used internally to clear a wakelock when transitioning
* from one net to another
*/
@@ -1822,20 +1816,6 @@
return true;
}
- /**
- * @see ConnectivityManager#getMobileDataEnabled()
- */
- public boolean getMobileDataEnabled() {
- // TODO: This detail should probably be in DataConnectionTracker's
- // which is where we store the value and maybe make this
- // asynchronous.
- enforceAccessPermission();
- boolean retVal = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.MOBILE_DATA, 1) == 1;
- if (VDBG) log("getMobileDataEnabled returning " + retVal);
- return retVal;
- }
-
public void setDataDependency(int networkType, boolean met) {
enforceConnectivityInternalPermission();
@@ -1908,22 +1888,6 @@
}
};
- /**
- * @see ConnectivityManager#setMobileDataEnabled(boolean)
- */
- public void setMobileDataEnabled(boolean enabled) {
- enforceChangePermission();
- if (DBG) log("setMobileDataEnabled(" + enabled + ")");
-
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA,
- (enabled ? ENABLED : DISABLED), 0));
- }
-
- private void handleSetMobileData(boolean enabled) {
- // TODO - handle this - probably generalize passing in a transport type and send to the
- // factories?
- }
-
@Override
public void setPolicyDataEnable(int networkType, boolean enabled) {
// only someone like NPMS should only be calling us
@@ -3315,11 +3279,6 @@
handleInetConditionHoldEnd(netType, sequence);
break;
}
- case EVENT_SET_MOBILE_DATA: {
- boolean enabled = (msg.arg1 == ENABLED);
- handleSetMobileData(enabled);
- break;
- }
case EVENT_APPLY_GLOBAL_HTTP_PROXY: {
handleDeprecatedGlobalHttpProxy();
break;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 88bebcb..c2518f6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -283,7 +283,7 @@
// before we decide it's never going to come up for real, when the process was
// started with a wrapper for instrumentation (such as Valgrind) because it
// could take much longer than usual.
- static final int PROC_START_TIMEOUT_WITH_WRAPPER = 300*1000;
+ static final int PROC_START_TIMEOUT_WITH_WRAPPER = 1200*1000;
// How long to wait after going idle before forcing apps to GC.
static final int GC_TIMEOUT = 5*1000;
@@ -6016,6 +6016,9 @@
IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"checkHoldingPermissionsLocked: uri=" + grantUri + " uid=" + uid);
+ if (UserHandle.getUserId(uid) != grantUri.sourceUserId) {
+ return false;
+ }
if (pi.applicationInfo.uid == uid) {
return true;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index d0ba118..a0440cb 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -37,6 +37,7 @@
import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_APP;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_SAVED_STATE;
+import static com.android.server.am.ActivityStackSupervisor.DEBUG_SCREENSHOTS;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES;
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
@@ -346,6 +347,10 @@
mWindowManager = mService.mWindowManager;
mStackId = activityContainer.mStackId;
mCurrentUser = mService.mCurrentUserId;
+ // Get the activity screenshot thumbnail dimensions
+ Resources res = mService.mContext.getResources();
+ mThumbnailWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+ mThumbnailHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
}
/**
@@ -729,42 +734,54 @@
}
}
+ /**
+ * This resets the saved state from the last screenshot, forcing a new screenshot to be taken
+ * again when requested.
+ */
+ private void invalidateLastScreenshot() {
+ mLastScreenshotActivity = null;
+ if (mLastScreenshotBitmap != null) {
+ mLastScreenshotBitmap.recycle();
+ }
+ mLastScreenshotBitmap = null;
+ }
+
public final Bitmap screenshotActivities(ActivityRecord who) {
+ if (DEBUG_SCREENSHOTS) Slog.d(TAG, "screenshotActivities: " + who);
if (who.noDisplay) {
+ if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tNo display");
return null;
}
TaskRecord tr = who.task;
- if (mService.getMostRecentTask() != tr && tr.intent != null &&
- (tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
- // If this task is being excluded from recents, we don't want to take
- // the expense of capturing a thumbnail, since we will never show it.
+ if (mService.getMostRecentTask() != tr || isHomeStack()) {
+ // This is an optimization -- since we never show Home or Recents within Recents itself,
+ // we can just go ahead and skip taking the screenshot if this is the home stack. In
+ // the case where the most recent task is not the task that was supplied, then the stack
+ // has changed, so invalidate the last screenshot().
+ invalidateLastScreenshot();
+ if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tIs Home stack? " + isHomeStack());
return null;
}
- Resources res = mService.mContext.getResources();
int w = mThumbnailWidth;
int h = mThumbnailHeight;
- if (w < 0) {
- mThumbnailWidth = w =
- res.getDimensionPixelSize(com.android.internal.R.dimen.recents_thumbnail_width);
- mThumbnailHeight = h =
- res.getDimensionPixelSize(com.android.internal.R.dimen.recents_thumbnail_height);
- }
-
if (w > 0) {
if (who != mLastScreenshotActivity || mLastScreenshotBitmap == null
|| mLastScreenshotActivity.state == ActivityState.RESUMED
|| mLastScreenshotBitmap.getWidth() != w
|| mLastScreenshotBitmap.getHeight() != h) {
+ if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tUpdating screenshot");
mLastScreenshotActivity = who;
mLastScreenshotBitmap = mWindowManager.screenshotApplications(
who.appToken, Display.DEFAULT_DISPLAY, w, h, SCREENSHOT_FORCE_565);
}
if (mLastScreenshotBitmap != null) {
+ if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tReusing last screenshot");
return mLastScreenshotBitmap.copy(mLastScreenshotBitmap.getConfig(), true);
}
}
+ Slog.e(TAG, "Invalid thumbnail dimensions: " + w + "x" + h);
return null;
}
@@ -1042,6 +1059,12 @@
} else {
next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
}
+
+ // If we are resuming the activity that we had last screenshotted, then we know it will be
+ // updated, so invalidate the last screenshot to ensure we take a fresh one when requested
+ if (next == mLastScreenshotActivity) {
+ invalidateLastScreenshot();
+ }
}
private void setVisibile(ActivityRecord r, boolean visible) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index c1a4643..c7ae6bc 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -48,6 +48,7 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.IActivityManager.WaitResult;
import android.app.ResultInfo;
+import android.app.StatusBarManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentSender;
@@ -76,6 +77,7 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
@@ -91,11 +93,13 @@
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.TransferPipe;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.wm.WindowManagerService;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -109,6 +113,7 @@
static final boolean DEBUG_SAVED_STATE = DEBUG || false;
static final boolean DEBUG_STATES = DEBUG || false;
static final boolean DEBUG_IDLE = DEBUG || false;
+ static final boolean DEBUG_SCREENSHOTS = DEBUG || false;
public static final int HOME_STACK_ID = 0;
@@ -130,9 +135,15 @@
static final int HANDLE_DISPLAY_CHANGED = FIRST_SUPERVISOR_STACK_MSG + 6;
static final int HANDLE_DISPLAY_REMOVED = FIRST_SUPERVISOR_STACK_MSG + 7;
static final int CONTAINER_CALLBACK_VISIBILITY = FIRST_SUPERVISOR_STACK_MSG + 8;
+ static final int LOCK_TASK_START_MSG = FIRST_SUPERVISOR_STACK_MSG + 9;
+ static final int LOCK_TASK_END_MSG = FIRST_SUPERVISOR_STACK_MSG + 10;
private final static String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
+ /** Status Bar Service **/
+ private IBinder mToken = new Binder();
+ private IStatusBarService mStatusBarService;
+
// For debugging to make sure the caller when acquiring/releasing our
// wake lock is the system process.
static final boolean VALIDATE_WAKE_LOCK_CALLER = false;
@@ -251,6 +262,21 @@
mLaunchingActivity.setReferenceCounted(false);
}
+ // This function returns a IStatusBarService. The value is from ServiceManager.
+ // getService and is cached.
+ private IStatusBarService getStatusBarService() {
+ synchronized (mService) {
+ if (mStatusBarService == null) {
+ mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
+ if (mStatusBarService == null) {
+ Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
+ }
+ }
+ return mStatusBarService;
+ }
+ }
+
void setWindowManager(WindowManagerService wm) {
synchronized (mService) {
mWindowManager = wm;
@@ -2940,9 +2966,12 @@
}
void setLockTaskModeLocked(TaskRecord task) {
+ final Message lockTaskMsg = Message.obtain();
if (task == null) {
// Take out of lock task mode.
mLockTaskModeTask = null;
+ lockTaskMsg.what = LOCK_TASK_END_MSG;
+ mHandler.sendMessage(lockTaskMsg);
return;
}
if (isLockTaskModeViolation(task)) {
@@ -2952,6 +2981,8 @@
mLockTaskModeTask = task;
findTaskToMoveToFrontLocked(task, 0, null);
resumeTopActivitiesLocked();
+ lockTaskMsg.what = LOCK_TASK_START_MSG;
+ mHandler.sendMessage(lockTaskMsg);
}
boolean isLockTaskModeViolation(TaskRecord task) {
@@ -3048,6 +3079,32 @@
} catch (RemoteException e) {
}
}
+ case LOCK_TASK_START_MSG: {
+ // When lock task starts, we disable the status bars.
+ try {
+ if (getStatusBarService() != null) {
+ getStatusBarService().disable
+ (StatusBarManager.DISABLE_MASK ^ StatusBarManager.DISABLE_BACK,
+ mToken, mService.mContext.getPackageName());
+ }
+ } catch (RemoteException ex) {
+ throw new RuntimeException(ex);
+ }
+ break;
+ }
+ case LOCK_TASK_END_MSG: {
+ // When lock task ends, we enable the status bars.
+ try {
+ if (getStatusBarService() != null) {
+ getStatusBarService().disable
+ (StatusBarManager.DISABLE_NONE,
+ mToken, mService.mContext.getPackageName());
+ }
+ } catch (RemoteException ex) {
+ throw new RuntimeException(ex);
+ }
+ break;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java
index 1bc278d..f8e9b7b 100644
--- a/services/core/java/com/android/server/hdmi/FeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/FeatureAction.java
@@ -164,8 +164,13 @@
mActionTimer.sendTimerMessage(state, delayMillis);
}
- protected final boolean sendCommand(HdmiCecMessage cmd) {
- return mService.sendCecCommand(cmd);
+ protected final void sendCommand(HdmiCecMessage cmd) {
+ mService.sendCecCommand(cmd);
+ }
+
+ protected final void sendCommand(HdmiCecMessage cmd,
+ HdmiControlService.SendMessageCallback callback) {
+ mService.sendCecCommand(cmd, callback);
}
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index f99a01d..8d0696b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -21,10 +21,10 @@
import android.hardware.hdmi.HdmiCecMessage;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseIntArray;
+
+import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
import libcore.util.EmptyArray;
@@ -37,6 +37,9 @@
* and pass it to CEC HAL so that it sends message to other device. For incoming
* message it translates the message and delegates it to proper module.
*
+ * <p>It should be careful to access member variables on IO thread because
+ * it can be accessed from system thread as well.
+ *
* <p>It can be created only by {@link HdmiCecController#create}
*
* <p>Declared as package-private, accessed by {@link HdmiControlService} only.
@@ -58,8 +61,7 @@
private static final int NUM_LOGICAL_ADDRESS = 16;
- // TODO: define other constants for errors.
- private static final int ERROR_SUCCESS = 0;
+ private static final int RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION = 3;
// Handler instance to process synchronous I/O (mainly send) message.
private Handler mIoHandler;
@@ -70,22 +72,19 @@
// Stores the pointer to the native implementation of the service that
// interacts with HAL.
- private long mNativePtr;
+ private volatile long mNativePtr;
private HdmiControlService mService;
- // Map-like container of all cec devices. A logical address of device is
- // used as key of container.
- private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos =
- new SparseArray<HdmiCecDeviceInfo>();
- // Set-like container for all local devices' logical address.
- // Key and value are same.
- private final SparseIntArray mLocalAddresses = new SparseIntArray();
+ // Map-like container of all cec devices including local ones.
+ // A logical address of device is used as key of container.
+ private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
+
+ // Stores the local CEC devices in the system.
+ private final ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
// Private constructor. Use HdmiCecController.create().
private HdmiCecController() {
- // TODO: Consider restoring the local device addresses from persistent storage
- // to allocate the same addresses again if possible.
}
/**
@@ -99,53 +98,42 @@
* returns {@code null}.
*/
static HdmiCecController create(HdmiControlService service) {
- HdmiCecController handler = new HdmiCecController();
- long nativePtr = nativeInit(handler);
+ HdmiCecController controller = new HdmiCecController();
+ long nativePtr = nativeInit(controller);
if (nativePtr == 0L) {
- handler = null;
+ controller = null;
return null;
}
- handler.init(service, nativePtr);
- return handler;
+ controller.init(service, nativePtr);
+ return controller;
+ }
+
+ private void init(HdmiControlService service, long nativePtr) {
+ mService = service;
+ mIoHandler = new Handler(service.getServiceLooper());
+ mControlHandler = new Handler(service.getServiceLooper());
+ mNativePtr = nativePtr;
}
/**
- * Initialize {@link #mLocalAddresses} by allocating logical addresses for each hosted type.
+ * Perform initialization for each hosted device.
*
- * @param deviceTypes local device types
+ * @param deviceTypes array of device types
*/
void initializeLocalDevices(int[] deviceTypes) {
- for (int deviceType : deviceTypes) {
- int preferred = getPreferredAddress(deviceType);
- allocateLogicalAddress(deviceType, preferred, new AllocateLogicalAddressCallback() {
- @Override
- public void onAllocated(int deviceType, int logicalAddress) {
- addLogicalAddress(logicalAddress);
- }
- });
- }
- }
-
- /**
- * Get the preferred address for a given type.
- *
- * @param deviceType logical device type to get the address for
- * @return preferred address; {@link HdmiCec#ADDR_UNREGISTERED} if not available.
- */
- private int getPreferredAddress(int deviceType) {
- // Uses the data restored from persistent memory at boot up if they are available.
- // Otherwise we return UNREGISTERED indicating there is no preferred address.
- // Note that for address SPECIFIC_USE(14), HdmiCec.getTypeFromAddress() returns DEVICE_TV,
- // meaning that we do not support device type video processor yet.
- for (int i = 0; i < mLocalAddresses.size(); ++i) {
- int address = mLocalAddresses.keyAt(i);
- int type = HdmiCec.getTypeFromAddress(address);
- if (type == deviceType) {
- return address;
+ assertRunOnServiceThread();
+ for (int type : deviceTypes) {
+ HdmiCecLocalDevice device = HdmiCecLocalDevice.create(this, type);
+ if (device == null) {
+ continue;
}
+ // TODO: Consider restoring the local device addresses from persistent storage
+ // to allocate the same addresses again if possible.
+ device.setPreferredAddress(HdmiCec.ADDR_UNREGISTERED);
+ mLocalDevices.add(device);
+ device.init();
}
- return HdmiCec.ADDR_UNREGISTERED;
}
/**
@@ -176,13 +164,55 @@
* Otherwise, scan address will start from {@code preferredAddress}
* @param callback callback interface to report allocated logical address to caller
*/
- void allocateLogicalAddress(int deviceType, int preferredAddress,
- AllocateLogicalAddressCallback callback) {
- Message msg = mIoHandler.obtainMessage(MSG_ALLOCATE_LOGICAL_ADDRESS);
- msg.arg1 = deviceType;
- msg.arg2 = preferredAddress;
- msg.obj = callback;
- mIoHandler.sendMessage(msg);
+ void allocateLogicalAddress(final int deviceType, final int preferredAddress,
+ final AllocateLogicalAddressCallback callback) {
+ assertRunOnServiceThread();
+
+ runOnIoThread(new Runnable() {
+ @Override
+ public void run() {
+ handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
+ }
+ });
+ }
+
+ private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
+ final AllocateLogicalAddressCallback callback) {
+ assertRunOnIoThread();
+ int startAddress = preferredAddress;
+ // If preferred address is "unregistered", start address will be the smallest
+ // address matched with the given device type.
+ if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) {
+ for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
+ if (deviceType == HdmiCec.getTypeFromAddress(i)) {
+ startAddress = i;
+ break;
+ }
+ }
+ }
+
+ int logicalAddress = HdmiCec.ADDR_UNREGISTERED;
+ // Iterates all possible addresses which has the same device type.
+ for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
+ int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
+ if (curAddress != HdmiCec.ADDR_UNREGISTERED
+ && deviceType == HdmiCec.getTypeFromAddress(i)) {
+ if (!sendPollMessage(curAddress, RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION)) {
+ logicalAddress = curAddress;
+ break;
+ }
+ }
+ }
+
+ final int assignedAddress = logicalAddress;
+ if (callback != null) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ callback.onAllocated(deviceType, assignedAddress);
+ }
+ });
+ }
}
private static byte[] buildBody(int opcode, byte[] params) {
@@ -192,101 +222,6 @@
return body;
}
- private final class IoHandler extends Handler {
- private IoHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SEND_CEC_COMMAND:
- HdmiCecMessage cecMessage = (HdmiCecMessage) msg.obj;
- byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
- nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
- cecMessage.getDestination(), body);
- break;
- case MSG_ALLOCATE_LOGICAL_ADDRESS:
- int deviceType = msg.arg1;
- int preferredAddress = msg.arg2;
- AllocateLogicalAddressCallback callback =
- (AllocateLogicalAddressCallback) msg.obj;
- handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
- break;
- default:
- Slog.w(TAG, "Unsupported CEC Io request:" + msg.what);
- break;
- }
- }
-
- private void handleAllocateLogicalAddress(int deviceType, int preferredAddress,
- AllocateLogicalAddressCallback callback) {
- int startAddress = preferredAddress;
- // If preferred address is "unregistered", start_index will be the smallest
- // address matched with the given device type.
- if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) {
- for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
- if (deviceType == HdmiCec.getTypeFromAddress(i)) {
- startAddress = i;
- break;
- }
- }
- }
-
- int logcialAddress = HdmiCec.ADDR_UNREGISTERED;
- // Iterates all possible addresses which has the same device type.
- for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
- int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
- if (curAddress != HdmiCec.ADDR_UNREGISTERED
- && deviceType == HdmiCec.getTypeFromAddress(i)) {
- // <Polling Message> is a message which has empty body and
- // uses same address for both source and destination address.
- // If sending <Polling Message> failed (NAK), it becomes
- // new logical address for the device because no device uses
- // it as logical address of the device.
- int error = nativeSendCecCommand(mNativePtr, curAddress, curAddress,
- EMPTY_BODY);
- if (error != ERROR_SUCCESS) {
- logcialAddress = curAddress;
- break;
- }
- }
- }
-
- Message msg = mControlHandler.obtainMessage(MSG_REPORT_LOGICAL_ADDRESS);
- msg.arg1 = deviceType;
- msg.arg2 = logcialAddress;
- msg.obj = callback;
- mControlHandler.sendMessage(msg);
- }
- }
-
- private final class ControlHandler extends Handler {
- private ControlHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_RECEIVE_CEC_COMMAND:
- // TODO: delegate it to HdmiControl service.
- onReceiveCommand((HdmiCecMessage) msg.obj);
- break;
- case MSG_REPORT_LOGICAL_ADDRESS:
- int deviceType = msg.arg1;
- int logicalAddress = msg.arg2;
- AllocateLogicalAddressCallback callback =
- (AllocateLogicalAddressCallback) msg.obj;
- callback.onAllocated(deviceType, logicalAddress);
- break;
- default:
- Slog.i(TAG, "Unsupported message type:" + msg.what);
- break;
- }
- }
- }
-
/**
* Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
* logical address as new device info's.
@@ -298,6 +233,7 @@
* that has the same logical address as new one has.
*/
HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
+ assertRunOnServiceThread();
HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
if (oldDeviceInfo != null) {
removeDeviceInfo(deviceInfo.getLogicalAddress());
@@ -316,6 +252,7 @@
* @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
*/
HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
+ assertRunOnServiceThread();
HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
if (deviceInfo != null) {
mDeviceInfos.remove(logicalAddress);
@@ -324,13 +261,14 @@
}
/**
- * Return a list of all {@HdmiCecDeviceInfo}.
+ * Return a list of all {@link HdmiCecDeviceInfo}.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*/
List<HdmiCecDeviceInfo> getDeviceInfoList() {
- List<HdmiCecDeviceInfo> deviceInfoList = new ArrayList<HdmiCecDeviceInfo>(
- mDeviceInfos.size());
+ assertRunOnServiceThread();
+
+ List<HdmiCecDeviceInfo> deviceInfoList = new ArrayList<>(mDeviceInfos.size());
for (int i = 0; i < mDeviceInfos.size(); ++i) {
deviceInfoList.add(mDeviceInfos.valueAt(i));
}
@@ -347,6 +285,7 @@
* Returns null if no logical address matched
*/
HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
+ assertRunOnServiceThread();
return mDeviceInfos.get(logicalAddress);
}
@@ -361,8 +300,8 @@
* @return 0 on success. Otherwise, returns negative value
*/
int addLogicalAddress(int newLogicalAddress) {
+ assertRunOnServiceThread();
if (HdmiCec.isValidAddress(newLogicalAddress)) {
- mLocalAddresses.put(newLogicalAddress, newLogicalAddress);
return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
} else {
return -1;
@@ -375,9 +314,12 @@
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*/
void clearLogicalAddress() {
+ assertRunOnServiceThread();
// TODO: consider to backup logical address so that new logical address
// allocation can use it as preferred address.
- mLocalAddresses.clear();
+ for (HdmiCecLocalDevice device : mLocalDevices) {
+ device.clearAddress();
+ }
nativeClearLogicalAddress(mNativePtr);
}
@@ -390,6 +332,7 @@
* is between 0x0000 and 0xFFFF. If failed it returns -1
*/
int getPhysicalAddress() {
+ assertRunOnServiceThread();
return nativeGetPhysicalAddress(mNativePtr);
}
@@ -399,6 +342,7 @@
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*/
int getVersion() {
+ assertRunOnServiceThread();
return nativeGetVersion(mNativePtr);
}
@@ -408,24 +352,114 @@
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*/
int getVendorId() {
+ assertRunOnServiceThread();
return nativeGetVendorId(mNativePtr);
}
- private void init(HdmiControlService service, long nativePtr) {
- mService = service;
- mIoHandler = new IoHandler(service.getServiceLooper());
- mControlHandler = new ControlHandler(service.getServiceLooper());
- mNativePtr = nativePtr;
+ /**
+ * Poll all remote devices. It sends <Polling Message> to all remote
+ * devices.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param callback an interface used to get a list of all remote devices' address
+ * @param retryCount the number of retry used to send polling message to remote devices
+ */
+ void pollDevices(DevicePollingCallback callback, int retryCount) {
+ assertRunOnServiceThread();
+ // Extract polling candidates. No need to poll against local devices.
+ ArrayList<Integer> pollingCandidates = new ArrayList<>();
+ for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) {
+ if (!isAllocatedLocalDeviceAddress(i)) {
+ pollingCandidates.add(i);
+ }
+ }
+
+ runDevicePolling(pollingCandidates, retryCount, callback);
+ }
+
+ private boolean isAllocatedLocalDeviceAddress(int address) {
+ for (HdmiCecLocalDevice device : mLocalDevices) {
+ if (device.isAddressOf(address)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void runDevicePolling(final List<Integer> candidates, final int retryCount,
+ final DevicePollingCallback callback) {
+ assertRunOnServiceThread();
+ runOnIoThread(new Runnable() {
+ @Override
+ public void run() {
+ final ArrayList<Integer> allocated = new ArrayList<>();
+ for (Integer address : candidates) {
+ if (sendPollMessage(address, retryCount)) {
+ allocated.add(address);
+ }
+ }
+ if (callback != null) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ callback.onPollingFinished(allocated);
+ }
+ });
+ }
+ }
+ });
+ }
+
+ private boolean sendPollMessage(int address, int retryCount) {
+ assertRunOnIoThread();
+ for (int i = 0; i < retryCount; ++i) {
+ // <Polling Message> is a message which has empty body and
+ // uses same address for both source and destination address.
+ // If sending <Polling Message> failed (NAK), it becomes
+ // new logical address for the device because no device uses
+ // it as logical address of the device.
+ if (nativeSendCecCommand(mNativePtr, address, address, EMPTY_BODY)
+ == HdmiControlService.SEND_RESULT_SUCCESS) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void assertRunOnIoThread() {
+ if (Looper.myLooper() != mIoHandler.getLooper()) {
+ throw new IllegalStateException("Should run on io thread.");
+ }
+ }
+
+ private void assertRunOnServiceThread() {
+ if (Looper.myLooper() != mControlHandler.getLooper()) {
+ throw new IllegalStateException("Should run on service thread.");
+ }
+ }
+
+ // Run a Runnable on IO thread.
+ // It should be careful to access member variables on IO thread because
+ // it can be accessed from system thread as well.
+ private void runOnIoThread(Runnable runnable) {
+ mIoHandler.post(runnable);
+ }
+
+ private void runOnServiceThread(Runnable runnable) {
+ mControlHandler.post(runnable);
}
private boolean isAcceptableAddress(int address) {
- // Can access command targeting devices available in local device or
- // broadcast command.
- return address == HdmiCec.ADDR_BROADCAST
- || mLocalAddresses.indexOfKey(address) < 0;
+ // Can access command targeting devices available in local device or broadcast command.
+ if (address == HdmiCec.ADDR_BROADCAST) {
+ return true;
+ }
+ return isAllocatedLocalDeviceAddress(address);
}
private void onReceiveCommand(HdmiCecMessage message) {
+ assertRunOnServiceThread();
if (isAcceptableAddress(message.getDestination()) &&
mService.handleCecCommand(message)) {
return;
@@ -438,13 +472,34 @@
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand
(sourceAddress, message.getSource(), message.getOpcode(),
HdmiCecMessageBuilder.ABORT_REFUSED);
- sendCommand(cecMessage);
-
+ sendCommand(cecMessage, null);
}
- boolean sendCommand(HdmiCecMessage cecMessage) {
- Message message = mIoHandler.obtainMessage(MSG_SEND_CEC_COMMAND, cecMessage);
- return mIoHandler.sendMessage(message);
+ void sendCommand(HdmiCecMessage cecMessage) {
+ sendCommand(cecMessage, null);
+ }
+
+ void sendCommand(final HdmiCecMessage cecMessage,
+ final HdmiControlService.SendMessageCallback callback) {
+ runOnIoThread(new Runnable() {
+ @Override
+ public void run() {
+ byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
+ final int error = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
+ cecMessage.getDestination(), body);
+ if (error != HdmiControlService.SEND_RESULT_SUCCESS) {
+ Slog.w(TAG, "Failed to send " + cecMessage);
+ }
+ if (callback != null) {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ callback.onSendCompleted(error);
+ }
+ });
+ }
+ }
+ });
}
/**
@@ -453,12 +508,15 @@
private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
byte opcode = body[0];
byte params[] = Arrays.copyOfRange(body, 1, body.length);
- HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
+ final HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
// Delegate message to main handler so that it handles in main thread.
- Message message = mControlHandler.obtainMessage(
- MSG_RECEIVE_CEC_COMMAND, cecMessage);
- mControlHandler.sendMessage(message);
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ onReceiveCommand(cecMessage);
+ }
+ });
}
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
new file mode 100644
index 0000000..e65e5fa
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -0,0 +1,103 @@
+/*
+ * 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.hdmi;
+
+import com.android.server.hdmi.HdmiCecController.AllocateLogicalAddressCallback;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+
+/**
+ * Class that models a logical CEC device hosted in this system. Handles initialization,
+ * CEC commands that call for actions customized per device type.
+ */
+abstract class HdmiCecLocalDevice {
+
+ protected final HdmiCecController mController;
+ protected final int mDeviceType;
+ protected int mAddress;
+ protected int mPreferredAddress;
+ protected HdmiCecDeviceInfo mDeviceInfo;
+
+ protected HdmiCecLocalDevice(HdmiCecController controller, int deviceType) {
+ mController = controller;
+ mDeviceType = deviceType;
+ mAddress = HdmiCec.ADDR_UNREGISTERED;
+ }
+
+ // Factory method that returns HdmiCecLocalDevice of corresponding type.
+ static HdmiCecLocalDevice create(HdmiCecController controller, int deviceType) {
+ switch (deviceType) {
+ case HdmiCec.DEVICE_TV:
+ return new HdmiCecLocalDeviceTv(controller);
+ case HdmiCec.DEVICE_PLAYBACK:
+ return new HdmiCecLocalDevicePlayback(controller);
+ default:
+ return null;
+ }
+ }
+
+ abstract void init();
+
+ protected void allocateAddress(int type) {
+ mController.allocateLogicalAddress(type, mPreferredAddress,
+ new AllocateLogicalAddressCallback() {
+ @Override
+ public void onAllocated(int deviceType, int logicalAddress) {
+ mAddress = mPreferredAddress = logicalAddress;
+
+ // Create and set device info.
+ HdmiCecDeviceInfo deviceInfo = createDeviceInfo(mAddress, deviceType);
+ setDeviceInfo(deviceInfo);
+ mController.addDeviceInfo(deviceInfo);
+
+ mController.addLogicalAddress(logicalAddress);
+ }
+ });
+ }
+
+ private final HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
+ int vendorId = mController.getVendorId();
+ int physicalAddress = mController.getPhysicalAddress();
+ // TODO: get device name read from system configuration.
+ String displayName = HdmiCec.getDefaultDeviceName(logicalAddress);
+ return new HdmiCecDeviceInfo(logicalAddress,
+ physicalAddress, deviceType, vendorId, displayName);
+ }
+
+ HdmiCecDeviceInfo getDeviceInfo() {
+ return mDeviceInfo;
+ }
+
+ void setDeviceInfo(HdmiCecDeviceInfo info) {
+ mDeviceInfo = info;
+ }
+
+ // Returns true if the logical address is same as the argument.
+ boolean isAddressOf(int addr) {
+ return addr == mAddress;
+ }
+
+ // Resets the logical address to unregistered(15), meaning the logical device is invalid.
+ void clearAddress() {
+ mAddress = HdmiCec.ADDR_UNREGISTERED;
+ }
+
+ void setPreferredAddress(int addr) {
+ mPreferredAddress = addr;
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
new file mode 100644
index 0000000..a953467
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -0,0 +1,36 @@
+/*
+ * 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.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+
+/**
+ * Represent a logical device of type Playback residing in Android system.
+ */
+final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
+
+ HdmiCecLocalDevicePlayback(HdmiCecController controller) {
+ super(controller, HdmiCec.DEVICE_PLAYBACK);
+ }
+
+ @Override
+ void init() {
+ allocateAddress(mDeviceType);
+ mController.sendCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ mAddress, mController.getPhysicalAddress(), mDeviceType));
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
new file mode 100644
index 0000000..01ea685
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -0,0 +1,43 @@
+/*
+ * 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.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+
+/**
+ * Represent a logical device of type TV residing in Android system.
+ */
+final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
+
+ HdmiCecLocalDeviceTv(HdmiCecController controller) {
+ super(controller, HdmiCec.DEVICE_TV);
+ }
+
+ @Override
+ void init() {
+ allocateAddress(mDeviceType);
+
+ // TODO: vendor-specific initialization here.
+
+ mController.sendCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ mAddress, mController.getPhysicalAddress(), mDeviceType));
+ mController.sendCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
+ mAddress, mController.getVendorId()));
+
+ // TODO: Start routing control action, device discovery action.
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 09153b9..74961fd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -18,27 +18,23 @@
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.hdmi.IHdmiControlCallback;
-import android.hardware.hdmi.IHdmiControlService;
-import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.hdmi.IHdmiControlService;
+import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Slog;
-import android.util.SparseArray;
-
import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -54,6 +50,37 @@
// TODO: Rename the permission to HDMI_CONTROL.
private static final String PERMISSION = "android.permission.HDMI_CEC";
+ static final int SEND_RESULT_SUCCESS = 0;
+ static final int SEND_RESULT_NAK = -1;
+ static final int SEND_RESULT_FAILURE = -2;
+
+ /**
+ * Interface to report send result.
+ */
+ interface SendMessageCallback {
+ /**
+ * Called when {@link HdmiControlService#sendCecCommand} is completed.
+ *
+ * @param error result of send request.
+ * @see {@link #SEND_RESULT_SUCCESS}
+ * @see {@link #SEND_RESULT_NAK}
+ * @see {@link #SEND_RESULT_FAILURE}
+ */
+ void onSendCompleted(int error);
+ }
+
+ /**
+ * Interface to get a list of available logical devices.
+ */
+ interface DevicePollingCallback {
+ /**
+ * Called when device polling is finished.
+ *
+ * @param ackedAddress a list of logical addresses of available devices
+ */
+ void onPollingFinished(List<Integer> ackedAddress);
+ }
+
// A thread to handle synchronous IO of CEC and MHL control service.
// Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
// and sparse call it shares a thread to handle IO operations.
@@ -205,10 +232,14 @@
* Transmit a CEC command to CEC bus.
*
* @param command CEC command to send out
- * @return {@code true} if succeeds to send command
+ * @param callback interface used to the result of send command
*/
- boolean sendCecCommand(HdmiCecMessage command) {
- return mCecController.sendCommand(command);
+ void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+ mCecController.sendCommand(command, callback);
+ }
+
+ void sendCecCommand(HdmiCecMessage command) {
+ mCecController.sendCommand(command, null);
}
/**
@@ -263,6 +294,17 @@
// TODO: Start "RequestArcInitiationAction" if ARC port.
}
+ /**
+ * Poll all remote devices. It sends <Polling Message> to all remote
+ * devices.
+ *
+ * @param callback an interface used to get a list of all remote devices' address
+ * @param retryCount the number of retry used to send polling message to remote devices
+ */
+ void pollDevices(DevicePollingCallback callback, int retryCount) {
+ mCecController.pollDevices(callback, retryCount);
+ }
+
private void handleInitiateArc(HdmiCecMessage message){
// In case where <Initiate Arc> is started by <Request ARC Initiation>
// need to clean up RequestArcInitiationAction.
diff --git a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
index 0a701f9..343aff7 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
@@ -16,7 +16,7 @@
package com.android.server.hdmi;
-import android.util.Slog;
+import android.hardware.hdmi.HdmiCecMessage;
/**
* Feature action that handles ARC action initiated by TV devices.
@@ -37,17 +37,22 @@
@Override
boolean start() {
- if (sendCommand(
- HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress, mAvrAddress))) {
- mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
- addTimer(mState, TIMEOUT_MS);
- } else {
- Slog.w(TAG, "Failed to send <Request ARC Initiation>");
- // If failed to send <Request ARC Initiation>, start "Disabled" ARC transmission
- // action.
- disableArcTransmission();
- finish();
- }
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress,
+ mAvrAddress);
+ sendCommand(command, new HdmiControlService.SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
+ addTimer(mState, TIMEOUT_MS);
+ } else {
+ // If failed to send <Request ARC Initiation>, start "Disabled"
+ // ARC transmission action.
+ disableArcTransmission();
+ finish();
+ }
+ }
+ });
return true;
}
}
diff --git a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
index db1b992..d4a35f8 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
@@ -16,7 +16,7 @@
package com.android.server.hdmi;
-import android.util.Slog;
+import android.hardware.hdmi.HdmiCecMessage;
/**
* Feature action to handle <Request ARC Termination>.
@@ -37,17 +37,22 @@
@Override
boolean start() {
- if (sendCommand(
- HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress))) {
- mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
- addTimer(mState, TIMEOUT_MS);
- } else {
- Slog.w(TAG, "Failed to send <Request ARC Initiation>");
- // If failed to send <Request ARC Termination>, start "Disabled" ARC transmission
- // action.
- disableArcTransmission();
- finish();
- }
+ HdmiCecMessage command =
+ HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress);
+ sendCommand(command, new HdmiControlService.SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
+ addTimer(mState, TIMEOUT_MS);
+ } else {
+ // If failed to send <Request ARC Termination>, start "Disabled" ARC
+ // transmission action.
+ disableArcTransmission();
+ finish();
+ }
+ }
+ });
return true;
}
}
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index 6bf149b..e3525d8 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -64,26 +64,7 @@
@Override
boolean start() {
if (mEnabled) {
- if (sendCommand(
- HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress))) {
- // Enable ARC status immediately after sending <Report Arc Initiated>.
- // If AVR responds with <Feature Abort>, disable ARC status again.
- // This is different from spec that says that turns ARC status to "Enabled"
- // if <Report ARC Initiated> is acknowledged and no <Feature Abort> is received.
- // But implemented this way to save the time having to wait for <Feature Abort>.
- setArcStatus(true);
- // If succeeds to send <Report ARC Initiated>, wait general timeout
- // to check whether there is no <Feature Abort> for <Report ARC Initiated>.
- mState = STATE_WAITING_TIMEOUT;
- addTimer(mState, TIMEOUT_MS);
- } else {
- // If fails to send <Report ARC Initiated>, disable ARC and
- // send <Report ARC Terminated> directly.
- Slog.w(TAG, "Failed to send <Report ARC Initiated>:[source:" + mSourceAddress
- + ", avr Address:" + mAvrAddress + "]");
- setArcStatus(false);
- finish();
- }
+ sendReportArcInitiated();
} else {
setArcStatus(false);
finish();
@@ -91,6 +72,35 @@
return true;
}
+ private void sendReportArcInitiated() {
+ HdmiCecMessage command =
+ HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress);
+ sendCommand(command, new HdmiControlService.SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+ // Enable ARC status immediately after sending <Report Arc Initiated>.
+ // If AVR responds with <Feature Abort>, disable ARC status again.
+ // This is different from spec that says that turns ARC status to
+ // "Enabled" if <Report ARC Initiated> is acknowledged and no
+ // <Feature Abort> is received.
+ // But implemented this way to save the time having to wait for
+ // <Feature Abort>.
+ setArcStatus(true);
+ // If succeeds to send <Report ARC Initiated>, wait general timeout
+ // to check whether there is no <Feature Abort> for <Report ARC Initiated>.
+ mState = STATE_WAITING_TIMEOUT;
+ addTimer(mState, TIMEOUT_MS);
+ } else {
+ // If fails to send <Report ARC Initiated>, disable ARC and
+ // send <Report ARC Terminated> directly.
+ setArcStatus(false);
+ finish();
+ }
+ }
+ });
+ }
+
private void setArcStatus(boolean enabled) {
boolean wasEnabled = mService.setArcStatus(enabled);
Slog.i(TAG, "Change arc status [old:" + wasEnabled + " ,new:" + enabled);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index f90d7ab..3ed73f7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -32,6 +32,7 @@
import android.content.pm.Signature;
import android.os.Build;
import android.os.Bundle;
+import android.os.FileBridge;
import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
@@ -114,7 +115,7 @@
private boolean mPermissionsConfirmed;
private boolean mInvalid;
- private ArrayList<WritePipe> mPipes = new ArrayList<>();
+ private ArrayList<FileBridge> mBridges = new ArrayList<>();
private IPackageInstallObserver2 mRemoteObserver;
@@ -159,14 +160,14 @@
// Quick sanity check of state, and allocate a pipe for ourselves. We
// then do heavy disk allocation outside the lock, but this open pipe
// will block any attempted install transitions.
- final WritePipe pipe;
+ final FileBridge bridge;
synchronized (mLock) {
if (!mMutationsAllowed) {
throw new IllegalStateException("Mutations not allowed");
}
- pipe = new WritePipe();
- mPipes.add(pipe);
+ bridge = new FileBridge();
+ mBridges.add(bridge);
}
try {
@@ -194,9 +195,9 @@
Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
}
- pipe.setTargetFd(targetFd);
- pipe.start();
- return pipe.getWriteFd();
+ bridge.setTargetFile(targetFd);
+ bridge.start();
+ return new ParcelFileDescriptor(bridge.getClientSocket());
} catch (ErrnoException e) {
throw new IllegalStateException("Failed to write", e);
@@ -218,8 +219,8 @@
// Verify that all writers are hands-off
if (mMutationsAllowed) {
- for (WritePipe pipe : mPipes) {
- if (!pipe.isClosed()) {
+ for (FileBridge bridge : mBridges) {
+ if (!bridge.isClosed()) {
throw new InstallFailedException(INSTALL_FAILED_PACKAGE_CHANGED,
"Files still open");
}
@@ -482,52 +483,6 @@
}
}
- private static class WritePipe extends Thread {
- private final ParcelFileDescriptor[] mPipe;
-
- private FileDescriptor mTargetFd;
-
- private volatile boolean mClosed;
-
- public WritePipe() {
- try {
- mPipe = ParcelFileDescriptor.createPipe();
- } catch (IOException e) {
- throw new IllegalStateException("Failed to create pipe");
- }
- }
-
- public boolean isClosed() {
- return mClosed;
- }
-
- public void setTargetFd(FileDescriptor targetFd) {
- mTargetFd = targetFd;
- }
-
- public ParcelFileDescriptor getWriteFd() {
- return mPipe[1];
- }
-
- @Override
- public void run() {
- FileInputStream in = null;
- FileOutputStream out = null;
- try {
- // TODO: look at switching to sendfile(2) to speed up
- in = new FileInputStream(mPipe[0].getFileDescriptor());
- out = new FileOutputStream(mTargetFd);
- Streams.copy(in, out);
- } catch (IOException e) {
- Slog.w(TAG, "Failed to stream data: " + e);
- } finally {
- IoUtils.closeQuietly(mPipe[0]);
- IoUtils.closeQuietly(mTargetFd);
- mClosed = true;
- }
- }
- }
-
private class InstallFailedException extends Exception {
private final int error;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 102b2d4..1bf40e0 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1140,53 +1140,57 @@
*/
public boolean removeUser(int userHandle) {
checkManageUsersPermission("Only the system can remove users");
- final UserInfo user;
- synchronized (mPackagesLock) {
- user = mUsers.get(userHandle);
- if (userHandle == 0 || user == null) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ final UserInfo user;
+ synchronized (mPackagesLock) {
+ user = mUsers.get(userHandle);
+ if (userHandle == 0 || user == null) {
+ return false;
+ }
+ mRemovingUserIds.put(userHandle, true);
+ try {
+ mAppOpsService.removeUser(userHandle);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e);
+ }
+ // Set this to a partially created user, so that the user will be purged
+ // on next startup, in case the runtime stops now before stopping and
+ // removing the user completely.
+ user.partial = true;
+ // Mark it as disabled, so that it isn't returned any more when
+ // profiles are queried.
+ user.flags |= UserInfo.FLAG_DISABLED;
+ writeUserLocked(user);
+ }
+
+ if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
+ && user.isManagedProfile()) {
+ // Send broadcast to notify system that the user removed was a
+ // managed user.
+ sendProfileRemovedBroadcast(user.profileGroupId, user.id);
+ }
+
+ if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle);
+ int res;
+ try {
+ res = ActivityManagerNative.getDefault().stopUser(userHandle,
+ new IStopUserCallback.Stub() {
+ @Override
+ public void userStopped(int userId) {
+ finishRemoveUser(userId);
+ }
+ @Override
+ public void userStopAborted(int userId) {
+ }
+ });
+ } catch (RemoteException e) {
return false;
}
- mRemovingUserIds.put(userHandle, true);
- try {
- mAppOpsService.removeUser(userHandle);
- } catch (RemoteException e) {
- Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e);
- }
- // Set this to a partially created user, so that the user will be purged
- // on next startup, in case the runtime stops now before stopping and
- // removing the user completely.
- user.partial = true;
- // Mark it as disabled, so that it isn't returned any more when
- // profiles are queried.
- user.flags |= UserInfo.FLAG_DISABLED;
- writeUserLocked(user);
+ return res == ActivityManager.USER_OP_SUCCESS;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
-
- if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
- && user.isManagedProfile()) {
- // Send broadcast to notify system that the user removed was a
- // managed user.
- sendProfileRemovedBroadcast(user.profileGroupId, user.id);
- }
-
- if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle);
- int res;
- try {
- res = ActivityManagerNative.getDefault().stopUser(userHandle,
- new IStopUserCallback.Stub() {
- @Override
- public void userStopped(int userId) {
- finishRemoveUser(userId);
- }
- @Override
- public void userStopAborted(int userId) {
- }
- });
- } catch (RemoteException e) {
- return false;
- }
-
- return res == ActivityManager.USER_OP_SUCCESS;
}
void finishRemoveUser(final int userHandle) {
diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java
index 6d208ff..80030b4 100644
--- a/services/core/java/com/android/server/task/TaskManagerService.java
+++ b/services/core/java/com/android/server/task/TaskManagerService.java
@@ -16,12 +16,22 @@
package com.android.server.task;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.task.ITaskManager;
+import android.app.task.Task;
import android.content.Context;
-import android.content.Task;
+import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.util.Log;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.server.task.controllers.TaskStatus;
@@ -39,13 +49,6 @@
/** Master list of tasks. */
private final TaskStore mTasks;
- /** Check the pending queue and start any tasks. */
- static final int MSG_RUN_PENDING = 0;
- /** Initiate the stop task flow. */
- static final int MSG_STOP_TASK = 1;
- /** */
- static final int MSG_CHECK_TASKS = 2;
-
/**
* Track Services that have currently active or pending tasks. The index is provided by
* {@link TaskStatus#getServiceToken()}
@@ -54,6 +57,14 @@
new SparseArray<TaskServiceContext>();
private final TaskHandler mHandler;
+ private final TaskManagerStub mTaskManagerStub;
+
+ /** Check the pending queue and start any tasks. */
+ static final int MSG_RUN_PENDING = 0;
+ /** Initiate the stop task flow. */
+ static final int MSG_STOP_TASK = 1;
+ /** */
+ static final int MSG_CHECK_TASKS = 2;
private class TaskHandler extends Handler {
@@ -94,21 +105,6 @@
}
/**
- * Entry point from client to schedule the provided task.
- * This will add the task to the
- * @param task Task object containing execution parameters
- * @param userId The id of the user this task is for.
- * @param uId The package identifier of the application this task is for.
- * @param canPersistTask Whether or not the client has the appropriate permissions for persisting
- * of this task.
- * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
- */
- public int schedule(Task task, int userId, int uId, boolean canPersistTask) {
- TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask);
- return 0;
- }
-
- /**
* Initializes the system service.
* <p>
* Subclasses must define a single argument constructor that accepts the context
@@ -121,11 +117,42 @@
super(context);
mTasks = new TaskStore(context);
mHandler = new TaskHandler(context.getMainLooper());
+ mTaskManagerStub = new TaskManagerStub();
}
@Override
public void onStart() {
+ publishBinderService(Context.TASK_SERVICE, mTaskManagerStub);
+ }
+ /**
+ * Entry point from client to schedule the provided task.
+ * This will add the task to the
+ * @param task Task object containing execution parameters
+ * @param userId The id of the user this task is for.
+ * @param uId The package identifier of the application this task is for.
+ * @param canPersistTask Whether or not the client has the appropriate permissions for
+ * persisting of this task.
+ * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
+ */
+ public int schedule(Task task, int userId, int uId, boolean canPersistTask) {
+ TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask);
+ return 0;
+ }
+
+ public List<Task> getPendingTasks(int uid) {
+ ArrayList<Task> outList = new ArrayList<Task>(3);
+ synchronized (mTasks) {
+ final SparseArray<TaskStatus> tasks = mTasks.getTasks();
+ final int N = tasks.size();
+ for (int i = 0; i < N; i++) {
+ TaskStatus ts = tasks.get(i);
+ if (ts.getUid() == uid) {
+ outList.add(ts.getTask());
+ }
+ }
+ }
+ return outList;
}
// StateChangedListener implementations.
@@ -162,7 +189,7 @@
public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule) {
final TaskServiceContext serviceContext = mActiveServices.get(serviceToken);
if (serviceContext == null) {
- Log.e(TAG, "Task completed for invalid service context; " + serviceToken);
+ Slog.e(TAG, "Task completed for invalid service context; " + serviceToken);
return;
}
@@ -203,4 +230,98 @@
private void postCheckTasksMessage() {
mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
}
+
+ /**
+ * Binder stub trampoline implementation
+ */
+ final class TaskManagerStub extends ITaskManager.Stub {
+ /** Cache determination of whether a given app can persist tasks
+ * key is uid of the calling app; value is undetermined/true/false
+ */
+ private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
+
+ // Determine whether the caller is allowed to persist tasks, with a small cache
+ // because the lookup is expensive enough that we'd like to avoid repeating it.
+ // This must be called from within the calling app's binder identity!
+ private boolean canCallerPersistTasks() {
+ final boolean canPersist;
+ final int callingUid = Binder.getCallingUid();
+ synchronized (mPersistCache) {
+ Boolean cached = mPersistCache.get(callingUid);
+ if (cached) {
+ canPersist = cached.booleanValue();
+ } else {
+ // Persisting tasks is tantamount to running at boot, so we permit
+ // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
+ // permission
+ int result = getContext().checkCallingPermission(
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED);
+ canPersist = (result == PackageManager.PERMISSION_GRANTED);
+ mPersistCache.put(callingUid, canPersist);
+ }
+ }
+ return canPersist;
+ }
+
+ // ITaskManager implementation
+ @Override
+ public int schedule(Task task) throws RemoteException {
+ final boolean canPersist = canCallerPersistTasks();
+ final int uid = Binder.getCallingUid();
+ final int userId = UserHandle.getCallingUserId();
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return TaskManagerService.this.schedule(task, userId, uid, canPersist);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public List<Task> getAllPendingTasks() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void cancelAll() throws RemoteException {
+ }
+
+ @Override
+ public void cancel(int taskId) throws RemoteException {
+ }
+
+ /**
+ * "dumpsys" infrastructure
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+
+ long identityToken = Binder.clearCallingIdentity();
+ try {
+ TaskManagerService.this.dumpInternal(pw);
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+ };
+
+ void dumpInternal(PrintWriter pw) {
+ synchronized (mTasks) {
+ pw.print("Registered tasks:");
+ if (mTasks.size() > 0) {
+ SparseArray<TaskStatus> tasks = mTasks.getTasks();
+ for (int i = 0; i < tasks.size(); i++) {
+ TaskStatus ts = tasks.get(i);
+ pw.println();
+ ts.dump(pw, " ");
+ }
+ } else {
+ pw.println();
+ pw.println("No tasks scheduled.");
+ }
+ }
+ pw.println();
+ }
}
diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java
index b51cbb3..165445a 100644
--- a/services/core/java/com/android/server/task/TaskServiceContext.java
+++ b/services/core/java/com/android/server/task/TaskServiceContext.java
@@ -45,7 +45,7 @@
* is reused to start concurrent tasks on the TaskService. Information here is unique
* to the service.
* Functionality provided by this class:
- * - Managages wakelock for the service.
+ * - Manages wakelock for the service.
* - Sends onStartTask() and onStopTask() messages to client app, and handles callbacks.
* -
*/
diff --git a/services/core/java/com/android/server/task/TaskStore.java b/services/core/java/com/android/server/task/TaskStore.java
index 3bfc8a5..81187c8 100644
--- a/services/core/java/com/android/server/task/TaskStore.java
+++ b/services/core/java/com/android/server/task/TaskStore.java
@@ -16,8 +16,8 @@
package com.android.server.task;
+import android.app.task.Task;
import android.content.Context;
-import android.content.Task;
import android.util.SparseArray;
import com.android.server.task.controllers.TaskStatus;
@@ -95,6 +95,13 @@
}
/**
+ * @return the number of tasks in the store
+ */
+ public int size() {
+ return mTasks.size();
+ }
+
+ /**
* @return The live array of TaskStatus objects.
*/
public SparseArray<TaskStatus> getTasks() {
diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
index 6a4e1f3..a0038c5d 100644
--- a/services/core/java/com/android/server/task/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
@@ -70,13 +70,10 @@
}
/**
- * @param userId Id of the user for whom we are updating the connectivity state.
+ *
*/
- private void updateTrackedTasks(int userId) {
+ private void updateTrackedTasks() {
for (TaskStatus ts : mTrackedTasks) {
- if (ts.userId != userId) {
- continue;
- }
boolean prevIsConnected = ts.connectivityConstraintSatisfied.getAndSet(mConnectivity);
boolean prevIsMetered = ts.meteredConstraintSatisfied.getAndSet(mMetered);
if (prevIsConnected != mConnectivity || prevIsMetered != mMetered) {
@@ -107,14 +104,13 @@
final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
// This broadcast gets sent a lot, only update if the active network has changed.
if (activeNetwork.getType() == networkType) {
- final int userid = context.getUserId();
mMetered = false;
mConnectivity =
!intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
if (mConnectivity) { // No point making the call if we know there's no conn.
mMetered = connManager.isActiveNetworkMetered();
}
- updateTrackedTasks(userid);
+ updateTrackedTasks();
}
} else {
Log.w(TAG, "Unrecognised action in intent: " + action);
diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/task/controllers/TaskStatus.java
index d96fedc..d270016 100644
--- a/services/core/java/com/android/server/task/controllers/TaskStatus.java
+++ b/services/core/java/com/android/server/task/controllers/TaskStatus.java
@@ -16,17 +16,19 @@
package com.android.server.task.controllers;
+import android.app.task.Task;
import android.content.ComponentName;
-import android.content.Task;
import android.content.pm.PackageParser;
import android.os.Bundle;
import android.os.SystemClock;
+import android.os.UserHandle;
+import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Uniquely identifies a task internally.
- * Created from the public {@link android.content.Task} object when it lands on the scheduler.
+ * Created from the public {@link android.app.task.Task} object when it lands on the scheduler.
* Contains current state of the requirements of the task, as well as a function to evaluate
* whether it's ready to run.
* This object is shared among the various controllers - hence why the different fields are atomic.
@@ -36,10 +38,9 @@
* @hide
*/
public class TaskStatus {
+ final Task task;
final int taskId;
- final int userId;
final int uId;
- final ComponentName component;
final Bundle extras;
final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
@@ -57,9 +58,9 @@
private long earliestRunTimeElapsedMillis;
private long latestRunTimeElapsedMillis;
- /** Provide a unique handle to the service that this task will be run on. */
+ /** Provide a handle to the service that this task will be run on. */
public int getServiceToken() {
- return component.hashCode() + userId;
+ return uId;
}
/** Generate a TaskStatus object for a given task and uid. */
@@ -70,9 +71,8 @@
/** Set up the state of a newly scheduled task. */
TaskStatus(Task task, int userId, int uId) {
+ this.task = task;
this.taskId = task.getTaskId();
- this.userId = userId;
- this.component = task.getService();
this.extras = task.getExtras();
this.uId = uId;
@@ -100,16 +100,20 @@
hasConnectivityConstraint = task.getNetworkCapabilities() == Task.NetworkType.ANY;
}
+ public Task getTask() {
+ return task;
+ }
+
public int getTaskId() {
return taskId;
}
public ComponentName getServiceComponent() {
- return component;
+ return task.getService();
}
public int getUserId() {
- return userId;
+ return UserHandle.getUserId(uId);
}
public int getUid() {
@@ -161,9 +165,9 @@
@Override
public int hashCode() {
- int result = component.hashCode();
+ int result = getServiceComponent().hashCode();
result = 31 * result + taskId;
- result = 31 * result + userId;
+ result = 31 * result + uId;
return result;
}
@@ -174,7 +178,14 @@
TaskStatus that = (TaskStatus) o;
return ((taskId == that.taskId)
- && (userId == that.userId)
- && (component.equals(that.component)));
+ && (uId == that.uId)
+ && (getServiceComponent().equals(that.getServiceComponent())));
+ }
+
+ // Dumpsys infrastructure
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("Task "); pw.println(taskId);
+ pw.print(prefix); pw.print("uid="); pw.println(uId);
+ pw.print(prefix); pw.print("component="); pw.println(task.getService());
}
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 986cdc1..1629a614 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -40,6 +40,7 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
+import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -326,8 +327,12 @@
for (int i = 0; i < mTrustListeners.size(); i++) {
try {
mTrustListeners.get(i).onTrustChanged(enabled, userId);
+ } catch (DeadObjectException e) {
+ if (DEBUG) Slog.d(TAG, "Removing dead TrustListener.");
+ mTrustListeners.remove(i);
+ i--;
} catch (RemoteException e) {
- Slog.e(TAG, "Exception while notifying TrustListener. Removing listener.", e);
+ Slog.e(TAG, "Exception while notifying TrustListener.", e);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6fbb39d..c23d1ea 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3189,6 +3189,7 @@
WindowState win = atoken.findMainWindow();
Rect containingFrame = new Rect(0, 0, width, height);
Rect contentInsets = new Rect();
+ boolean isFullScreen = true;
if (win != null) {
if (win.mContainingFrame != null) {
containingFrame.set(win.mContainingFrame);
@@ -3196,11 +3197,11 @@
if (win.mContentInsets != null) {
contentInsets.set(win.mContentInsets);
}
+ isFullScreen =
+ ((win.mSystemUiVisibility & SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN) ==
+ SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN);
}
- boolean isFullScreen =
- ((win.mSystemUiVisibility & SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN)
- == SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN);
Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height,
mCurConfiguration.orientation, containingFrame, contentInsets, isFullScreen);
if (a != null) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index de46b16..bc34e0e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -83,6 +83,7 @@
import com.android.server.search.SearchManagerService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
+import com.android.server.task.TaskManagerService;
import com.android.server.trust.TrustManagerService;
import com.android.server.tv.TvInputManagerService;
import com.android.server.twilight.TwilightService;
@@ -132,6 +133,8 @@
"com.android.server.hdmi.HdmiCecService";
private static final String ETHERNET_SERVICE_CLASS =
"com.android.server.ethernet.EthernetService";
+ private static final String TASK_SERVICE_CLASS =
+ "com.android.server.task.TaskManagerService";
private final int mFactoryTestMode;
private Timer mProfilerSnapshotTimer;
@@ -831,6 +834,8 @@
mSystemServiceManager.startService(UiModeManagerService.class);
+ mSystemServiceManager.startService(TaskManagerService.class);
+
if (!disableNonCoreServices) {
try {
if (pm.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
new file mode 100644
index 0000000..c439211
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.internal.telecomm;
+
+/**
+ * Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing
+ * commands that were previously handled by ITelephony.
+ * {@hide}
+ */
+oneway interface ITelecommService {
+
+ /**
+ * Silence the ringer if an incoming call is currently ringing.
+ * (If vibrating, stop the vibrator also.)
+ *
+ * It's safe to call this if the ringer has already been silenced, or
+ * even if there's no incoming call. (If so, this method will do nothing.)
+ */
+ void silenceRinger();
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 5d485c5..4aed1fe 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -30,6 +30,7 @@
import android.telephony.Rlog;
import android.util.Log;
+import com.android.internal.telecomm.ITelecommService;
import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.ITelephonyRegistry;
@@ -65,6 +66,8 @@
public class TelephonyManager {
private static final String TAG = "TelephonyManager";
+ private static final String TELECOMM_SERVICE_NAME = "telecomm";
+
private static ITelephonyRegistry sRegistry;
/**
@@ -1536,6 +1539,10 @@
return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
}
+ private ITelecommService getTelecommService() {
+ return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
+ }
+
//
//
// PhoneStateListener
@@ -2016,9 +2023,9 @@
@PrivateApi
public void silenceRinger() {
try {
- getITelephony().silenceRinger();
+ getTelecommService().silenceRinger();
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#silenceRinger", e);
+ Log.e(TAG, "Error calling ITelecommService#silenceRinger", e);
}
}
@@ -2249,4 +2256,25 @@
}
return false;
}
+
+ /** @hide */
+ @PrivateApi
+ public void setDataEnabled(boolean enable) {
+ try {
+ getITelephony().setDataEnabled(enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setDataEnabled", e);
+ }
+ }
+
+ /** @hide */
+ @PrivateApi
+ public boolean getDataEnabled() {
+ try {
+ return getITelephony().getDataEnabled();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getDataEnabled", e);
+ }
+ return false;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index baacb74..6d7f158 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -436,4 +436,18 @@
* @return true on success; false on any failure.
*/
boolean setPreferredNetworkType(int networkType);
+
+ /**
+ * User enable/disable Mobile Data.
+ *
+ * @param enable true to turn on, else false
+ */
+ void setDataEnabled(boolean enable);
+
+ /**
+ * Get the user enabled state of Mobile Data.
+ *
+ * @return true on enabled
+ */
+ boolean getDataEnabled();
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index bafc71e..1157de7 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -509,8 +509,6 @@
* @hide
*/
public boolean isValid() {
- if (SSID == null)
- return false;
if (allowedKeyManagement == null)
return false;
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 1484d49..7debb93 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -224,9 +224,9 @@
public static final int TTLS = 2;
/** EAP-Password */
public static final int PWD = 3;
- /** EAP-Subscriber Identity Module */
+ /** EAP-Subscriber Identity Module {@hide} */
public static final int SIM = 4;
- /** EAP-Authentication and Key Agreement */
+ /** EAP-Authentication and Key Agreement {@hide} */
public static final int AKA = 5;
/** @hide */
public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA" };
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
index 08b430f..090ac56 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
@@ -25,13 +25,18 @@
import java.util.Iterator;
import java.util.Map;
+/**
+ * A class representing a Wi-Fi Passpoint credential.
+ */
public class WifiPasspointCredential implements Parcelable {
private final static String TAG = "PasspointCredential";
+ private String mWifiTreePath;
private String mWifiSPFQDN;
private String mCredentialName;
private String mUpdateIdentifier;
private String mSubscriptionUpdateMethod;
+ private WifiEnterpriseConfig mEnterpriseConfig;
private String mType;
private String mInnerMethod;
private String mCertType;
@@ -88,17 +93,16 @@
/**
* Constructor
* @param realm Realm of the passpoint credential
- * @param config Credential information, must be either EAP-TLS or EAP-TTLS.
+ * @param fqdn Fully qualified domain name (FQDN) of the credential
+ * @param config Enterprise config, must be either EAP-TLS or EAP-TTLS
* @see WifiEnterpriseConfig
*/
- public WifiPasspointCredential(String realm, WifiEnterpriseConfig config) {
+ public WifiPasspointCredential(String realm, String fqdn, WifiEnterpriseConfig config) {
mRealm = realm;
switch (config.getEapMethod()) {
case WifiEnterpriseConfig.Eap.TLS:
- // TODO;
- break;
case WifiEnterpriseConfig.Eap.TTLS:
- // TODO;
+ mEnterpriseConfig = new WifiEnterpriseConfig(config);
break;
default:
// ignore
@@ -294,11 +298,21 @@
}
/**
- * Get EAP method of this Passpoint credential.
- * @return EAP method, refer to {@link WifiEnterpriseConfig.Eap} for possible return values
+ * Get enterprise config of this Passpoint credential.
+ * @return Enterprise config
+ * @see WifiEnterpriseConfig
*/
- public int getEapMethod() {
- return 0;
+ public WifiEnterpriseConfig getEnterpriseConfig() {
+ return new WifiEnterpriseConfig(mEnterpriseConfig);
+ }
+
+ /**
+ * Set enterprise config of this Passpoint credential.
+ * @param config Enterprise config, must be either EAP-TLS or EAP-TTLS
+ * @see WifiEnterpriseConfig
+ */
+ public void setEnterpriseConfig(WifiEnterpriseConfig config) {
+ // TODO
}
/** @hide */
@@ -311,10 +325,7 @@
return mCertSha256Fingerprint;
}
- /**
- * Get the user name of this Passpoint credential, for EAP-TTLS only.
- * @return user name
- */
+ /** @hide */
public String getUserName() {
return mUsername;
}
@@ -325,10 +336,7 @@
return mPasswd;
}
- /**
- * Get the IMSI of this Passpoint credential, for EAP-SIM / EAP-AKA only.
- * @return IMSI
- */
+ /** @hide */
public String getImsi() {
return mImsi;
}
@@ -344,26 +352,31 @@
}
/** @hide */
- public String getCaRootCert() {
+ public String getCaRootCertPath() {
return mCaRootCert;
}
- /**
- * Get the client certificate path of this Passpoint credential, for EAP-TLS only.
- * @return client certificate path
- */
+ /** @hide */
public String getClientCertPath() {
return mClientCert;
}
/**
- * Get the realm of this Passpoint credential, for all EAP methods.
+ * Get the realm of this Passpoint credential.
* @return Realm
*/
public String getRealm() {
return mRealm;
}
+ /**
+ * Set the ream of this Passpoint credential.
+ * @param realm Realm
+ */
+ public void setRealm(String realm) {
+ mRealm = realm;
+ }
+
/** @hide */
public int getPriority() {
if (mUserPreferred) {
@@ -374,14 +387,22 @@
}
/**
- * Get the fully qualified domain name (FQDN) of this Passpoint credential,
- * for all EAP methods.
+ * Get the fully qualified domain name (FQDN) of this Passpoint credential.
* @return FQDN
*/
public String getFqdn() {
return mHomeSpFqdn;
}
+ /**
+ * Set the fully qualified domain name (FQDN) of this Passpoint credential.
+ * @param fqdn FQDN
+ */
+ public void setFqdn(String fqdn) {
+ mHomeSpFqdn = fqdn;
+ }
+
+
/** @hide */
public String getOtherhomepartners() {
return mOtherhomepartnerFqdn;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
index ee4dc5a..ebaa8c9 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
@@ -443,10 +443,9 @@
return null;
}
- /* TODO: add credential APIs */
-
/**
- * Give a list of all saved Passpoint credentials.
+ * Get a list of saved Passpoint credentials. Only those credentials owned
+ * by the caller will be returned.
*
* @return The list of credentials
*/