Merge "Page content sometimes disappears when scrolling a long doc in print preview." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index f9913fa..cbf4d67 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28073,14 +28073,18 @@
ctor public Conference(android.telecom.PhoneAccountHandle);
method public final boolean addConnection(android.telecom.Connection);
method public final void destroy();
+ method public final android.telecom.AudioState getAudioState();
method public final int getCapabilities();
method public final java.util.List<android.telecom.Connection> getConnections();
method public final android.telecom.PhoneAccountHandle getPhoneAccountHandle();
method public final int getState();
+ method public void onAudioStateChanged(android.telecom.AudioState);
method public void onDisconnect();
method public void onHold();
method public void onMerge();
+ method public void onPlayDtmfTone(char);
method public void onSeparate(android.telecom.Connection);
+ method public void onStopDtmfTone();
method public void onSwap();
method public void onUnhold();
method public final void removeConnection(android.telecom.Connection);
@@ -28219,7 +28223,6 @@
method public android.net.Uri getSubscriptionAddress();
method public java.util.List<java.lang.String> getSupportedUriSchemes();
method public boolean hasCapabilities(int);
- method public boolean isEnabled();
method public boolean supportsUriScheme(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
@@ -28274,8 +28277,11 @@
method public android.telecom.DisconnectCause getDisconnectCause();
method public final int getState();
method public void hold();
+ method public void playDtmfTone(char);
method public final void registerCallback(android.telecom.RemoteConference.Callback);
method public void separate(android.telecom.RemoteConnection);
+ method public void setAudioState(android.telecom.AudioState);
+ method public void stopDtmfTone();
method public void unhold();
method public final void unregisterCallback(android.telecom.RemoteConference.Callback);
}
@@ -28349,21 +28355,19 @@
method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
method public void cancelMissedCallsNotification();
method public void clearAccounts();
+ method public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
method public android.telecom.PhoneAccountHandle getConnectionManager();
method public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(java.lang.String);
- method public java.util.List<android.telecom.PhoneAccountHandle> getEnabledPhoneAccounts();
method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String);
method public boolean handleMmi(java.lang.String);
- method public boolean hasMultipleEnabledAccounts();
+ method public boolean hasMultipleCallCapableAccounts();
method public boolean isInCall();
method public void registerPhoneAccount(android.telecom.PhoneAccount);
method public void showInCallScreen(boolean);
method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
field public static final java.lang.String ACTION_CONNECTION_SERVICE_CONFIGURE = "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
- field public static final java.lang.String ACTION_PHONE_ACCOUNT_DISABLED = "android.telecom.action.PHONE_ACCOUNT_DISABLED";
- field public static final java.lang.String ACTION_PHONE_ACCOUNT_ENABLED = "android.telecom.action.PHONE_ACCOUNT_ENABLED";
field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 6a5ecee..b8b2087 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -39,6 +39,7 @@
}
private static final String COMMAND_SET_DEVICE_OWNER = "set-device-owner";
+ private static final String COMMAND_SET_PROFILE_OWNER = "set-profile-owner";
private IDevicePolicyManager mDevicePolicyManager;
@@ -47,9 +48,13 @@
out.println(
"usage: dpm [subcommand] [options]\n" +
"usage: dpm set-device-owner <COMPONENT>\n" +
+ "usage: dpm set-profile-owner <COMPONENT> <USER_ID>\n" +
"\n" +
"dpm set-device-owner: Sets the given component as active admin, and its\n" +
- " package as device owner.\n");
+ " package as device owner.\n" +
+ "\n" +
+ "dpm set-profile-owner: Sets the given component as active admin and profile" +
+ " owner for an existing user.\n");
}
@Override
@@ -64,24 +69,25 @@
String command = nextArgRequired();
switch (command) {
case COMMAND_SET_DEVICE_OWNER:
- runSetDeviceOwner(nextArgRequired());
+ runSetDeviceOwner();
+ break;
+ case COMMAND_SET_PROFILE_OWNER:
+ runSetProfileOwner();
break;
default:
throw new IllegalArgumentException ("unknown command '" + command + "'");
}
}
- private void runSetDeviceOwner(String argument) throws Exception {
- ComponentName component = ComponentName.unflattenFromString(argument);
- if (component == null) {
- throw new IllegalArgumentException ("Invalid component " + argument);
- }
- mDevicePolicyManager.setActiveAdmin(component, true, UserHandle.USER_OWNER);
+ private void runSetDeviceOwner() throws RemoteException {
+ ComponentName component = parseComponentName(nextArgRequired());
+ mDevicePolicyManager.setActiveAdmin(component, true /*refreshing*/, UserHandle.USER_OWNER);
String packageName = component.getPackageName();
try {
- if (!mDevicePolicyManager.setDeviceOwner(packageName, null)) {
- throw new Exception("Can't set package " + packageName + " as device owner.");
+ if (!mDevicePolicyManager.setDeviceOwner(packageName, null /*ownerName*/)) {
+ throw new RuntimeException(
+ "Can't set package " + packageName + " as device owner.");
}
} catch (Exception e) {
// Need to remove the admin that we just added.
@@ -91,4 +97,39 @@
System.out.println("Device owner set to package " + packageName);
System.out.println("Active admin set to component " + component.toShortString());
}
+
+ private void runSetProfileOwner() throws RemoteException {
+ ComponentName component = parseComponentName(nextArgRequired());
+ int userId = parseInt(nextArgRequired());
+ mDevicePolicyManager.setActiveAdmin(component, true /*refreshing*/, userId);
+
+ try {
+ if (!mDevicePolicyManager.setProfileOwner(component, "" /*ownerName*/, userId)) {
+ throw new RuntimeException("Can't set component " + component.toShortString() +
+ " as profile owner for user " + userId);
+ }
+ } catch (Exception e) {
+ // Need to remove the admin that we just added.
+ mDevicePolicyManager.removeActiveAdmin(component, userId);
+ throw e;
+ }
+ System.out.println("Active admin and profile owner set to " + component.toShortString() +
+ " for user " + userId);
+ }
+
+ private ComponentName parseComponentName(String component) {
+ ComponentName cn = ComponentName.unflattenFromString(component);
+ if (cn == null) {
+ throw new IllegalArgumentException ("Invalid component " + component);
+ }
+ return cn;
+ }
+
+ private int parseInt(String argument) {
+ try {
+ return Integer.parseInt(argument);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException ("Invalid integer argument '" + argument + "'", e);
+ }
+ }
}
\ No newline at end of file
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 6d12133..5e9d8f7 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1190,10 +1190,6 @@
if (userId < 0) {
info = mUm.createUser(name, flags);
} else {
- if (Process.myUid() != 0) {
- System.err.println("Error: not running as root.");
- return;
- }
info = mUm.createProfileForUser(name, flags, userId);
}
if (info != null) {
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 80e5b91..b83198d 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -120,6 +120,25 @@
*/
public static final int EVENT_UNBLOCK_ADDRESS_FAMILY = BASE + 8;
+ /**
+ * Sent by ConnectivitySerice to the NetworkAgent to inform the agent of the
+ * networks status - whether we could use the network or could not, due to
+ * either a bad network configuration (no internet link) or captive portal.
+ *
+ * arg1 = either {@code VALID_NETWORK} or {@code INVALID_NETWORK}
+ */
+ public static final int CMD_REPORT_NETWORK_STATUS = BASE + 9;
+
+ public static final int VALID_NETWORK = 1;
+ public static final int INVALID_NETWORK = 2;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to indicate this network was
+ * explicitly selected. This should be sent before the NetworkInfo is marked
+ * CONNECTED so it can be given special treatment at that time.
+ */
+ public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 10;
+
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
this(looper, context, logTag, ni, nc, lp, score, null);
@@ -181,6 +200,14 @@
log("Unhandled Message " + msg);
break;
}
+ case CMD_REPORT_NETWORK_STATUS: {
+ if (VDBG) {
+ log("CMD_REPORT_NETWORK_STATUS(" +
+ (msg.arg1 == VALID_NETWORK ? "VALID)" : "INVALID)"));
+ }
+ networkStatus(msg.arg1);
+ break;
+ }
}
}
@@ -261,6 +288,15 @@
}
/**
+ * Called by the bearer to indicate this network was manually selected by the user.
+ * This should be called before the NetworkInfo is marked CONNECTED so that this
+ * Network can be given special treatment at that time.
+ */
+ public void explicitlySelected() {
+ queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, 0);
+ }
+
+ /**
* Called when ConnectivityService has indicated they no longer want this network.
* The parent factory should (previously) have received indication of the change
* as well, either canceling NetworkRequests or altering their score such that this
@@ -268,6 +304,24 @@
*/
abstract protected void unwanted();
+ /**
+ * Called when the system determines the usefulness of this network.
+ *
+ * Networks claiming internet connectivity will have their internet
+ * connectivity verified.
+ *
+ * Currently there are two possible values:
+ * {@code VALID_NETWORK} if the system is happy with the connection,
+ * {@code INVALID_NETWORK} if the system is not happy.
+ * TODO - add indications of captive portal-ness and related success/failure,
+ * ie, CAPTIVE_SUCCESS_NETWORK, CAPTIVE_NETWORK for successful login and detection
+ *
+ * This may be called multiple times as the network status changes and may
+ * generate false negatives if we lose ip connectivity before the link is torn down.
+ */
+ protected void networkStatus(int status) {
+ }
+
protected void log(String s) {
Log.d(LOG_TAG, "NetworkAgent: " + s);
}
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 34f6cf4..5d2a43d 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -25,12 +25,32 @@
* @hide
*/
public class NetworkMisc implements Parcelable {
+
/**
* If the {@link Network} is a VPN, whether apps are allowed to bypass the VPN. This is set by
* a {@link VpnService} and used by {@link ConnectivityService} when creating a VPN.
*/
public boolean allowBypass;
+ /**
+ * Set if the network was manually/explicitly connected to by the user either from settings
+ * or a 3rd party app. For example, turning on cell data is not explicit but tapping on a wifi
+ * ap in the wifi settings to trigger a connection is explicit. A 3rd party app asking to
+ * connect to a particular access point is also explicit, though this may change in the future
+ * as we want apps to use the multinetwork apis.
+ */
+ public boolean explicitlySelected;
+
+ public NetworkMisc() {
+ }
+
+ public NetworkMisc(NetworkMisc nm) {
+ if (nm != null) {
+ allowBypass = nm.allowBypass;
+ explicitlySelected = nm.explicitlySelected;
+ }
+ }
+
@Override
public int describeContents() {
return 0;
@@ -39,6 +59,7 @@
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(allowBypass ? 1 : 0);
+ out.writeInt(explicitlySelected ? 1 : 0);
}
public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
@@ -46,6 +67,7 @@
public NetworkMisc createFromParcel(Parcel in) {
NetworkMisc networkMisc = new NetworkMisc();
networkMisc.allowBypass = in.readInt() != 0;
+ networkMisc.explicitlySelected = in.readInt() != 0;
return networkMisc;
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 2bfe3b4..9e0719d 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -316,7 +316,6 @@
top ? 0 : mSystemWindowInsets.top,
right ? 0 : mSystemWindowInsets.right,
bottom ? 0 : mSystemWindowInsets.bottom);
- result.mSystemWindowInsetsConsumed = !hasSystemWindowInsets();
return result;
}
return this;
@@ -336,7 +335,6 @@
int right, int bottom) {
final WindowInsets result = new WindowInsets(this);
result.mSystemWindowInsets = new Rect(left, top, right, bottom);
- result.mSystemWindowInsetsConsumed = !hasSystemWindowInsets();
return result;
}
@@ -351,7 +349,6 @@
public WindowInsets replaceSystemWindowInsets(Rect systemWindowInsets) {
final WindowInsets result = new WindowInsets(this);
result.mSystemWindowInsets = new Rect(systemWindowInsets);
- result.mSystemWindowInsetsConsumed = !hasSystemWindowInsets();
return result;
}
@@ -376,7 +373,6 @@
top ? 0 : mWindowDecorInsets.top,
right ? 0 : mWindowDecorInsets.right,
bottom ? 0 : mWindowDecorInsets.bottom);
- result.mWindowDecorInsetsConsumed = !hasWindowDecorInsets();
return result;
}
return this;
@@ -388,7 +384,6 @@
public WindowInsets replaceWindowDecorInsets(int left, int top, int right, int bottom) {
final WindowInsets result = new WindowInsets(this);
result.mWindowDecorInsets = new Rect(left, top, right, bottom);
- result.mWindowDecorInsetsConsumed = !hasWindowDecorInsets();
return result;
}
diff --git a/core/java/android/view/accessibility/CaptioningManager.java b/core/java/android/view/accessibility/CaptioningManager.java
index 334ff43..382a266 100644
--- a/core/java/android/view/accessibility/CaptioningManager.java
+++ b/core/java/android/view/accessibility/CaptioningManager.java
@@ -330,15 +330,30 @@
*/
public final String mRawTypeface;
+ private final boolean mHasForegroundColor;
+ private final boolean mHasBackgroundColor;
+ private final boolean mHasEdgeType;
+ private final boolean mHasEdgeColor;
+ private final boolean mHasWindowColor;
+
+ /** Lazily-created typeface based on the raw typeface string. */
private Typeface mParsedTypeface;
private CaptionStyle(int foregroundColor, int backgroundColor, int edgeType, int edgeColor,
int windowColor, String rawTypeface) {
- this.foregroundColor = foregroundColor;
- this.backgroundColor = backgroundColor;
- this.edgeType = edgeType;
- this.edgeColor = edgeColor;
- this.windowColor = windowColor;
+ mHasForegroundColor = foregroundColor != COLOR_UNSPECIFIED;
+ mHasBackgroundColor = backgroundColor != COLOR_UNSPECIFIED;
+ mHasEdgeType = edgeType != EDGE_TYPE_UNSPECIFIED;
+ mHasEdgeColor = edgeColor != COLOR_UNSPECIFIED;
+ mHasWindowColor = windowColor != COLOR_UNSPECIFIED;
+
+ // Always use valid colors, even when no override is specified, to
+ // ensure backwards compatibility with apps targeting KitKat MR2.
+ this.foregroundColor = mHasForegroundColor ? foregroundColor : Color.WHITE;
+ this.backgroundColor = mHasBackgroundColor ? backgroundColor : Color.BLACK;
+ this.edgeType = mHasEdgeType ? edgeType : EDGE_TYPE_NONE;
+ this.edgeColor = mHasEdgeColor ? edgeColor : Color.BLACK;
+ this.windowColor = mHasWindowColor ? windowColor : COLOR_NONE_OPAQUE;
mRawTypeface = rawTypeface;
}
@@ -375,7 +390,7 @@
* otherwise
*/
public boolean hasBackgroundColor() {
- return backgroundColor != COLOR_UNSPECIFIED;
+ return mHasBackgroundColor;
}
/**
@@ -384,7 +399,7 @@
* otherwise
*/
public boolean hasForegroundColor() {
- return foregroundColor != COLOR_UNSPECIFIED;
+ return mHasForegroundColor;
}
/**
@@ -393,7 +408,7 @@
* otherwise
*/
public boolean hasEdgeType() {
- return edgeType != EDGE_TYPE_UNSPECIFIED;
+ return mHasEdgeType;
}
/**
@@ -402,7 +417,7 @@
* otherwise
*/
public boolean hasEdgeColor() {
- return edgeColor != COLOR_UNSPECIFIED;
+ return mHasEdgeColor;
}
/**
@@ -411,7 +426,7 @@
* otherwise
*/
public boolean hasWindowColor() {
- return windowColor != COLOR_UNSPECIFIED;
+ return mHasWindowColor;
}
/**
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3859e48..af53ec8 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3323,7 +3323,8 @@
if (mLastY == Integer.MIN_VALUE) {
rawDeltaY -= mMotionCorrection;
}
- if (dispatchNestedPreScroll(0, -rawDeltaY, mScrollConsumed, mScrollOffset)) {
+ if (dispatchNestedPreScroll(0, mLastY != Integer.MIN_VALUE ? mLastY - y : -rawDeltaY,
+ mScrollConsumed, mScrollOffset)) {
rawDeltaY += mScrollConsumed[1];
scrollOffsetCorrection = -mScrollOffset[1];
scrollConsumedCorrection = mScrollConsumed[1];
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 5ef7658..3e4eb02 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -502,11 +502,6 @@
mValidationCallback.onValidationChanged(valid);
}
}
-
- @Override
- public void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
- mDelegator.dispatchThawSelfOnly(container);
- }
}
/**
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index d2f68d0..0460282 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -137,6 +137,11 @@
private boolean mIs24HourMode;
private boolean mShowHours;
+
+ /**
+ * When in 24-hour mode, indicates that the current hour is between
+ * 1 and 12 (inclusive).
+ */
private boolean mIsOnInnerCircle;
private int mXCenter;
@@ -513,49 +518,53 @@
mListener = listener;
}
+ /**
+ * Sets the current hour in 24-hour time.
+ *
+ * @param hour the current hour between 0 and 23 (inclusive)
+ */
public void setCurrentHour(int hour) {
final int degrees = (hour % 12) * DEGREES_FOR_ONE_HOUR;
mSelectionDegrees[HOURS] = degrees;
mSelectionDegrees[HOURS_INNER] = degrees;
- mAmOrPm = ((hour % 24) < 12) ? AM : PM;
+
+ // 0 is 12 AM (midnight) and 12 is 12 PM (noon).
+ mAmOrPm = (hour == 0 || (hour % 24) < 12) ? AM : PM;
+
if (mIs24HourMode) {
- mIsOnInnerCircle = (mAmOrPm == AM);
+ // Inner circle is 1 through 12.
+ mIsOnInnerCircle = hour >= 1 && hour <= 12;
} else {
mIsOnInnerCircle = false;
}
+
initData();
updateLayoutData();
invalidate();
}
- // Return hours in 0-23 range
+ /**
+ * Returns the current hour in 24-hour time.
+ *
+ * @return the current hour between 0 and 23 (inclusive)
+ */
public int getCurrentHour() {
- int hours =
- mSelectionDegrees[mIsOnInnerCircle ? HOURS_INNER : HOURS] / DEGREES_FOR_ONE_HOUR;
+ int hour = (mSelectionDegrees[mIsOnInnerCircle ?
+ HOURS_INNER : HOURS] / DEGREES_FOR_ONE_HOUR) % 12;
if (mIs24HourMode) {
- if (mIsOnInnerCircle) {
- hours = hours % 12;
- if (hours == 0) {
- hours = 12;
- }
- } else {
- if (hours != 0) {
- hours += 12;
- }
+ // Convert the 12-hour value into 24-hour time based on where the
+ // selector is positioned.
+ if (mIsOnInnerCircle && hour == 0) {
+ // Inner circle is 1 through 12.
+ hour = 12;
+ } else if (hour != 0) {
+ // Outer circle is 13 through 23 and 0.
+ hour += 12;
}
- } else {
- hours = hours % 12;
- if (hours == 0) {
- if (mAmOrPm == PM) {
- hours = 12;
- }
- } else {
- if (mAmOrPm == PM) {
- hours += 12;
- }
- }
+ } else if (mAmOrPm == PM) {
+ hour += 12;
}
- return hours;
+ return hour;
}
public void setCurrentMinute(int minute) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1509e80..a80d70a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8182,6 +8182,11 @@
* @removed
*/
public static ColorStateList getTextColors(Context context, TypedArray attrs) {
+ if (attrs == null) {
+ // Preserve behavior prior to removal of this API.
+ throw new NullPointerException();
+ }
+
// It's not safe to use this method from apps. The parameter 'attrs'
// must have been obtained using the TextView filter array which is not
// available to the SDK. As such, we grab a default TypedArray with the
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_large.xml b/core/res/res/drawable/vector_drawable_progress_bar_large.xml
index 3bf3cd7..023f5cc 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_large.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_large.xml
@@ -28,7 +28,7 @@
android:fillColor="#00000000"
android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
android:strokeColor="?attr/colorControlActivated"
- android:strokeLineCap="round"
+ android:strokeLineCap="square"
android:strokeLineJoin="miter"
android:strokeWidth="4"
android:trimPathEnd="0"
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
index 62f9225..e72097e 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
@@ -28,7 +28,7 @@
android:fillColor="#00000000"
android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
android:strokeColor="?attr/colorControlActivated"
- android:strokeLineCap="round"
+ android:strokeLineCap="square"
android:strokeLineJoin="miter"
android:strokeWidth="4"
android:trimPathEnd="0"
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_small.xml b/core/res/res/drawable/vector_drawable_progress_bar_small.xml
index 1352cfc..875e7a3 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_small.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_small.xml
@@ -28,7 +28,7 @@
android:fillColor="#00000000"
android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
android:strokeColor="?attr/colorControlActivated"
- android:strokeLineCap="round"
+ android:strokeLineCap="square"
android:strokeLineJoin="miter"
android:strokeWidth="4"
android:trimPathEnd="0"
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 5a7e168..cbaf54f 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -148,10 +148,10 @@
<color name="battery_saver_mode_color">#fff4511e</color><!-- deep orange 600 -->
<!-- Default user icon colors -->
- <color name="user_icon_1">#ffe91e63</color><!-- pink 500 -->
+ <color name="user_icon_1">#ff00bcd4</color><!-- teal 500 -->
<color name="user_icon_2">#ff3f51b5</color><!-- indigo 500 -->
<color name="user_icon_3">#ff4285f4</color><!-- blue 500 -->
- <color name="user_icon_4">#ff00bcd4</color><!-- teal 500 -->
+ <color name="user_icon_4">#ffe91e63</color><!-- pink 500 -->
<color name="user_icon_5">#ff0f9d58</color><!-- green 500 -->
<color name="user_icon_6">#ff8bc34a</color><!-- light green 500 -->
<color name="user_icon_7">#ffff9800</color><!-- orange 500 -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 855c63a..e95d735 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -612,13 +612,13 @@
<!-- Vibrator pattern for feedback when selecting an hour/minute tick of a Clock -->
<integer-array name="config_clockTickVibePattern">
<item>125</item>
- <item>5</item>
+ <item>30</item>
</integer-array>
<!-- Vibrator pattern for feedback when selecting a day/month/year date of a Calendar -->
<integer-array name="config_calendarDateVibePattern">
<item>125</item>
- <item>5</item>
+ <item>30</item>
</integer-array>
<!-- Vibrator pattern for feedback about booting with safe mode disabled -->
diff --git a/docs/html/distribute/googleplay/edu/start.jd b/docs/html/distribute/googleplay/edu/start.jd
index 067a15f..3c3a175 100644
--- a/docs/html/distribute/googleplay/edu/start.jd
+++ b/docs/html/distribute/googleplay/edu/start.jd
@@ -9,27 +9,23 @@
<div id="qv-wrapper"><div id="qv">
<h2>Steps to Join</h2>
<ol>
-<li><a href="#register">Register for a Publisher Account</li>
+<li><a href="#register">Register for a Publisher Account</a></li>
<li><a href="#prepare">Prepare Your Apps</a></li>
<li><a href="#publish">Publish Your Apps</a></li>
<li><a href="#related-resources">Related Resources</a></li>
</ol>
</div></div>
<p>
- If you've got great Android apps for education and want to reach even more teachers
+ If you've got great Android and Chrome apps for education and want to reach even more teachers
and students, you can join the <strong>Google Play for Education</strong>
program in a few simple steps. You do everything using the familiar tools and
- processes in Google Play.
+ processes you currently use to manage your Android or Chrome apps.
</p>
<p>
Note that Google Play for Education is currently available to <strong>K-12 schools in the United
States</strong> only.</p>
-<p>If you have an educational Chrome app instead of an Android app, you can learn more about
-Chrome Apps in Google Play for Education at <a href=
-"https://developers.google.com/edu">developers.google.com/edu</a>.
-</p>
<img src="{@docRoot}images/gpfe-start-0.jpg" style=
"border:1px solid #ddd;padding:0px" width="760" height="403">
@@ -43,23 +39,20 @@
</div>
<p>
- If you’re new to Google Play, review the information on <a href=
+ To publish an Android app in Google Play for Education, review the information on <a href=
"{@docRoot}distribute/googleplay/start.html">getting started</a> with
publishing on Google Play. You’ll gain access to the <a href=
"{@docRoot}distribute/googleplay/developer-console.html">Developer
Console</a>, where you’ll manage your details, apps, and payments.
</p>
+<p>To publish a Chrome app in Google Play for Education, you'll need a
+<a href="https://developer.chrome.com/webstore/publish">Chrome Web Store account</a>.</p>
+
<div class="headerLine">
<h2 id="prepare">
Prepare Your Apps
</h2>
-
-
-</div>
-
-<div class="figure-right">
- <img src="{@docRoot}images/gp-edu-process.png">
</div>
<p>
@@ -77,13 +70,13 @@
To prepare for a launch on Google Play for Education, start by reviewing the
guidelines for educational apps in Google Play and the policies that apply to
your apps. See the <a href=
- "{@docRoot}distribute/essentials/gpfe-guidelines.html">Education
+ "https://developers.google.com/edu/guidelines">Education
Guidelines</a> for details.
</p>
<p>
Also, make sure that you're familiar with the policies that your app must
- comply with, including <a href=
+ comply with. For Android, they include <a href=
"http://play.google.com/about/developer-content-policy.html">content
policies</a>, the <a href=
"http://play.google.com/about/developer-distribution-agreement.html">Developer
@@ -91,6 +84,14 @@
"https://play.google.com/about/developer-distribution-agreement-addendum.html">
Google Play for Education Addendum</a>.
</p>
+<p>For Chrome, they include <a href=
+ "https://developer.chrome.com/webstore/program_policies">content
+ policies</a>, the <a href=
+ "https://developer.chrome.com/webstore/terms">Developer
+ Distribution Agreement</a>, and <a href=
+ "https://developers.google.com/edu/chrome-d-d-a-addendum">
+ Google Play for Education Addendum</a>.
+</p>
<h3>
Design and develop a great app for education
@@ -105,7 +106,7 @@
<p>
Assess your app against the criteria listed in the <a href=
- "{@docRoot}distribute/essentials/gpfe-guidelines.html">Education
+ "https://developers.google.com/edu/guidelines">Education
Guidelines</a> and plan on supporting them to the greatest extent possible.
In some cases you might need to modify the app’s features or UI to support
classroom requirements. It's a good idea to identify any changes early in
@@ -113,7 +114,7 @@
</p>
<p>
- With Google Play for Education, optimizing your apps for tablets is crucial.
+ With Google Play for Education, optimizing your Android apps for tablets is crucial.
A variety of resources are available to help you understand what you need to
do — a good starting point is the <a href=
"{@docRoot}distribute/essentials/quality/tablets.html">Tablet App Quality</a>
@@ -132,6 +133,11 @@
</p>
<p>
+For Chrome apps, optimizing your apps for mouse, keyboard, and touch is just as important.
+Some students will use touch as their primary input method, and some will have Chromebooks
+without touch. Make sure your app works for all students.
+</p>
+<p>
Comprehensive testing and quality assurance are key aspects of delivering
great apps for teachers and students. Make sure you set up a <a href=
"{@docRoot}distribute/essentials/gpfe-guidelines.html#test-environment">proper
@@ -160,15 +166,13 @@
apps.
</li>
- <li>Publish your apps in the Developer Console as normal, but opt-in to
+ <li>Publish your apps in the Developer Console (for Android apps) or the Chrome Web Store
+ (for Chrome apps) as normal, but opt-in to
Google Play for Education.
</li>
</ul>
-<h3 id="opt-in">
- Opt-in to Google Play for Education and publish
-</h3>
-
+<h3>Opt-in to Google Play for Education and publish Android apps</h3>
<p>
Once you've built your release-ready APK upload it to the Developer Console,
create your store listing, and set distribution options. If you aren't
@@ -231,7 +235,7 @@
will be shown to educators when they are browsing your app.
</li>
- <li>Click <strong>Save</strong>f to save your Pricing and Distribution
+ <li>Click <strong>Save</strong> to save your Pricing and Distribution
changes.
</li>
</ol>
@@ -247,58 +251,55 @@
</div>
<p>
- Once you save changes and publish your app, the app will be submitted to our
- third-party educator network for review and approval. If the app is already
- published, it will be submitted for review as soon as you opt-in and save
- your changes.
+ Once you save changes and publish your app, the app will be available on Google Play for
+ Education. We may submit your app to our
+ third-party educator network for additional review and to be featured and badged as
+ Educator-approved.
</p>
<p class="note">
<strong>Note</strong>: Google Play for Education is part of Google Play. When
- you publish an app that's opted-in to Google Play for Education, the app
- becomes available to users in Google Play right away. After the app is
- <a href="{@docRoot}distribute/essentials/gpfe-guidelines.html#e-value">review
- and approval</a>, it then becomes available to educators in Google Play for
- Education.
+ you publish an Android app that's opted-in to Google Play for Education, the app
+ becomes available to users in Google Play.
</p>
-<h3>
- Track your review and approval
-</h3>
-
+<h3>Opt-in to Google Play for Education and publish Chrome apps</h3>
<p>
- As soon as you opt-in to Google Play for Education and publish, your apps are
- queued for review by our third-party educator network. The review and
- approval process can take four weeks or more. You'll receive notification by
- email (to your developer account address) when the review is complete, with a
- summary of the review results.
+Once you've uploaded your app or extension to the Developer Dashboard, create your store listing
+and set distribution options.
</p>
-
<p>
- At any time, you can check the review and approval status of your app in the
- Developer Console, under "Google Play for Education" in the app's Pricing and
- Distribution page. There are three approval states:
+When your apps are ready to publish, you opt-in to Google Play for Education directly from the
+<a href="https://chrome.google.com/webstore/developer/dashboard">Developer Dashboard</a>.
+Opt-in means that you want your apps to be made available to educators
+through Google Play for Education, including review, classification, and approval by our
+third-party educator network.
+</p>
+<p>
+Opt-in also confirms that your app complies with
+<a href="https://developer.chrome.com/webstore/program_policies">Chrome Web Store Program Policies</a>
+and the <a href="https://developer.chrome.com/webstore/terms">Terms of Service</a>, including a
+<a href="https://developers.google.com/edu/chrome-d-d-a-addendum">Google Play for Education Addendum</a>.
+If you are not familiar with these
+policy documents or the Addendum, make sure to read them before opting-in.
</p>
-<ul>
- <li>
- <em>Pending</em> — Your app was sent for review and the review isn't
- yet complete.
- </li>
+<p>Here's how to opt-in to Google Play for Education for a specific app or extension:</p>
+<ol>
+<li>In the Developer Dashboard, click <b>Edit</b> on the app you want to opt-in.</li>
+<li>Scroll down to <b>Google Play for Education</b> and review the additional
+Developer Terms and Conditions.</li>
+<li>Select the checkbox to opt-in to include this item in Google Play for Education.</li>
+<li>Click <b>Publish Changes</b> to save your changes and submit your application.
+Once you save changes and publish your app, the app will be available on Google Play for Education.
+We may submit your app to our third-party educator network for additional review, and to be
+featured and badged as Educator-approved.</li>
+<p class="note"><b>Note:</b>
+When you publish an app that's opted-in to Google Play for Education,
+the app becomes available to users in the Chrome Web Store.
+</p>
+</ol>
- <li>
- <em>Approved</em> — Your app was reviewed and approved. The app will
- be made available directly to educators through Google Play for Education.
- Once your app is approved, you can update it at your convenience without
- needing another full review.
- </li>
-
- <li>
- <em>Not approved</em> — Your app was reviewed and not approved. Check
- the notification email send for information about why the app wasn’t
- approved. You can address any issues and opt-in again for another review.
- </li>
-</ul>
<div class="headerLine">
<h2 id="related-resources">Related Resources</h2>
</div>
diff --git a/docs/html/google/play/billing/billing_subscriptions.jd b/docs/html/google/play/billing/billing_subscriptions.jd
index 2b78ab3..b9b77df 100644
--- a/docs/html/google/play/billing/billing_subscriptions.jd
+++ b/docs/html/google/play/billing/billing_subscriptions.jd
@@ -11,25 +11,32 @@
<div id="qv">
<h2>Quickview</h2>
<ul>
- <li>Users purchase your subscriptions from inside your apps, rather than
-directly from Google Play.</li>
- <li>Subscriptions let you sell products with automated, recurring billing
-(monthly or annual).</li>
- <li>You can offer a configurable trial period for any subscription.</li>
-
+ <li>Subscriptions let you sell products with automated, recurring billing
+ at a variety of intervals.</li>
+ <li>You can offer a configurable trial period for monthly and
+ annual subscriptions.</li>
+ <li>You can manage subscriptions through the Developer Console, or by using
+ the
+ <a href="https://developers.google.com/android-publisher/">Google Play
+ Developer API</a>.</li>
+ <li>Users purchase your subscriptions from inside your apps, rather than
+ directly from Google Play.</li>
+ <li>You can defer billing for a particular user's subscription, to manage
+ accounts or offer rewards.</li>
</ul>
<h2>In this document</h2>
<ol>
- <li><a href="#overview">Overview</a></li>
+ <li><a href="#overview">Overview of Subscriptions</a></li>
<li><a href="#administering">Configuring Subscriptions Items</a></li>
- <li><a href="#cancellation">Cancellation</a></li>
- <li><a href="#payment">Payment Processing</a></li>
+ <li><a href="#cancellation">Subscription Cancellation</a></li>
+ <li><a href="#payment">Payment Processing and Policies</a></li>
<li><a href="#strategies">Purchase Verification Strategies</a></li>
+ <li><a href="#play-dev-api">Google Play Developer API</a></li>
</ol>
<h2>See also</h2>
<ol>
<li><a href="{@docRoot}google/play/billing/billing_integrate.html#Subs">Implementing Subscriptions (V3)</a></li>
- <li><a href="https://developers.google.com/android-publisher/v1_1/">Google Play Android Developer API</a></li>
+ <li><a href="https://developers.google.com/android-publisher/">Google Play Developer API</a></li>
</ol>
</div>
</div>
@@ -44,14 +51,15 @@
<h2 id="overview">Overview of Subscriptions</h2>
<p>A <em>subscription</em> is a product type offered in In-app Billing that
lets you sell content, services, or features to users from inside your app with
-recurring monthly or annual billing. You can sell subscriptions to almost any
+recurring, automated billing at the interval you specify. You can sell subscriptions to almost
+any
type of digital content, from any type of app or game.</p>
<p>As with other in-app products, you configure and publish subscriptions using
the Developer Console and then sell them from inside apps installed on
Android devices. In the Developer console, you create subscription
products and add them to a product list, then set a price and optional trial
-period for each, choose a billing interval (monthly or annual), and then
+period for each, choose a billing interval, and then
publish. For more information about using the Developer Console, see
<a href="#administering">Configuring Subscription Items</a>.</p>
@@ -63,17 +71,17 @@
<img src="{@docRoot}images/in-app-billing/v3/billing_subscription_v3.png" style="float:right; border:4px solid ddd;">
-<p>After users have purchase subscriptions, they can view the subscriptions and
+<p>After users have purchased subscriptions, they can view the subscriptions and
cancel them from the <strong>My Apps</strong> screen in the Play Store app or
from the app's product details page in the Play Store app. For more information
about handling user cancellations, see <a href="#cancellation">Subscription Cancellation</a>.</p>
-<p>In adddition to client-side API calls, you can use the server-side API for
+<p>In addition to client-side API calls, you can use the server-side API for
In-app Billing to provide subscription purchasers with extended access to
content (for example, from your web site or another service).
The server-side API lets you validate the status of a subscription when users
sign into your other services. For more information about the API, see <a
-href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Purchase Status API</a>. </p>
+href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google Play Developer API</a>. </p>
<p>You can also build on your existing external subscriber base from inside your
Android apps.</p>
@@ -102,8 +110,10 @@
subscriptions, see the <a href="{@docRoot}google/play/billing/versions.html#Subs">Version Notes</a>.</p>
<h2 id="administering">Configuring Subscription Items</h2>
-<p>To create and manage subscriptions, use the Developer Console to set up a
-product list for the app then configure these attributes for each subscription
+
+<p>To create and manage subscriptions, you can use the Developer Console to set
+up a
+product list for the app, then configure these attributes for each subscription
product:</p>
<ul>
@@ -113,8 +123,8 @@
<li>Language: The default language for displaying the subscription</li>
<li>Title: The title of the subscription product</li>
<li>Description: Details that tell the user about the subscription</li>
-<li>Price: USD price of subscription per recurrence</li>
-<li>Recurrence: monthly or yearly</li>
+<li>Price: Default price of subscription per recurrence</li>
+<li>Recurrence: Interval of billing recurrence</li>
<li>Additional currency pricing (can be auto-filled)</li>
</ul>
@@ -122,6 +132,10 @@
see <a href="{@docRoot}google/play/billing/billing_admin.html">Administering
In-app Billing</a>.</p>
+<p>You can also create and manage subscriptions using the
+<a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">
+Google Play Developer API</a>.</p>
+
<h3 id="pricing">Subscription pricing</h3>
<p>When you create a subscription in the Developer Console, you can set a price
@@ -139,20 +153,30 @@
<h3 id="user-billing">User billing</h3>
<p>In the Developer Console, you can configure subscription products with
-automated recurring billing at either of two intervals:</p>
+automated recurring billing at your choice of intervals:</p>
<ul>
<li>Monthly — Google Play bills the customer’s Google Wallet account at
the time of purchase and monthly subsequent to the purchase date (exact billing
- intervals can vary slightly over time)</li>
+ intervals can vary slightly over time).</li>
<li>Annually — Google Play bills the customer's Google Wallet account at
the time of purchase and again on the same date in subsequent years.</li>
+
+ <li>Seasonal — Google Play bills the customer's Google Wallet account at
+ the beginning of each "season" (you specify the season beginning and end
+ dates). This
+ is intended for annual purchases of seasonal content (such as sports-related
+ content). The subscription runs through the end of the season, and restarts
+ the next year at the start of the season.</li>
+
</ul>
<p>Billing continues indefinitely at the interval and price specified for the
subscription. At each subscription renewal, Google Play charges the user account
-automatically, then notifies the user of the charges afterward by email. Billing
-cycles will always match subscription cycles, based on the purchase date.</p>
+automatically, then notifies the user of the charges afterward by email. For
+monthly and annual subscriptions, billing cycles will always match subscription
+cycles, based on the purchase date. (Seasonal subscriptions are charged
+annually, on the first day of the season.)</p>
<p>Over the life of a subscription, the form of payment billed remains the same
— Google Play always bills the same form of payment (such as credit card
@@ -164,7 +188,7 @@
API. Your apps can store the token locally or pass it to your backend servers,
which can then use it to validate or cancel the subscription remotely using the
<a
-href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Purchase Status API</a>.</p>
+href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google Play Developer API</a>.</p>
<p>If a recurring payment fails (for example, because the customer’s credit
card has become invalid), the subscription does not renew. How your app is
@@ -182,18 +206,57 @@
billing errors that may occur. Your backend servers can use the server-side API
to query and update your records and follow up with customers directly, if needed.</p>
+<h3 id="deferred-billing">Deferred Billing</h3>
+
+<p>Using the
+<a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google
+Play Developer API</a>, you can defer the next billing date for a
+subscriber. The user continues to be subscribed to the content, and has full
+access to it, but is not charged during the deferral period. This allows you
+to do things like:</p>
+
+<ul>
+ <li>Give users free access as part of a bundle or a special offer (for
+ example, giving free access to web content to users who subscribe to a
+ print magazine)</li>
+ <li>Give free access to customers as a goodwill gesture</li>
+</ul>
+
+<p>The longest you can defer billing is for one year per call. Of course, you
+can call the API again before the year is up to defer billing further.</p>
+
+<p>For example, Darcy has a monthly subscription to online content for the
+<em>Fishing Gentleman</em> app. He is normally
+billed £1.25 on the first of each month.
+On March 10, he participates in an online survey for the app publisher. The
+publisher rewards him by deferring his next payment until June 1. Darcy is not
+charged on April 1 or May 1, but still has access to the content as normal. On
+June 1, he is charged his normal £1.25 subscription fee.</p>
+
+<p class="note"><strong>Note:</strong> The API always defers the billing date
+by a whole number of days. If you request a deferral period that includes a
+fractional number of days, the API rounds the period up to the next full day.
+For example, if a user's subscription is set to renew on 15 June 2015 at
+14:00:00 UTC, and you use the API to defer the renewal date to 15 August 2015 at
+02:00:00 UTC, the API will round up to the next full day and set the renewal
+date to 15 August 2015 14:00:00 UTC.</p>
+
+<p>You can also offer free trials to new subscribers, as described in
+<a href="#trials">Free trials</a>.</p>
+
<h3 id="trials">Free trials</h3>
<p>In the Developer Console, you can set up a free trial period that lets users
try your subscription content before buying it. The trial period runs for the
period of time that you set and then automatically converts to a full
subscription managed according to the subscription's billing interval and
-price.</p>
+price. Free trials are supported for monthly and annual subscriptions only, and are not supported for seasonal subscriptions.</p>
<p>To take advantage of a free trial, a user must "purchase" the full
subscription through the standard In-app Billing flow, providing a valid form of
payment to use for billing and completing the normal purchase transaction.
-However, the user is not charged any money, since the initial period corresponds
+However, the user is not charged any money, because the initial period
+corresponds
to the free trial. Instead, Google Play records a transaction of $0.00 and the
subscription is marked as purchased for the duration of the trial period or
until cancellation. When the transaction is complete, Google Play notifies users
@@ -220,8 +283,10 @@
period per subscription product.</p>
<h3 id="publishing">Subscription publishing</h3>
+
<p>When you have finished configuring your subscription product details in the
-Developer Console, you can publish the subscription in the app product list.</p>
+Developer Console or via the API,
+you can publish the subscription in the app product list.</p>
<p>In the product list, you can add subscriptions, in-app products, or both. You
can add multiple subscriptions that give access to different content or
@@ -263,10 +328,13 @@
<p class="caution"><strong>Important:</strong> In all cases, you must continue
to offer the content that your subscribers have purchased through their
-subscriptions, for as long any users are able to access it. That is, you must
-not remove any subscriber’s content while any user still has an active
+subscriptions, as long any user is able to access it. That is, you must
+not remove any content while any user still has an active
subscription to it, even if that subscription will terminate at the end of the
-current billing cycle. Removing content that a subscriber is entitled to access
+current billing cycle. Alternatively, you can use the <a href="#refunds">refund
+and revoke</a> API to revoke each subscriber's subscription (one by one) and
+refund their subscription payments.
+Removing content that any subscriber is entitled to access
will result in penalties. Please see the <a
href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=140504">policies document</a> for more information. </p>
@@ -280,19 +348,26 @@
screen of the Play Store app. If the user chooses to cancel the uninstallation,
the app and subscriptions remain as they were.</p>
-<h3 id="refunds">Refunds</h3>
+<h3 id="refunds">Refunding and revoking subscriptions</h3>
-<p>With subscriptions, Google Play does not provide a refund window, so users
-will need to contact you directly to request a refund.
+<p>With subscriptions, Google Play does not provide a refund window, so users
+will need to request a refund. They can request a refund from the <strong>My
+Orders</strong> page in the Play Store, or by contacting you directly.</p>
-<p>If you receive requests for refunds, you can use the server-side API to
-cancel the subscription or verify that it is already cancelled. However, keep in
-mind that Google Play considers cancelled subscriptions valid until the end of
-their current billing cycles, so even if you grant a refund and cancel the
-subscription, the user will still have access to the content.
+<p>If you receive requests for refunds, you can use the
+<a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google Play
+Developer API</a> or the Merchant Center to cancel the subscription, verify that it
+is already cancelled, or refund the user's payment without cancelling it. You
+can also use the
+<a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google
+Play Developer API</a> to <em>refund and revoke</em> a
+user's subscription. If you refund and revoke a subscription, the user's
+subscription is immediately cancelled, and the user's most recent subscription
+payment is refunded. (If you want to refund more than the most recent payment,
+you can process additional refunds through the Merchant Center.)</p>
-<p class="caution"><strong>Important:</strong> Partial refunds for canceled
-subscriptions are not available at this time.</p>
+<p class="caution"><strong>Important:</strong> Partial refunds are not available
+at this time.</p>
<h2 id="payment">Payment Processing and Policies</h2>
@@ -317,9 +392,9 @@
each recurring transaction by appending an integer as follows: </p>
<p><span style="color:#777"><code style="color:#777">12999556515565155651.5565135565155651</code> (base order number)</span><br />
-<code>12999556515565155651.5565135565155651..0</code> (initial purchase orderID)<br />
-<code>12999556515565155651.5565135565155651..1</code> (first recurrence orderID)<br />
-<code>12999556515565155651.5565135565155651..2</code> (second recurrence orderID)<br />
+<code>12999556515565155651.5565135565155651..0</code> (first recurrence orderID)<br />
+<code>12999556515565155651.5565135565155651..1</code> (second recurrence orderID)<br />
+<code>12999556515565155651.5565135565155651..2</code> (third recurrence orderID)<br />
...<br /></p>
<p>Google Play provides the order number as the value of the
@@ -334,19 +409,28 @@
<p>To verify a purchase, the app passes the purchase token and other details up
to your backend servers, which verifies them directly with Google Play using the
-Purchase Status API. If the backend server determines that the purchase is
+Google Play Developer API. If the backend server determines that the purchase is
valid, it notifies the app and grants access to the content.</p>
<p>Keep in mind that users will want the ability to use your app at any time,
including when there may be no network connection available. Make sure that your
approach to purchase verification accounts for the offline use-case.</p>
-<h2 id="play-dev-api">Google Play Android Developer API</h2>
+<h2 id="play-dev-api">Google Play Developer API</h2>
-<p>Google Play offers an HTTP-based API that lets you remotely query the
-validity of a specific subscription at any time or cancel a subscription. The
-API is designed to be used from your backend servers as a way of securely
+<p>Google Play offers an HTTP-based API that lets you perform such tasks as:</p>
+ <ul>
+ <li>Remotely query the validity of a specific subscription at any time</li>
+ <li>Cancel a subscription</li>
+ <li>Defer a subscription's next billing date</li>
+ <li>Refund a subscription payment without cancelling the subscription</li>
+ <li>Refund and revoke a subscription</li>
+ </ul>
+
+<p>The API is designed to be used from your backend servers as a way of securely
managing subscriptions, as well as extending and integrating subscriptions with
other services.</p>
-<p>For complete information, see <a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Purchase Status API</a>.</p>
+<p>For complete information, see
+<a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google Play
+Developer API</a>.</p>
diff --git a/docs/html/google/play/billing/index.jd b/docs/html/google/play/billing/index.jd
index 875271fe..e1326d7 100644
--- a/docs/html/google/play/billing/index.jd
+++ b/docs/html/google/play/billing/index.jd
@@ -14,14 +14,27 @@
<div class="sidebox">
<h2><strong>New in In-App Billing</strong></h2>
<ul>
+ <li><strong>Seasonal subscriptions</strong>—You can now set up a
+ recurring <a href="billing_subscriptions.html#user-billing">seasonal
+ subscription</a> that starts and ends on the same date each year (for
+ example, a sports subscription that starts every September 1 and ends every
+ April 10).</li>
+ <li><strong>Deferred subscription billing</strong>—You can
+ <a href="billing_subscriptions.html#deferred-billing">defer</a> a
+ subscriber's next billing date until the date you choose. The user still has
+ access to the content but is not charged during the deferral period.</li>
<li><strong>Google Play Developer API</strong>—The
<a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google
Play Developer API</a> allows you to perform a number of publishing and
app-management tasks. It includes the functionality previously known as the
<em>Purchase Status API.</em> </li>
+ <li><strong>Refund/Revoke subscription</strong>—You can use the
+ Google Play Developer API to <a href="billing_subscriptions.html#refunds">refund
+ and revoke</a> a user's subscription. If you do this, the user's
+ subscription ends
+ immediately, and his or her most recent subscription payment is
+ refunded.</li>
<li><strong>In-app Billing Version 3</strong>—The <a href="{@docRoot}google/play/billing/api.html">latest version</a> of In-app Billing features a synchronous API that is easier to implement and lets you manage in-app products and subscriptions more effectively.</li>
- <li><strong>Subscriptions now supported in Version 3</strong>—You can query and launch purchase flows for subscription items using the V3 API.</li>
- <li><strong>Free trials</strong>—You can now offer users a configurable <a href="/google/play/billing/v2/billing_subscriptions.html#trials">free trial period</a> for your in-app subscriptions. You can set up trials with a simple change in the Developer Console—no change to your app code is needed.</li>
</ul>
</div>
</div>
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index a8b8b16..06353c0 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -327,8 +327,10 @@
// None
"",
// Matrix
+ " fragColor.rgb /= (fragColor.a + 0.0019);\n" // un-premultiply
" fragColor *= colorMatrix;\n"
- " fragColor += colorMatrixVector;\n",
+ " fragColor += colorMatrixVector;\n"
+ " fragColor.rgb *= (fragColor.a + 0.0019);\n", // re-premultiply
// PorterDuff
" fragColor = blendColors(colorBlend, fragColor);\n"
};
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index 6d4bb4a..3681637 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -70,6 +70,7 @@
// TODO: move to a method on android:Paint
static inline bool paintWillNotDraw(const SkPaint& paint) {
return paint.getAlpha() == 0
+ && !paint.getColorFilter()
&& getXfermode(paint.getXfermode()) != SkXfermode::kClear_Mode;
}
@@ -77,6 +78,7 @@
static inline bool paintWillNotDrawText(const SkPaint& paint) {
return paint.getAlpha() == 0
&& paint.getLooper() == NULL
+ && !paint.getColorFilter()
&& getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode;
}
// ----------------------------------------------------------------------------
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 40d45ea..71a05ab 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -1351,6 +1351,16 @@
streamType = AudioSystem.STREAM_NOTIFICATION;
}
+ // If Hdmi-CEC system audio mode is on, show volume bar
+ // only when TV receives volume notification from Audio Receiver.
+ if (mHdmiTvClient != null && streamType == AudioSystem.STREAM_MUSIC) {
+ synchronized (mHdmiTvClient) {
+ if (mHdmiSystemAudioSupported &&
+ ((flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) == 0)) {
+ flags &= ~AudioManager.FLAG_SHOW_UI;
+ }
+ }
+ }
mVolumeController.postVolumeChanged(streamType, flags);
if ((flags & AudioManager.FLAG_FIXED_VOLUME) == 0) {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b94a258..3c2a776 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -199,7 +199,7 @@
android:excludeFromRecents="true"
android:stateNotNeeded="true"
android:resumeWhilePausing="true"
- android:theme="@style/RecentsTheme">
+ android:theme="@style/config_recents_activity_theme">
<intent-filter>
<action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
</intent-filter>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index a8c95c1..9654da9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -35,6 +35,10 @@
<!-- The number of app icons we keep in memory -->
<integer name="config_recents_max_icon_count">20</integer>
+
+ <!-- The theme to use for RecentsActivity. -->
+ <item type="style" name="config_recents_activity_theme">@style/RecentsTheme.Wallpaper</item>
+
<!-- Control whether status bar should distinguish HSPA data icon form UMTS
data icon on devices -->
<bool name="config_hspa_data_distinguishable">false</bool>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 0456c82..46e7587 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -20,14 +20,9 @@
<item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item>
</style>
- <!-- Alternate Recents theme -->
<style name="RecentsTheme" parent="@android:style/Theme">
<!-- NoTitle -->
<item name="android:windowNoTitle">true</item>
- <!-- Wallpaper -->
- <item name="android:windowBackground">@color/transparent</item>
- <item name="android:colorBackgroundCacheHint">@null</item>
- <item name="android:windowShowWallpaper">true</item>
<!-- Misc -->
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
@@ -36,6 +31,20 @@
<item name="android:ambientShadowAlpha">0.35</item>
</style>
+
+ <!-- Alternate Recents theme -->
+ <style name="RecentsTheme.Wallpaper">
+ <!-- Wallpaper -->
+ <item name="android:windowBackground">@color/transparent</item>
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ <item name="android:windowShowWallpaper">true</item>
+ </style>
+
+ <!-- Performance optimized alternate Recents theme (no wallpaper) -->
+ <style name="RecentsTheme.NoWallpaper">
+ <item name="android:windowBackground">@android:color/black</item>
+ </style>
+
<!-- Animations for a non-full-screen window or activity. -->
<style name="Animation.RecentsActivity" parent="@android:style/Animation.Activity">
<item name="android:activityOpenEnterAnimation">@anim/recents_launch_from_launcher_enter</item>
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 55d8c09..dfec307 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1925,6 +1925,15 @@
}
break;
}
+ case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_SET_EXPLICITLY_SELECTED from unknown NetworkAgent");
+ break;
+ }
+ nai.networkMisc.explicitlySelected = true;
+ break;
+ }
case NetworkMonitor.EVENT_NETWORK_TESTED: {
NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
if (isLiveNetworkAgent(nai, "EVENT_NETWORK_VALIDATED")) {
@@ -1935,6 +1944,11 @@
rematchNetworkAndRequests(nai);
}
updateInetCondition(nai, valid);
+ // Let the NetworkAgent know the state of its network
+ nai.asyncChannel.sendMessage(
+ android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS,
+ (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
+ 0, null);
}
break;
}
@@ -2517,6 +2531,7 @@
nai = mNetworkForNetId.get(network.netId);
}
if (nai == null) return;
+ if (DBG) log("reportBadNetwork(" + nai.name() + ") by " + uid);
synchronized (nai) {
if (isNetworkBlocked(nai, uid)) return;
@@ -4244,7 +4259,7 @@
NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,
- networkMisc);
+ new NetworkMisc(networkMisc));
synchronized (this) {
nai.networkMonitor.systemReady = mSystemReady;
}
@@ -4546,8 +4561,10 @@
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
if (newNetwork == currentNetwork) {
- if (DBG) log("Network " + newNetwork.name() + " was already satisfying" +
- " request " + nri.request.requestId + ". No change.");
+ if (DBG) {
+ log("Network " + newNetwork.name() + " was already satisfying" +
+ " request " + nri.request.requestId + ". No change.");
+ }
keep = true;
continue;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 95d33e7..6d8e105 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3360,7 +3360,7 @@
intent.setComponent(new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name));
mStackSupervisor.startActivityLocked(null, intent, null, ri.activityInfo,
- null, null, null, null, 0, 0, 0, null, 0, null, false, null, null,
+ null, null, null, null, 0, 0, 0, null, 0, 0, 0, null, false, null, null,
null);
}
}
@@ -3759,8 +3759,8 @@
final long origId = Binder.clearCallingIdentity();
int res = mStackSupervisor.startActivityLocked(r.app.thread, intent,
r.resolvedType, aInfo, null, null, resultTo != null ? resultTo.appToken : null,
- resultWho, requestCode, -1, r.launchedFromUid, r.launchedFromPackage, 0,
- options, false, null, null, null);
+ resultWho, requestCode, -1, r.launchedFromUid, r.launchedFromPackage,
+ -1, r.launchedFromUid, 0, options, false, null, null, null);
Binder.restoreCallingIdentity(origId);
r.finishing = wasFinishing;
@@ -8482,7 +8482,7 @@
void moveTaskToFrontLocked(int taskId, int flags, Bundle options) {
if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
- Binder.getCallingUid(), "Task to front")) {
+ Binder.getCallingUid(), -1, -1, "Task to front")) {
ActivityOptions.abort(options);
return;
}
@@ -8524,7 +8524,7 @@
ActivityStack stack = tr.stack;
if (stack.mResumedActivity != null && stack.mResumedActivity.task == tr) {
if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
- Binder.getCallingUid(), "Task to back")) {
+ Binder.getCallingUid(), -1, -1, "Task to back")) {
return;
}
}
@@ -8576,7 +8576,7 @@
synchronized(this) {
if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
- Binder.getCallingUid(), "Task backwards")) {
+ Binder.getCallingUid(), -1, -1, "Task backwards")) {
return;
}
final long origId = Binder.clearCallingIdentity();
@@ -10120,20 +10120,31 @@
}
}
- boolean checkAppSwitchAllowedLocked(int callingPid, int callingUid,
- String name) {
+ boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
+ int callingPid, int callingUid, String name) {
if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
return true;
}
- final int perm = checkComponentPermission(
- android.Manifest.permission.STOP_APP_SWITCHES, callingPid,
- callingUid, -1, true);
+ int perm = checkComponentPermission(
+ android.Manifest.permission.STOP_APP_SWITCHES, sourcePid,
+ sourceUid, -1, true);
if (perm == PackageManager.PERMISSION_GRANTED) {
return true;
}
-
- Slog.w(TAG, name + " request from " + callingUid + " stopped");
+
+ // If the actual IPC caller is different from the logical source, then
+ // also see if they are allowed to control app switches.
+ if (callingUid != -1 && callingUid != sourceUid) {
+ perm = checkComponentPermission(
+ android.Manifest.permission.STOP_APP_SWITCHES, callingPid,
+ callingUid, -1, true);
+ if (perm == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ }
+
+ Slog.w(TAG, name + " request from " + sourceUid + " stopped");
return false;
}
@@ -18944,13 +18955,7 @@
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
if (tr.getRootActivity() != null) {
- long origId = Binder.clearCallingIdentity();
- try {
- moveTaskToFrontLocked(tr.taskId, 0, null);
- return;
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ moveTaskToFrontLocked(tr.taskId, 0, null);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index e224ac0..a75fc5a 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -550,10 +550,10 @@
if (DEBUG_TASKS) Slog.d(TAG, "Comparing existing cls="
+ taskIntent.getComponent().flattenToShortString()
- + "/aff=" + r.task.affinity + " to new cls="
+ + "/aff=" + r.task.rootAffinity + " to new cls="
+ intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
- if (!isDocument && !taskIsDocument && task.affinity != null) {
- if (task.affinity.equals(target.taskAffinity)) {
+ if (!isDocument && !taskIsDocument && task.rootAffinity != null) {
+ if (task.rootAffinity.equals(target.taskAffinity)) {
if (DEBUG_TASKS) Slog.d(TAG, "Found matching affinity!");
return r;
}
@@ -2949,7 +2949,7 @@
int res = mStackSupervisor.startActivityLocked(srec.app.thread, destIntent,
null, aInfo, null, null, parent.appToken, null,
0, -1, parent.launchedFromUid, parent.launchedFromPackage,
- 0, null, true, null, null, null);
+ -1, parent.launchedFromUid, 0, null, true, null, null, null);
foundParentInTask = res == ActivityManager.START_SUCCESS;
} catch (RemoteException e) {
foundParentInTask = false;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 7b6a202..4940493 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -785,8 +785,8 @@
void startHomeActivity(Intent intent, ActivityInfo aInfo) {
moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE);
- startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null, 0,
- null, false, null, null, null);
+ startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null,
+ 0, 0, 0, null, false, null, null, null);
}
final int startActivityMayWait(IApplicationThread caller, int callingUid,
@@ -810,12 +810,14 @@
ActivityContainer container = (ActivityContainer)iContainer;
synchronized (mService) {
+ final int realCallingPid = Binder.getCallingPid();
+ final int realCallingUid = Binder.getCallingUid();
int callingPid;
if (callingUid >= 0) {
callingPid = -1;
} else if (caller == null) {
- callingPid = Binder.getCallingPid();
- callingUid = Binder.getCallingUid();
+ callingPid = realCallingPid;
+ callingUid = realCallingUid;
} else {
callingPid = callingUid = -1;
}
@@ -841,11 +843,11 @@
if (mService.mHeavyWeightProcess != null &&
(mService.mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
!mService.mHeavyWeightProcess.processName.equals(aInfo.processName))) {
- int realCallingUid = callingUid;
+ int appCallingUid = callingUid;
if (caller != null) {
ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
if (callerApp != null) {
- realCallingUid = callerApp.info.uid;
+ appCallingUid = callerApp.info.uid;
} else {
Slog.w(TAG, "Unable to find app for caller " + caller
+ " (pid=" + callingPid + ") when starting: "
@@ -857,7 +859,7 @@
IIntentSender target = mService.getIntentSenderLocked(
ActivityManager.INTENT_SENDER_ACTIVITY, "android",
- realCallingUid, userId, null, null, 0, new Intent[] { intent },
+ appCallingUid, userId, null, null, 0, new Intent[] { intent },
new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
| PendingIntent.FLAG_ONE_SHOT, null);
@@ -903,7 +905,8 @@
int res = startActivityLocked(caller, intent, resolvedType, aInfo,
voiceSession, voiceInteractor, resultTo, resultWho,
- requestCode, callingPid, callingUid, callingPackage, startFlags, options,
+ requestCode, callingPid, callingUid, callingPackage,
+ realCallingPid, realCallingUid, startFlags, options,
componentSpecified, null, container, inTask);
Binder.restoreCallingIdentity(origId);
@@ -1017,7 +1020,8 @@
theseOptions = null;
}
int res = startActivityLocked(caller, intent, resolvedTypes[i],
- aInfo, null, null, resultTo, null, -1, callingPid, callingUid, callingPackage,
+ aInfo, null, null, resultTo, null, -1, callingPid, callingUid,
+ callingPackage, callingPid, callingUid,
0, theseOptions, componentSpecified, outActivity, null, null);
if (res < 0) {
return res;
@@ -1248,7 +1252,8 @@
Intent intent, String resolvedType, ActivityInfo aInfo,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode,
- int callingPid, int callingUid, String callingPackage, int startFlags, Bundle options,
+ int callingPid, int callingUid, String callingPackage,
+ int realCallingPid, int realCallingUid, int startFlags, Bundle options,
boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container,
TaskRecord inTask) {
int err = ActivityManager.START_SUCCESS;
@@ -1293,8 +1298,7 @@
final int launchFlags = intent.getFlags();
- if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
- && sourceRecord != null) {
+ if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
// Transfer the result target from the source activity to the new
// one being started, including any failures.
if (requestCode >= 0) {
@@ -1344,8 +1348,8 @@
if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
&& sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
try {
- if (!AppGlobals.getPackageManager().activitySupportsIntent(intent.getComponent(),
- intent, resolvedType)) {
+ if (!AppGlobals.getPackageManager().activitySupportsIntent(
+ intent.getComponent(), intent, resolvedType)) {
err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
}
} catch (RemoteException e) {
@@ -1439,7 +1443,8 @@
final ActivityStack stack = getFocusedStack();
if (voiceSession == null && (stack.mResumedActivity == null
|| stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
- if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
+ if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
+ realCallingPid, realCallingUid, "Activity start")) {
PendingActivityLaunch pal =
new PendingActivityLaunch(r, sourceRecord, startFlags, stack);
mPendingActivityLaunches.add(pal);
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index b21af48..df1772a 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -51,6 +51,12 @@
* task being launched a chance to load its resources without this occupying IO bandwidth. */
private static final long PRE_TASK_DELAY_MS = 3000;
+ /** The maximum number of entries to keep in the queue before draining it automatically. */
+ private static final int MAX_WRITE_QUEUE_LENGTH = 6;
+
+ /** Special value for mWriteTime to mean don't wait, just write */
+ private static final long FLUSH_QUEUE = -1;
+
private static final String RECENTS_FILENAME = "_task";
private static final String TASKS_DIRNAME = "recent_tasks";
private static final String TASK_EXTENSION = ".xml";
@@ -120,6 +126,31 @@
mLazyTaskWriterThread.start();
}
+ private void removeThumbnails(TaskRecord task) {
+ final String taskString = Integer.toString(task.taskId);
+ for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
+ final WriteQueueItem item = mWriteQueue.get(queueNdx);
+ if (item instanceof ImageWriteQueueItem &&
+ ((ImageWriteQueueItem) item).mFilename.startsWith(taskString)) {
+ if (DEBUG) Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilename +
+ " from write queue");
+ mWriteQueue.remove(queueNdx);
+ }
+ }
+ }
+
+ private void yieldIfQueueTooDeep() {
+ boolean stall = false;
+ synchronized (this) {
+ if (mNextWriteTime == FLUSH_QUEUE) {
+ stall = true;
+ }
+ }
+ if (stall) {
+ Thread.yield();
+ }
+ }
+
void wakeup(TaskRecord task, boolean flush) {
synchronized (this) {
if (task != null) {
@@ -128,6 +159,10 @@
final WriteQueueItem item = mWriteQueue.get(queueNdx);
if (item instanceof TaskWriteQueueItem &&
((TaskWriteQueueItem) item).mTask == task) {
+ if (!task.inRecents) {
+ // This task is being removed.
+ removeThumbnails(task);
+ }
break;
}
}
@@ -138,15 +173,18 @@
// Dummy.
mWriteQueue.add(new WriteQueueItem());
}
- if (flush) {
- mNextWriteTime = -1;
+ if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
+ mNextWriteTime = FLUSH_QUEUE;
} else if (mNextWriteTime == 0) {
mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
}
if (DEBUG) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " mNextWriteTime="
- + mNextWriteTime + " Callers=" + Debug.getCallers(4));
+ + mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size()
+ + " Callers=" + Debug.getCallers(4));
notifyAll();
}
+
+ yieldIfQueueTooDeep();
}
void saveImage(Bitmap image, String filename) {
@@ -166,7 +204,9 @@
if (queueNdx < 0) {
mWriteQueue.add(new ImageWriteQueueItem(filename, image));
}
- if (mNextWriteTime == 0) {
+ if (mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
+ mNextWriteTime = FLUSH_QUEUE;
+ } else if (mNextWriteTime == 0) {
mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
}
if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename + " now=" +
@@ -174,6 +214,8 @@
mNextWriteTime + " Callers=" + Debug.getCallers(4));
notifyAll();
}
+
+ yieldIfQueueTooDeep();
}
Bitmap getThumbnail(String filename) {
@@ -425,7 +467,7 @@
// If mNextWriteTime, then don't delay between each call to saveToXml().
final WriteQueueItem item;
synchronized (TaskPersister.this) {
- if (mNextWriteTime >= 0) {
+ if (mNextWriteTime != FLUSH_QUEUE) {
// The next write we don't have to wait so long.
mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
if (DEBUG) Slog.d(TAG, "Next write time may be in " +
@@ -439,13 +481,14 @@
TaskPersister.this.wait();
} catch (InterruptedException e) {
}
- // Invariant: mNextWriteTime is either -1 or PRE_WRITE_DELAY_MS from now.
+ // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
+ // from now.
}
item = mWriteQueue.remove(0);
long now = SystemClock.uptimeMillis();
if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
- mNextWriteTime);
+ mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
while (now < mNextWriteTime) {
try {
if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
@@ -484,7 +527,7 @@
TaskRecord task = ((TaskWriteQueueItem) item).mTask;
if (DEBUG) Slog.d(TAG, "Writing task=" + task);
synchronized (mService) {
- if (mService.mRecentTasks.contains(task)) {
+ if (task.inRecents) {
// Still there.
try {
if (DEBUG) Slog.d(TAG, "Saving task=" + task);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 071db97..4de7367 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -58,6 +58,7 @@
private static final String ATTR_ORIGACTIVITY = "orig_activity";
private static final String TAG_ACTIVITY = "activity";
private static final String ATTR_AFFINITY = "affinity";
+ private static final String ATTR_ROOT_AFFINITY = "root_affinity";
private static final String ATTR_ROOTHASRESET = "root_has_reset";
private static final String ATTR_AUTOREMOVERECENTS = "auto_remove_recents";
private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
@@ -84,7 +85,8 @@
static final boolean IGNORE_RETURN_TO_RECENTS = true;
final int taskId; // Unique identifier for this task.
- String affinity; // The affinity name for this task, or null.
+ String affinity; // The affinity name for this task, or null; may change identity.
+ String rootAffinity; // Initial base affinity, or null; does not change from initial root.
final IVoiceInteractionSession voiceSession; // Voice interaction session driving task
final IVoiceInteractor voiceInteractor; // Associated interactor to provide to app
Intent intent; // The original intent that started the task.
@@ -208,9 +210,9 @@
}
TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
- String _affinity, ComponentName _realActivity, ComponentName _origActivity,
- boolean _rootWasReset, boolean _autoRemoveRecents, boolean _askedCompatMode,
- int _taskType, int _userId, int _effectiveUid,
+ String _affinity, String _rootAffinity, ComponentName _realActivity,
+ ComponentName _origActivity, boolean _rootWasReset, boolean _autoRemoveRecents,
+ boolean _askedCompatMode, int _taskType, int _userId, int _effectiveUid,
String _lastDescription, ArrayList<ActivityRecord> activities, long _firstActiveTime,
long _lastActiveTime, long lastTimeMoved, boolean neverRelinquishIdentity,
ActivityManager.TaskDescription _lastTaskDescription, int taskAffiliation,
@@ -224,6 +226,7 @@
intent = _intent;
affinityIntent = _affinityIntent;
affinity = _affinity;
+ rootAffinity = _affinity;
voiceSession = null;
voiceInteractor = null;
realActivity = _realActivity;
@@ -279,6 +282,12 @@
}
affinity = info.taskAffinity;
+ if (intent == null) {
+ // If this task already has an intent associated with it, don't set the root
+ // affinity -- we don't want it changing after initially set, but the initially
+ // set value may be null.
+ rootAffinity = affinity;
+ }
effectiveUid = info.applicationInfo.uid;
stringName = null;
@@ -840,8 +849,17 @@
if (origActivity != null) {
out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
}
+ // Write affinity, and root affinity if it is different from affinity.
+ // We use the special string "@" for a null root affinity, so we can identify
+ // later whether we were given a root affinity or should just make it the
+ // same as the affinity.
if (affinity != null) {
out.attribute(null, ATTR_AFFINITY, affinity);
+ if (!affinity.equals(rootAffinity)) {
+ out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
+ }
+ } else if (rootAffinity != null) {
+ out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
}
out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
@@ -901,6 +919,8 @@
ComponentName realActivity = null;
ComponentName origActivity = null;
String affinity = null;
+ String rootAffinity = null;
+ boolean hasRootAffinity = false;
boolean rootHasReset = false;
boolean autoRemoveRecents = false;
boolean askedCompatMode = false;
@@ -935,6 +955,9 @@
origActivity = ComponentName.unflattenFromString(attrValue);
} else if (ATTR_AFFINITY.equals(attrName)) {
affinity = attrValue;
+ } else if (ATTR_ROOT_AFFINITY.equals(attrName)) {
+ rootAffinity = attrValue;
+ hasRootAffinity = true;
} else if (ATTR_ROOTHASRESET.equals(attrName)) {
rootHasReset = Boolean.valueOf(attrValue);
} else if (ATTR_AUTOREMOVERECENTS.equals(attrName)) {
@@ -1007,6 +1030,12 @@
createLastTaskDescriptionIconFilename(taskId, lastActiveTime)));
}
+ if (!hasRootAffinity) {
+ rootAffinity = affinity;
+ } else if ("@".equals(rootAffinity)) {
+ rootAffinity = null;
+ }
+
if (effectiveUid <= 0) {
Intent checkIntent = intent != null ? intent : affinityIntent;
effectiveUid = 0;
@@ -1028,7 +1057,7 @@
}
final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
- affinityIntent, affinity, realActivity, origActivity, rootHasReset,
+ affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
@@ -1047,8 +1076,13 @@
pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid);
pw.print(" mCallingPackage="); pw.println(mCallingPackage);
- if (affinity != null) {
- pw.print(prefix); pw.print("affinity="); pw.println(affinity);
+ if (affinity != null || rootAffinity != null) {
+ pw.print(prefix); pw.print("affinity="); pw.print(affinity);
+ if (affinity == null || !affinity.equals(rootAffinity)) {
+ pw.print(" root="); pw.println(rootAffinity);
+ } else {
+ pw.println();
+ }
}
if (voiceSession != null || voiceInteractor != null) {
pw.print(prefix); pw.print("VOICE: session=0x");
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 957d705..15ffc0d 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -53,6 +53,9 @@
// Penalty applied to scores of Networks that have not been validated.
private static final int UNVALIDATED_SCORE_PENALTY = 40;
+ // Score for explicitly connected network.
+ private static final int EXPLICITLY_SELECTED_NETWORK_SCORE = 100;
+
// The list of NetworkRequests being satisfied by this Network.
public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>();
@@ -95,9 +98,10 @@
int score = currentScore;
if (!validated) score -= UNVALIDATED_SCORE_PENALTY;
-
if (score < 0) score = 0;
+ if (networkMisc.explicitlySelected) score = EXPLICITLY_SELECTED_NETWORK_SCORE;
+
return score;
}
@@ -110,7 +114,8 @@
network + "} lp{" +
linkProperties + "} nc{" +
networkCapabilities + "} Score{" + getCurrentScore() + "} " +
- "validated{" + validated + "} created{" + created + "} }";
+ "validated{" + validated + "} created{" + created + "} " +
+ "explicitlySelected{" + networkMisc.explicitlySelected + "} }";
}
public String name() {
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 9e33205..fab064c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -260,7 +260,7 @@
addState(mUserPromptedState, mDefaultState);
addState(mCaptivePortalState, mDefaultState);
addState(mLingeringState, mDefaultState);
- setInitialState(mOfflineState);
+ setInitialState(mDefaultState);
mServer = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.CAPTIVE_PORTAL_SERVER);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 9a34047..4d9b4e9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -798,7 +798,7 @@
// FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
// volume change notification back to hdmi control service.
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
- AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
+ AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
}
}
diff --git a/services/core/java/com/android/server/hdmi/MhlConstants.java b/services/core/java/com/android/server/hdmi/MhlConstants.java
deleted file mode 100644
index fe479f3..0000000
--- a/services/core/java/com/android/server/hdmi/MhlConstants.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.android.server.hdmi;
-
-/**
- * Defines constants related to MHL protocol internal implementation.
- */
-final class MhlConstants {
- // --------------------------------------------------
- // MHL sub command message types.
- static final int MSG_MSGE = 0x02;
- static final int MSG_RCP = 0x10;
- static final int MSG_RCPK = 0x11;
- static final int MSG_RCPE = 0x12;
- static final int MSG_RAP = 0x20;
- static final int MSG_RAPK = 0x21;
-
- // MHL RAP messages.
- static final int RAP_ACTION_POLL = 0x00;
- static final int RAP_ACTION_CONTENT_ON = 0x10;
- static final int RAP_ACTION_CONTENT_OFF = 0x11;
-
- // MHL RAPK messages.
- static final int RAPK_NO_ERROR = 0x00;
- static final int RAPK_UNRECOGNIZED_ACTION = 0x01;
- static final int RAPK_UNSUPPORTED_ACTION = 0x02;
- static final int RAPK_RESPONDER_BUSY = 0x03;
-
- static final int INVALID_ADOPTER_ID = -1;
- static final int INVALID_DEVICE_ID = -1;
-
- static final int CBUS_MODE_OCBUS = 1;
- static final int CBUS_MODE_ECBUS_S = 2;
- static final int CBUS_MODE_ECBUS_D = 3;
-
- // MHL RCPE messages
- static final int RCPE_NO_ERROR = 0x00;
- static final int RCPE_INEFFECTIVE_KEYCODE = 0x01;
- static final int RCPE_RESPONDER_BUSY = 0x02;
-
- private MhlConstants() { /* cannot be instantiated */ }
-}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d87fc2e..79c9955 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4311,7 +4311,7 @@
* grantPermissions will assume the package update is trying to
* expand its permissions.
*/
- grantPermissionsLPw(pkg, true);
+ grantPermissionsLPw(pkg, true, pkg.packageName);
mSettings.disableSystemPackageLPw(pkg.packageName);
}
}
@@ -6696,17 +6696,19 @@
if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
for (PackageParser.Package pkg : mPackages.values()) {
if (pkg != pkgInfo) {
- grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0);
+ grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0,
+ changingPkg);
}
}
}
if (pkgInfo != null) {
- grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0);
+ grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg);
}
}
- private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace) {
+ private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace,
+ String packageOfInterest) {
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null) {
return;
@@ -6740,8 +6742,10 @@
}
if (bp == null || bp.packageSetting == null) {
- Slog.w(TAG, "Unknown permission " + name
- + " in package " + pkg.packageName);
+ if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
+ Slog.w(TAG, "Unknown permission " + name
+ + " in package " + pkg.packageName);
+ }
continue;
}
@@ -6806,9 +6810,11 @@
gp.gids = appendInts(gp.gids, bp.gids);
}
} else {
- Slog.w(TAG, "Not granting permission " + perm
- + " to package " + pkg.packageName
- + " because it was previously installed without");
+ if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
+ Slog.w(TAG, "Not granting permission " + perm
+ + " to package " + pkg.packageName
+ + " because it was previously installed without");
+ }
}
} else {
if (gp.grantedPermissions.remove(perm)) {
@@ -6822,11 +6828,13 @@
} else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) {
// Don't print warning for app op permissions, since it is fine for them
// not to be granted, there is a UI for the user to decide.
- Slog.w(TAG, "Not granting permission " + perm
- + " to package " + pkg.packageName
- + " (protectionLevel=" + bp.protectionLevel
- + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
- + ")");
+ if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
+ Slog.w(TAG, "Not granting permission " + perm
+ + " to package " + pkg.packageName
+ + " (protectionLevel=" + bp.protectionLevel
+ + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+ + ")");
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d4bcd5c..0c51160 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -328,6 +328,7 @@
for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
AppWindowToken wtoken = tokens.get(tokenNdx);
if (wtoken.mDeferRemoval) {
+ stack.mExitingAppTokens.remove(wtoken);
wtoken.mDeferRemoval = false;
mService.removeAppFromTaskLocked(wtoken);
}
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index d1e150f..9b350c1 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -43,6 +43,7 @@
Collections.unmodifiableList(mChildConnections);
private PhoneAccountHandle mPhoneAccount;
+ private AudioState mAudioState;
private int mState = Connection.STATE_NEW;
private DisconnectCause mDisconnectCause;
private int mCapabilities;
@@ -94,6 +95,15 @@
}
/**
+ * @return The audio state of the conference, describing how its audio is currently
+ * being routed by the system. This is {@code null} if this Conference
+ * does not directly know about its audio state.
+ */
+ public final AudioState getAudioState() {
+ return mAudioState;
+ }
+
+ /**
* Invoked when the Conference and all it's {@link Connection}s should be disconnected.
*/
public void onDisconnect() {}
@@ -128,6 +138,25 @@
public void onSwap() {}
/**
+ * Notifies this conference of a request to play a DTMF tone.
+ *
+ * @param c A DTMF character.
+ */
+ public void onPlayDtmfTone(char c) {}
+
+ /**
+ * Notifies this conference of a request to stop any currently playing DTMF tones.
+ */
+ public void onStopDtmfTone() {}
+
+ /**
+ * Notifies this conference that the {@link #getAudioState()} property has a new value.
+ *
+ * @param state The new call audio state.
+ */
+ public void onAudioStateChanged(AudioState state) {}
+
+ /**
* Sets state to be on hold.
*/
public final void setOnHold() {
@@ -251,6 +280,18 @@
return this;
}
+ /**
+ * Inform this Conference that the state of its audio output has been changed externally.
+ *
+ * @param state The new audio state.
+ * @hide
+ */
+ final void setAudioState(AudioState state) {
+ Log.d(this, "setAudioState %s", state);
+ mAudioState = state;
+ onAudioStateChanged(state);
+ }
+
private void setState(int newState) {
if (newState != Connection.STATE_ACTIVE &&
newState != Connection.STATE_HOLDING &&
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 76348ec..7979e44 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -771,7 +771,7 @@
public final void setDisconnected(DisconnectCause disconnectCause) {
mDisconnectCause = disconnectCause;
setState(STATE_DISCONNECTED);
- Log.d(this, "Disconnected with cause %d message %s", disconnectCause);
+ Log.d(this, "Disconnected with cause %s", disconnectCause);
for (Listener l : mListeners) {
l.onDisconnected(this, disconnectCause);
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 0da0adb..3e18bac 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -400,7 +400,7 @@
@Override
public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
String id = mIdByConnection.get(c);
- Log.d(this, "Adapter set disconnected %d %s", disconnectCause);
+ Log.d(this, "Adapter set disconnected %s", disconnectCause);
mAdapter.setDisconnected(id, disconnectCause);
}
@@ -607,17 +607,29 @@
private void onAudioStateChanged(String callId, AudioState audioState) {
Log.d(this, "onAudioStateChanged %s %s", callId, audioState);
- findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState);
+ } else {
+ findConferenceForAction(callId, "onAudioStateChanged").setAudioState(audioState);
+ }
}
private void playDtmfTone(String callId, char digit) {
Log.d(this, "playDtmfTone %s %c", callId, digit);
- findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
+ } else {
+ findConferenceForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
+ }
}
private void stopDtmfTone(String callId) {
Log.d(this, "stopDtmfTone %s", callId);
- findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone();
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone();
+ } else {
+ findConferenceForAction(callId, "stopDtmfTone").onStopDtmfTone();
+ }
}
private void conference(String callId1, String callId2) {
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 0c233eb..4b059b2 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -91,17 +91,6 @@
public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 0x10;
/**
- * Flag indicating that this {@code PhoneAccount} is always enabled and cannot be disabled by
- * the user.
- * This capability is reserved for important {@code PhoneAccount}s such as the emergency calling
- * only {@code PhoneAccount}.
- * <p>
- * See {@link #getCapabilities}
- * @hide
- */
- public static final int CAPABILITY_ALWAYS_ENABLED = 0x20;
-
- /**
* URI scheme for telephone number URIs.
*/
public static final String SCHEME_TEL = "tel";
@@ -124,7 +113,6 @@
private final CharSequence mLabel;
private final CharSequence mShortDescription;
private final List<String> mSupportedUriSchemes;
- private final boolean mIsEnabled;
public static class Builder {
private PhoneAccountHandle mAccountHandle;
@@ -135,7 +123,6 @@
private CharSequence mLabel;
private CharSequence mShortDescription;
private List<String> mSupportedUriSchemes = new ArrayList<String>();
- private boolean mIsEnabled = false;
public Builder(PhoneAccountHandle accountHandle, CharSequence label) {
this.mAccountHandle = accountHandle;
@@ -157,7 +144,6 @@
mLabel = phoneAccount.getLabel();
mShortDescription = phoneAccount.getShortDescription();
mSupportedUriSchemes.addAll(phoneAccount.getSupportedUriSchemes());
- mIsEnabled = phoneAccount.isEnabled();
}
public Builder setAddress(Uri value) {
@@ -217,19 +203,6 @@
}
/**
- * Specifies whether the {@link PhoneAccount} is enabled or not. {@link PhoneAccount}s are
- * by default not enabled.
- *
- * @param value {@code True} if the {@link PhoneAccount} is enabled.
- * @return The Builder.
- * @hide
- */
- public Builder setEnabled(boolean value) {
- this.mIsEnabled = value;
- return this;
- }
-
- /**
* Creates an instance of a {@link PhoneAccount} based on the current builder settings.
*
* @return The {@link PhoneAccount}.
@@ -248,8 +221,7 @@
mIconResId,
mLabel,
mShortDescription,
- mSupportedUriSchemes,
- mIsEnabled);
+ mSupportedUriSchemes);
}
}
@@ -261,8 +233,7 @@
int iconResId,
CharSequence label,
CharSequence shortDescription,
- List<String> supportedUriSchemes,
- boolean enabled) {
+ List<String> supportedUriSchemes) {
mAccountHandle = account;
mAddress = address;
mSubscriptionAddress = subscriptionAddress;
@@ -271,7 +242,6 @@
mLabel = label;
mShortDescription = shortDescription;
mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
- mIsEnabled = enabled;
}
public static Builder builder(
@@ -392,15 +362,6 @@
}
/**
- * Determines whether this {@code PhoneAccount} is enabled.
- *
- * @return {@code True} if this {@code PhoneAccount} is enabled..
- */
- public boolean isEnabled() {
- return mIsEnabled;
- }
-
- /**
* The icon resource ID for the icon of this {@code PhoneAccount}.
*
* @return A resource ID.
@@ -455,7 +416,6 @@
out.writeCharSequence(mLabel);
out.writeCharSequence(mShortDescription);
out.writeList(mSupportedUriSchemes);
- out.writeInt(mIsEnabled ? 1 : 0);
}
public static final Creator<PhoneAccount> CREATOR
@@ -485,6 +445,5 @@
List<String> supportedUriSchemes = new ArrayList<>();
in.readList(supportedUriSchemes, classLoader);
mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
- mIsEnabled = in.readInt() == 1;
}
}
diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
index 0cf84d0..f931bc5 100644
--- a/telecomm/java/android/telecom/RemoteConference.java
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -181,6 +181,27 @@
return mDisconnectCause;
}
+ public void playDtmfTone(char digit) {
+ try {
+ mConnectionService.playDtmfTone(mId, digit);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void stopDtmfTone() {
+ try {
+ mConnectionService.stopDtmfTone(mId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void setAudioState(AudioState state) {
+ try {
+ mConnectionService.onAudioStateChanged(mId, state);
+ } catch (RemoteException e) {
+ }
+ }
+
public final void registerCallback(Callback callback) {
mCallbacks.add(callback);
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 4d438ed..a91d92f 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -71,24 +71,6 @@
"android.telecom.action.CHANGE_PHONE_ACCOUNTS";
/**
- * The {@link android.content.Intent} action used to inform a
- * {@link android.telecom.ConnectionService} that one of its {@link PhoneAccount}s has been
- * enabled. The {@link TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE} extra is used to indicate
- * which {@link PhoneAccount} has been enabled.
- */
- public static final String ACTION_PHONE_ACCOUNT_ENABLED =
- "android.telecom.action.PHONE_ACCOUNT_ENABLED";
-
- /**
- * The {@link android.content.Intent} action used to inform a
- * {@link android.telecom.ConnectionService} that one of its {@link PhoneAccount}s has been
- * disabled. The {@link TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE} extra is used to indicate
- * which {@link PhoneAccount} has been disabled.
- */
- public static final String ACTION_PHONE_ACCOUNT_DISABLED =
- "android.telecom.action.PHONE_ACCOUNT_DISABLED";
-
- /**
* Optional extra for {@link android.content.Intent#ACTION_CALL} containing a boolean that
* determines whether the speakerphone should be automatically turned on for an outgoing call.
*/
@@ -325,14 +307,14 @@
/**
* Return the {@link PhoneAccount} which is the user-chosen default for making outgoing phone
* calls with a specified URI scheme. This {@code PhoneAccount} will always be a member of the
- * list which is returned from calling {@link #getEnabledPhoneAccounts()}.
+ * list which is returned from calling {@link #getCallCapablePhoneAccounts()}.
* <p>
* Apps must be prepared for this method to return {@code null}, indicating that there currently
* exists no user-chosen default {@code PhoneAccount}. In this case, apps wishing to initiate a
* phone call must either create their {@link android.content.Intent#ACTION_CALL} or
* {@link android.content.Intent#ACTION_DIAL} {@code Intent} with no
* {@link TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE}, or present the user with an affordance to
- * select one of the elements of {@link #getEnabledPhoneAccounts()}.
+ * select one of the elements of {@link #getCallCapablePhoneAccounts()}.
* <p>
* An {@link android.content.Intent#ACTION_CALL} or {@link android.content.Intent#ACTION_DIAL}
* {@code Intent} with no {@link TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE} is valid, and
@@ -355,7 +337,7 @@
/**
* Return the {@link PhoneAccount} which is the user-chosen default for making outgoing phone
* calls. This {@code PhoneAccount} will always be a member of the list which is returned from
- * calling {@link #getEnabledPhoneAccounts()}
+ * calling {@link #getCallCapablePhoneAccounts()}
*
* Apps must be prepared for this method to return {@code null}, indicating that there currently
* exists no user-chosen default {@code PhoneAccount}.
@@ -389,19 +371,19 @@
}
/**
- * Return a list of enabled {@link PhoneAccountHandle}s which can be used to make and receive
- * phone calls.
+ * Return a list of {@link PhoneAccountHandle}s which can be used to make and receive phone
+ * calls.
*
* @see #EXTRA_PHONE_ACCOUNT_HANDLE
* @return A list of {@code PhoneAccountHandle} objects.
*/
- public List<PhoneAccountHandle> getEnabledPhoneAccounts() {
+ public List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
try {
if (isServiceConnected()) {
- return getTelecomService().getEnabledPhoneAccounts();
+ return getTelecomService().getCallCapablePhoneAccounts();
}
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getEnabledPhoneAccounts", e);
+ Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts", e);
}
return new ArrayList<>();
}
@@ -467,8 +449,8 @@
}
/**
- * Returns a list of the enabled {@link PhoneAccountHandle}s which can be used to make and
- * receive phone calls which support the specified URI scheme.
+ * Returns a list of the {@link PhoneAccountHandle}s which can be used to make and receive phone
+ * calls which support the specified URI scheme.
* <P>
* For example, invoking with {@code "tel"} will find all {@link PhoneAccountHandle}s which
* support telephone calls (e.g. URIs such as {@code tel:555-555-1212}). Invoking with
@@ -490,13 +472,14 @@
}
/**
- * Determine whether the device has more than one account registered and enabled.
+ * Determine whether the device has more than one account registered that can make and receive
+ * phone calls.
*
- * @return {@code true} if the device has more than one account registered and enabled and
- * {@code false} otherwise.
+ * @return {@code true} if the device has more than one account registered and {@code false}
+ * otherwise.
*/
- public boolean hasMultipleEnabledAccounts() {
- return getEnabledPhoneAccounts().size() > 1;
+ public boolean hasMultipleCallCapableAccounts() {
+ return getCallCapablePhoneAccounts().size() > 1;
}
/**
@@ -518,9 +501,9 @@
}
/**
- * Returns a count of enabled and disabled {@link PhoneAccount}s.
+ * Returns a count of all {@link PhoneAccount}s.
*
- * @return The count of enabled and disabled {@link PhoneAccount}s.
+ * @return The count of {@link PhoneAccount}s.
* @hide
*/
@SystemApi
@@ -572,24 +555,6 @@
}
/**
- * Enables or disables a {@link PhoneAccount}.
- *
- * @param account The {@link PhoneAccountHandle} to enable or disable.
- * @param isEnabled {@code True} if the phone account should be enabled.
- * @hide
- */
- @SystemApi
- public void setPhoneAccountEnabled(PhoneAccountHandle account, boolean isEnabled) {
- try {
- if (isServiceConnected()) {
- getTelecomService().setPhoneAccountEnabled(account, isEnabled);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#setPhoneAccountEnabled", e);
- }
- }
-
- /**
* Register a {@link PhoneAccount} for use by the system.
*
* @param account The complete {@link PhoneAccount}.
@@ -797,9 +762,8 @@
/**
* Registers a new incoming call. A {@link ConnectionService} should invoke this method when it
* has an incoming call. The specified {@link PhoneAccountHandle} must have been registered
- * with {@link #registerPhoneAccount} and subsequently enabled by the user within the phone's
- * settings. Once invoked, this method will cause the system to bind to the
- * {@link ConnectionService} associated with the {@link PhoneAccountHandle} and request
+ * with {@link #registerPhoneAccount}. Once invoked, this method will cause the system to bind
+ * to the {@link ConnectionService} associated with the {@link PhoneAccountHandle} and request
* additional information about the call (See
* {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming call UI.
*
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 4875cc3..77a80fe 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -50,9 +50,9 @@
void setUserSelectedOutgoingPhoneAccount(in PhoneAccountHandle account);
/**
- * @see TelecomServiceImpl#getEnabledPhoneAccounts
+ * @see TelecomServiceImpl#getCallCapablePhoneAccounts
*/
- List<PhoneAccountHandle> getEnabledPhoneAccounts();
+ List<PhoneAccountHandle> getCallCapablePhoneAccounts();
/**
* @see TelecomManager#getPhoneAccountsSupportingScheme
@@ -95,11 +95,6 @@
List<PhoneAccountHandle> getSimCallManagers();
/**
- * @see TelecomServiceImpl#setPhoneAccountEnabled
- */
- void setPhoneAccountEnabled(in PhoneAccountHandle account, in boolean isEnabled);
-
- /**
* @see TelecomServiceImpl#registerPhoneAccount
*/
void registerPhoneAccount(in PhoneAccount metadata);
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 3fb6be7..6fe10dc 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -263,7 +263,7 @@
case OUTGOING_CANCELED:
return "OUTGOING_CANCELED";
default:
- return "INVALID";
+ return "INVALID: " + cause;
}
}
}