Merge "Avoid surfacing "Sign in to network" notification if user selects "use as is" from captive portal app." into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index a400fa6..231be10 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9630,6 +9630,7 @@
method public boolean equals(android.content.res.Configuration);
method public int getLayoutDirection();
method public boolean isLayoutSizeAtLeast(int);
+ method public boolean isScreenRound();
method public static boolean needNewResources(int, int);
method public void readFromParcel(android.os.Parcel);
method public void setLayoutDirection(java.util.Locale);
@@ -9672,6 +9673,10 @@
field public static final int SCREENLAYOUT_LONG_NO = 16; // 0x10
field public static final int SCREENLAYOUT_LONG_UNDEFINED = 0; // 0x0
field public static final int SCREENLAYOUT_LONG_YES = 32; // 0x20
+ field public static final int SCREENLAYOUT_ROUND_MASK = 768; // 0x300
+ field public static final int SCREENLAYOUT_ROUND_NO = 256; // 0x100
+ field public static final int SCREENLAYOUT_ROUND_UNDEFINED = 0; // 0x0
+ field public static final int SCREENLAYOUT_ROUND_YES = 512; // 0x200
field public static final int SCREENLAYOUT_SIZE_LARGE = 3; // 0x3
field public static final int SCREENLAYOUT_SIZE_MASK = 15; // 0xf
field public static final int SCREENLAYOUT_SIZE_NORMAL = 2; // 0x2
@@ -16462,18 +16467,21 @@
ctor public NotProvisionedException(java.lang.String);
}
- public final class PlaybackParams {
+ public final class PlaybackParams implements android.os.Parcelable {
ctor public PlaybackParams();
method public android.media.PlaybackParams allowDefaults();
+ method public int describeContents();
method public int getAudioFallbackMode();
method public float getPitch();
method public float getSpeed();
method public android.media.PlaybackParams setAudioFallbackMode(int);
method public android.media.PlaybackParams setPitch(float);
method public android.media.PlaybackParams setSpeed(float);
+ method public void writeToParcel(android.os.Parcel, int);
field public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0; // 0x0
field public static final int AUDIO_FALLBACK_MODE_FAIL = 2; // 0x2
field public static final int AUDIO_FALLBACK_MODE_MUTE = 1; // 0x1
+ field public static final android.os.Parcelable.Creator<android.media.PlaybackParams> CREATOR;
}
public final class Rating implements android.os.Parcelable {
@@ -23091,24 +23099,23 @@
public final class MessageQueue {
method public void addIdleHandler(android.os.MessageQueue.IdleHandler);
+ method public void addOnFileDescriptorEventListener(java.io.FileDescriptor, int, android.os.MessageQueue.OnFileDescriptorEventListener);
method public boolean isIdle();
- method public void registerFileDescriptorCallback(java.io.FileDescriptor, int, android.os.MessageQueue.FileDescriptorCallback);
method public void removeIdleHandler(android.os.MessageQueue.IdleHandler);
- method public void unregisterFileDescriptorCallback(java.io.FileDescriptor);
- }
-
- public static abstract class MessageQueue.FileDescriptorCallback {
- ctor public MessageQueue.FileDescriptorCallback();
- method public int onFileDescriptorEvents(java.io.FileDescriptor, int);
- field public static final int EVENT_ERROR = 4; // 0x4
- field public static final int EVENT_INPUT = 1; // 0x1
- field public static final int EVENT_OUTPUT = 2; // 0x2
+ method public void removeOnFileDescriptorEventListener(java.io.FileDescriptor);
}
public static abstract interface MessageQueue.IdleHandler {
method public abstract boolean queueIdle();
}
+ public static abstract interface MessageQueue.OnFileDescriptorEventListener {
+ method public abstract int onFileDescriptorEvents(java.io.FileDescriptor, int);
+ field public static final int EVENT_ERROR = 4; // 0x4
+ field public static final int EVENT_INPUT = 1; // 0x1
+ field public static final int EVENT_OUTPUT = 2; // 0x2
+ }
+
public final class Messenger implements android.os.Parcelable {
ctor public Messenger(android.os.Handler);
ctor public Messenger(android.os.IBinder);
@@ -28884,7 +28891,6 @@
field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction";
field public static final int START_SOURCE_ASSIST_GESTURE = 4; // 0x4
field public static final int START_WITH_ASSIST = 1; // 0x1
- field public static final int START_WITH_SCREENSHOT = 2; // 0x2
}
public abstract class VoiceInteractionSession implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback {
@@ -28910,7 +28916,6 @@
method public void onDestroy();
method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
method public void onHandleAssist(android.os.Bundle);
- method public void onHandleScreenshot(android.graphics.Bitmap);
method public void onHide();
method public boolean onKeyDown(int, android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
@@ -34533,6 +34538,7 @@
field public static final int DEFAULT_DISPLAY = 0; // 0x0
field public static final int FLAG_PRESENTATION = 8; // 0x8
field public static final int FLAG_PRIVATE = 4; // 0x4
+ field public static final int FLAG_ROUND = 16; // 0x10
field public static final int FLAG_SECURE = 2; // 0x2
field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
field public static final int STATE_DOZE = 3; // 0x3
diff --git a/api/system-current.txt b/api/system-current.txt
index 351ec8e..afd82e3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -111,6 +111,7 @@
field public static final java.lang.String INVOKE_CARRIER_SETUP = "android.permission.INVOKE_CARRIER_SETUP";
field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
field public static final java.lang.String KILL_UID = "android.permission.KILL_UID";
+ field public static final java.lang.String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS";
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO";
field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS";
@@ -9940,6 +9941,7 @@
method public boolean equals(android.content.res.Configuration);
method public int getLayoutDirection();
method public boolean isLayoutSizeAtLeast(int);
+ method public boolean isScreenRound();
method public static boolean needNewResources(int, int);
method public void readFromParcel(android.os.Parcel);
method public void setLayoutDirection(java.util.Locale);
@@ -9982,6 +9984,10 @@
field public static final int SCREENLAYOUT_LONG_NO = 16; // 0x10
field public static final int SCREENLAYOUT_LONG_UNDEFINED = 0; // 0x0
field public static final int SCREENLAYOUT_LONG_YES = 32; // 0x20
+ field public static final int SCREENLAYOUT_ROUND_MASK = 768; // 0x300
+ field public static final int SCREENLAYOUT_ROUND_NO = 256; // 0x100
+ field public static final int SCREENLAYOUT_ROUND_UNDEFINED = 0; // 0x0
+ field public static final int SCREENLAYOUT_ROUND_YES = 512; // 0x200
field public static final int SCREENLAYOUT_SIZE_LARGE = 3; // 0x3
field public static final int SCREENLAYOUT_SIZE_MASK = 15; // 0xf
field public static final int SCREENLAYOUT_SIZE_NORMAL = 2; // 0x2
@@ -17703,18 +17709,21 @@
ctor public NotProvisionedException(java.lang.String);
}
- public final class PlaybackParams {
+ public final class PlaybackParams implements android.os.Parcelable {
ctor public PlaybackParams();
method public android.media.PlaybackParams allowDefaults();
+ method public int describeContents();
method public int getAudioFallbackMode();
method public float getPitch();
method public float getSpeed();
method public android.media.PlaybackParams setAudioFallbackMode(int);
method public android.media.PlaybackParams setPitch(float);
method public android.media.PlaybackParams setSpeed(float);
+ method public void writeToParcel(android.os.Parcel, int);
field public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0; // 0x0
field public static final int AUDIO_FALLBACK_MODE_FAIL = 2; // 0x2
field public static final int AUDIO_FALLBACK_MODE_MUTE = 1; // 0x1
+ field public static final android.os.Parcelable.Creator<android.media.PlaybackParams> CREATOR;
}
public final class Rating implements android.os.Parcelable {
@@ -25005,24 +25014,23 @@
public final class MessageQueue {
method public void addIdleHandler(android.os.MessageQueue.IdleHandler);
+ method public void addOnFileDescriptorEventListener(java.io.FileDescriptor, int, android.os.MessageQueue.OnFileDescriptorEventListener);
method public boolean isIdle();
- method public void registerFileDescriptorCallback(java.io.FileDescriptor, int, android.os.MessageQueue.FileDescriptorCallback);
method public void removeIdleHandler(android.os.MessageQueue.IdleHandler);
- method public void unregisterFileDescriptorCallback(java.io.FileDescriptor);
- }
-
- public static abstract class MessageQueue.FileDescriptorCallback {
- ctor public MessageQueue.FileDescriptorCallback();
- method public int onFileDescriptorEvents(java.io.FileDescriptor, int);
- field public static final int EVENT_ERROR = 4; // 0x4
- field public static final int EVENT_INPUT = 1; // 0x1
- field public static final int EVENT_OUTPUT = 2; // 0x2
+ method public void removeOnFileDescriptorEventListener(java.io.FileDescriptor);
}
public static abstract interface MessageQueue.IdleHandler {
method public abstract boolean queueIdle();
}
+ public static abstract interface MessageQueue.OnFileDescriptorEventListener {
+ method public abstract int onFileDescriptorEvents(java.io.FileDescriptor, int);
+ field public static final int EVENT_ERROR = 4; // 0x4
+ field public static final int EVENT_INPUT = 1; // 0x1
+ field public static final int EVENT_OUTPUT = 2; // 0x2
+ }
+
public final class Messenger implements android.os.Parcelable {
ctor public Messenger(android.os.Handler);
ctor public Messenger(android.os.IBinder);
@@ -31013,7 +31021,6 @@
field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction";
field public static final int START_SOURCE_ASSIST_GESTURE = 4; // 0x4
field public static final int START_WITH_ASSIST = 1; // 0x1
- field public static final int START_WITH_SCREENSHOT = 2; // 0x2
}
public abstract class VoiceInteractionSession implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback {
@@ -31039,7 +31046,6 @@
method public void onDestroy();
method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
method public void onHandleAssist(android.os.Bundle);
- method public void onHandleScreenshot(android.graphics.Bitmap);
method public void onHide();
method public boolean onKeyDown(int, android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
@@ -36795,6 +36801,7 @@
field public static final int DEFAULT_DISPLAY = 0; // 0x0
field public static final int FLAG_PRESENTATION = 8; // 0x8
field public static final int FLAG_PRIVATE = 4; // 0x4
+ field public static final int FLAG_ROUND = 16; // 0x10
field public static final int FLAG_SECURE = 2; // 0x2
field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
field public static final int STATE_DOZE = 3; // 0x3
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1e9bc54..15cfbcd 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2466,13 +2466,6 @@
return true;
}
- case SYSTEM_BACKUP_RESTORED: {
- data.enforceInterface(IActivityManager.descriptor);
- systemBackupRestored();
- reply.writeNoException();
- return true;
- }
-
case NOTIFY_CLEARTEXT_NETWORK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final int uid = data.readInt();
@@ -5759,17 +5752,6 @@
}
@Override
- public void systemBackupRestored() throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- mRemote.transact(SYSTEM_BACKUP_RESTORED, data, reply, 0);
- reply.readException();
- data.recycle();
- reply.recycle();
- }
-
- @Override
public void notifyCleartextNetwork(int uid, byte[] firstPacket) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 05a936c..4ba1055 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -488,7 +488,6 @@
public void notifyLaunchTaskBehindComplete(IBinder token) throws RemoteException;
public void notifyEnterAnimationComplete(IBinder token) throws RemoteException;
- public void systemBackupRestored() throws RemoteException;
public void notifyCleartextNetwork(int uid, byte[] firstPacket) throws RemoteException;
public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
@@ -825,7 +824,6 @@
int START_IN_PLACE_ANIMATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+240;
int CHECK_PERMISSION_WITH_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+241;
int REGISTER_TASK_STACK_LISTENER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+242;
- int SYSTEM_BACKUP_RESTORED = IBinder.FIRST_CALL_TRANSACTION+243;
// Start of M transactions
int NOTIFY_CLEARTEXT_NETWORK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+280;
diff --git a/core/java/android/app/backup/RecentsBackupHelper.java b/core/java/android/app/backup/RecentsBackupHelper.java
deleted file mode 100644
index 1a64da6..0000000
--- a/core/java/android/app/backup/RecentsBackupHelper.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.backup;
-
-import android.content.Context;
-import android.os.Environment;
-import android.os.ParcelFileDescriptor;
-import android.util.Slog;
-
-import java.io.File;
-
-/**
- * Helper for saving/restoring 'recent tasks' infrastructure.
- * @hide
- */
-public class RecentsBackupHelper implements BackupHelper {
- private static final String TAG = "RecentsBackup";
- private static final boolean DEBUG = false;
-
- // This must match TaskPersister.TASKS_DIRNAME, but that class is not accessible from here
- private static final String RECENTS_TASK_DIR = "recent_tasks";
-
- // Must match TaskPersister.IMAGES_DIRNAME, as above
- private static final String RECENTS_IMAGE_DIR = "recent_images";
-
- // At restore time, tasks/thumbnails are placed in these directories alongside
- // the "live" recents dirs named above.
- private static final String RECENTS_TASK_RESTORE_DIR = "restored_" + RECENTS_TASK_DIR;
- private static final String RECENTS_IMAGE_RESTORE_DIR = "restored_" + RECENTS_IMAGE_DIR;
-
- // Prefixes for tagging the two kinds of recents backup records that we might generate
- private static final String RECENTS_TASK_KEY = "task:";
- private static final String RECENTS_IMAGE_KEY = "image:";
-
- FileBackupHelperBase mTaskFileHelper;
-
- final File mSystemDir;
- final File mTasksDir;
- final File mRestoredTasksDir;
- final File mRestoredImagesDir;
- final String[] mRecentFiles;
- final String[] mRecentKeys;
-
- /**
- * @param context The agent context in which this helper instance will run
- */
- public RecentsBackupHelper(Context context) {
- mTaskFileHelper = new FileBackupHelperBase(context);
-
- mSystemDir = new File(Environment.getDataDirectory(), "system");
- mTasksDir = new File(mSystemDir, RECENTS_TASK_DIR);
- mRestoredTasksDir = new File(mSystemDir, RECENTS_TASK_RESTORE_DIR);
- mRestoredImagesDir = new File(mSystemDir, RECENTS_IMAGE_RESTORE_DIR);
-
- // Currently we back up only the recent-task descriptions, not the thumbnails
- File[] recentFiles = mTasksDir.listFiles();
- if (recentFiles != null) {
- // We explicitly proceed even if this is a zero-size array
- final int N = recentFiles.length;
- mRecentKeys = new String[N];
- mRecentFiles = new String[N];
- if (DEBUG) {
- Slog.i(TAG, "Identifying recents for backup: " + N);
- }
- for (int i = 0; i < N; i++) {
- mRecentKeys[i] = new String(RECENTS_TASK_KEY + recentFiles[i].getName());
- mRecentFiles[i] = recentFiles[i].getAbsolutePath();
- if (DEBUG) {
- Slog.i(TAG, " " + mRecentKeys[i]);
- }
- }
- } else {
- mRecentFiles = mRecentKeys = new String[0];
- }
- }
-
- /**
- * Task-file key: RECENTS_TASK_KEY + leaf filename
- * Thumbnail-file key: RECENTS_IMAGE_KEY + leaf filename
- */
- @Override
- public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
- ParcelFileDescriptor newState) {
- FileBackupHelperBase.performBackup_checked(oldState, data, newState,
- mRecentFiles, mRecentKeys);
- }
-
- @Override
- public void restoreEntity(BackupDataInputStream data) {
- final String key = data.getKey();
- File output = null;
- if (key.startsWith(RECENTS_TASK_KEY)) {
- String name = key.substring(RECENTS_TASK_KEY.length());
- output = new File(mRestoredTasksDir, name);
- mRestoredTasksDir.mkdirs();
- } else if (key.startsWith(RECENTS_IMAGE_KEY)) {
- String name = key.substring(RECENTS_IMAGE_KEY.length());
- output = new File(mRestoredImagesDir, name);
- mRestoredImagesDir.mkdirs();
- }
-
- if (output != null) {
- if (DEBUG) {
- Slog.i(TAG, "Restoring key='"
- + key + "' to " + output.getAbsolutePath());
- }
- mTaskFileHelper.writeFile(output, data);
- }
- }
-
- @Override
- public void writeNewStateDescription(ParcelFileDescriptor newState) {
- mTaskFileHelper.writeNewStateDescription(newState);
- }
-
-}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 8768f40..b22b914 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -96,6 +96,14 @@
private static final boolean VDBG = false;
/**
+ * Default MAC address reported to a client that does not have the
+ * android.permission.LOCAL_MAC_ADDRESS permission.
+ *
+ * @hide
+ */
+ public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00";
+
+ /**
* Sentinel error value for this class. Guaranteed to not equal any other
* integer constant in this class. Provided as a convenience for functions
* that require a sentinel error value, for example:
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index bc6d4ce..fd60476 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -156,9 +156,34 @@
* value indicating that a layout dir has been set to RTL. */
public static final int SCREENLAYOUT_LAYOUTDIR_RTL = 0x02 << SCREENLAYOUT_LAYOUTDIR_SHIFT;
+ /** Constant for {@link #screenLayout}: bits that encode roundness of the screen. */
+ public static final int SCREENLAYOUT_ROUND_MASK = 0x300;
+ /** @hide Constant for {@link #screenLayout}: bit shift to get to screen roundness bits */
+ public static final int SCREENLAYOUT_ROUND_SHIFT = 8;
+ /**
+ * Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_ROUND_MASK} value indicating
+ * that it is unknown whether or not the screen has a round shape.
+ */
+ public static final int SCREENLAYOUT_ROUND_UNDEFINED = 0x00;
+ /**
+ * Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_ROUND_MASK} value indicating
+ * that the screen does not have a rounded shape.
+ */
+ public static final int SCREENLAYOUT_ROUND_NO = 0x1 << SCREENLAYOUT_ROUND_SHIFT;
+ /**
+ * Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_ROUND_MASK} value indicating
+ * that the screen has a rounded shape. Corners may not be visible to the user;
+ * developers should pay special attention to the {@link android.view.WindowInsets} delivered
+ * to views for more information about ensuring content is not obscured.
+ *
+ * <p>Corresponds to the <code>-round</code> resource qualifier.</p>
+ */
+ public static final int SCREENLAYOUT_ROUND_YES = 0x2 << SCREENLAYOUT_ROUND_SHIFT;
+
/** Constant for {@link #screenLayout}: a value indicating that screenLayout is undefined */
public static final int SCREENLAYOUT_UNDEFINED = SCREENLAYOUT_SIZE_UNDEFINED |
- SCREENLAYOUT_LONG_UNDEFINED | SCREENLAYOUT_LAYOUTDIR_UNDEFINED;
+ SCREENLAYOUT_LONG_UNDEFINED | SCREENLAYOUT_LAYOUTDIR_UNDEFINED |
+ SCREENLAYOUT_ROUND_UNDEFINED;
/**
* Special flag we generate to indicate that the screen layout requires
@@ -174,18 +199,22 @@
* <p>The {@link #SCREENLAYOUT_SIZE_MASK} bits define the overall size
* of the screen. They may be one of
* {@link #SCREENLAYOUT_SIZE_SMALL}, {@link #SCREENLAYOUT_SIZE_NORMAL},
- * {@link #SCREENLAYOUT_SIZE_LARGE}, or {@link #SCREENLAYOUT_SIZE_XLARGE}.
+ * {@link #SCREENLAYOUT_SIZE_LARGE}, or {@link #SCREENLAYOUT_SIZE_XLARGE}.</p>
*
* <p>The {@link #SCREENLAYOUT_LONG_MASK} defines whether the screen
* is wider/taller than normal. They may be one of
- * {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}.
+ * {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}.</p>
*
* <p>The {@link #SCREENLAYOUT_LAYOUTDIR_MASK} defines whether the screen layout
* is either LTR or RTL. They may be one of
- * {@link #SCREENLAYOUT_LAYOUTDIR_LTR} or {@link #SCREENLAYOUT_LAYOUTDIR_RTL}.
+ * {@link #SCREENLAYOUT_LAYOUTDIR_LTR} or {@link #SCREENLAYOUT_LAYOUTDIR_RTL}.</p>
+ *
+ * <p>The {@link #SCREENLAYOUT_ROUND_MASK} defines whether the screen has a rounded
+ * shape. They may be one of {@link #SCREENLAYOUT_ROUND_NO} or {@link #SCREENLAYOUT_ROUND_YES}.
+ * </p>
*
* <p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
- * Multiple Screens</a> for more information.
+ * Multiple Screens</a> for more information.</p>
*/
public int screenLayout;
@@ -1328,6 +1357,16 @@
}
/**
+ * Return whether the screen has a round shape. Apps may choose to change styling based
+ * on this property, such as the alignment or layout of text or informational icons.
+ *
+ * @return true if the screen is rounded, false otherwise
+ */
+ public boolean isScreenRound() {
+ return (screenLayout & SCREENLAYOUT_ROUND_MASK) == SCREENLAYOUT_ROUND_YES;
+ }
+
+ /**
*
* @hide
*/
@@ -1425,6 +1464,17 @@
break;
}
+ switch (config.screenLayout & Configuration.SCREENLAYOUT_ROUND_MASK) {
+ case Configuration.SCREENLAYOUT_ROUND_YES:
+ parts.add("round");
+ break;
+ case Configuration.SCREENLAYOUT_ROUND_NO:
+ parts.add("notround");
+ break;
+ default:
+ break;
+ }
+
switch (config.orientation) {
case Configuration.ORIENTATION_LANDSCAPE:
parts.add("land");
@@ -1640,6 +1690,11 @@
delta.screenLayout |= change.screenLayout & SCREENLAYOUT_LONG_MASK;
}
+ if ((base.screenLayout & SCREENLAYOUT_ROUND_MASK) !=
+ (change.screenLayout & SCREENLAYOUT_ROUND_MASK)) {
+ delta.screenLayout |= change.screenLayout & SCREENLAYOUT_ROUND_MASK;
+ }
+
if ((base.uiMode & UI_MODE_TYPE_MASK) != (change.uiMode & UI_MODE_TYPE_MASK)) {
delta.uiMode |= change.uiMode & UI_MODE_TYPE_MASK;
}
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index d08c52b..9046e81 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -371,7 +371,6 @@
mDeviceImpl.stopRepeating();
} catch (IllegalStateException e) {
// OK: Camera device may already be closed, nothing else to do
- Log.w(TAG, mIdString + "The camera device was already closed: ", e);
// TODO: Fire onClosed anytime we get the device onClosed or the ISE?
// or just suppress the ISE only and rely onClosed.
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 77200a5..c1b4a1f 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -106,13 +106,13 @@
ProxyInfo getDefaultProxy();
- boolean prepareVpn(String oldPackage, String newPackage);
+ boolean prepareVpn(String oldPackage, String newPackage, int userId);
- void setVpnPackageAuthorization(boolean authorized);
+ void setVpnPackageAuthorization(String packageName, int userId, boolean authorized);
ParcelFileDescriptor establishVpn(in VpnConfig config);
- VpnConfig getVpnConfig();
+ VpnConfig getVpnConfig(int userId);
void startLegacyVpn(in VpnProfile profile);
diff --git a/core/java/android/net/IpReachabilityMonitor.java b/core/java/android/net/IpReachabilityMonitor.java
index 7e1c05b..add8774 100644
--- a/core/java/android/net/IpReachabilityMonitor.java
+++ b/core/java/android/net/IpReachabilityMonitor.java
@@ -75,7 +75,6 @@
private boolean mRunning;
final private Thread mObserverThread;
- // TODO: consider passing in a NetworkInterface object from the caller.
public IpReachabilityMonitor(String ifName, Callback callback) throws IllegalArgumentException {
mInterfaceName = ifName;
int ifIndex = -1;
@@ -154,6 +153,12 @@
}
}
+ private boolean stillRunning() {
+ synchronized (mLock) {
+ return mRunning;
+ }
+ }
+
public void updateLinkProperties(LinkProperties lp) {
if (!mInterfaceName.equals(lp.getInterfaceName())) {
// TODO: figure out how to cope with interface changes.
@@ -204,6 +209,47 @@
}
}
+ public void probeAll() {
+ Set<InetAddress> ipProbeList = new HashSet<InetAddress>();
+ synchronized (mLock) {
+ ipProbeList.addAll(mIpWatchList);
+ }
+ for (InetAddress target : ipProbeList) {
+ if (!stillRunning()) { break; }
+ probeIp(target);
+ }
+ }
+
+ private void probeIp(InetAddress ip) {
+ // This currently does not cause neighbor probing if the target |ip|
+ // has been confirmed reachable within the past "delay_probe_time"
+ // seconds, i.e. within the past 5 seconds.
+ //
+ // TODO: replace with a transition directly to NUD_PROBE state once
+ // kernels are updated to do so correctly.
+ if (DBG) { Log.d(TAG, "Probing ip=" + ip.getHostAddress()); }
+
+ final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
+ 1, ip, StructNdMsg.NUD_DELAY, mInterfaceIndex, null);
+ NetlinkSocket nlSocket = null;
+
+ try {
+ nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
+ nlSocket.connectToKernel();
+ nlSocket.sendMessage(msg, 0, msg.length, 300);
+ final NetlinkMessage response = NetlinkMessage.parse(nlSocket.recvMessage(300));
+ if (response != null && response instanceof NetlinkErrorMessage) {
+ Log.e(TAG, "Error probing ip=" + response.toString());
+ }
+ } catch (ErrnoException | InterruptedIOException | SocketException e) {
+ Log.d(TAG, "Error probing ip=" + ip.getHostAddress(), e);
+ }
+
+ if (nlSocket != null) {
+ nlSocket.close();
+ }
+ }
+
private final class NetlinkSocketObserver implements Runnable {
private static final String TAG = "NetlinkSocketObserver";
@@ -242,12 +288,6 @@
if (VDBG) { Log.d(TAG, "Finishing observing thread."); }
}
- private boolean stillRunning() {
- synchronized (mLock) {
- return mRunning;
- }
- }
-
private void clearNetlinkSocket() {
if (mSocket != null) {
mSocket.close();
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index ab70485..cf747cf 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -619,6 +619,7 @@
case NET_CAPABILITY_NOT_RESTRICTED: capabilities += "NOT_RESTRICTED"; break;
case NET_CAPABILITY_TRUSTED: capabilities += "TRUSTED"; break;
case NET_CAPABILITY_NOT_VPN: capabilities += "NOT_VPN"; break;
+ case NET_CAPABILITY_VALIDATED: capabilities += "VALIDATED"; break;
}
if (++i < types.length) capabilities += "&";
}
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index a0e65eb..2bb48b3 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -156,7 +156,7 @@
*/
public static Intent prepare(Context context) {
try {
- if (getService().prepareVpn(context.getPackageName(), null)) {
+ if (getService().prepareVpn(context.getPackageName(), null, UserHandle.myUserId())) {
return null;
}
} catch (RemoteException e) {
@@ -182,10 +182,11 @@
String packageName = context.getPackageName();
try {
// Only prepare if we're not already prepared.
- if (!cm.prepareVpn(packageName, null)) {
- cm.prepareVpn(null, packageName);
+ int userId = UserHandle.myUserId();
+ if (!cm.prepareVpn(packageName, null, userId)) {
+ cm.prepareVpn(null, packageName, userId);
}
- cm.setVpnPackageAuthorization(true);
+ cm.setVpnPackageAuthorization(packageName, userId, true);
} catch (RemoteException e) {
// ignore
}
diff --git a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java
index d4b572c..b5f5817 100644
--- a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java
+++ b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java
@@ -21,9 +21,11 @@
import android.net.netlink.StructNlAttr;
import android.net.netlink.StructNlMsgHdr;
import android.net.netlink.NetlinkMessage;
+import android.system.OsConstants;
import android.util.Log;
import java.net.InetAddress;
+import java.net.Inet6Address;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -131,6 +133,34 @@
return bytes;
}
+ /**
+ * A convenience method to create an RTM_NEWNEIGH message, to modify
+ * the kernel's state information for a specific neighbor.
+ */
+ public static byte[] newNewNeighborMessage(
+ int seqNo, InetAddress ip, short nudState, int ifIndex, byte[] llAddr) {
+ final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
+ nlmsghdr.nlmsg_type = NetlinkConstants.RTM_NEWNEIGH;
+ nlmsghdr.nlmsg_flags = StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_REPLACE;
+ nlmsghdr.nlmsg_seq = seqNo;
+
+ final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage(nlmsghdr);
+ msg.mNdmsg = new StructNdMsg();
+ msg.mNdmsg.ndm_family =
+ (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
+ msg.mNdmsg.ndm_ifindex = ifIndex;
+ msg.mNdmsg.ndm_state = nudState;
+ msg.mDestination = ip;
+ msg.mLinkLayerAddr = llAddr; // might be null
+
+ final byte[] bytes = new byte[msg.getRequiredSpace()];
+ nlmsghdr.nlmsg_len = bytes.length;
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+ byteBuffer.order(ByteOrder.nativeOrder());
+ msg.pack(byteBuffer);
+ return bytes;
+ }
+
private StructNdMsg mNdmsg;
private InetAddress mDestination;
private byte[] mLinkLayerAddr;
@@ -166,6 +196,41 @@
return mCacheInfo;
}
+ public int getRequiredSpace() {
+ int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
+ if (mDestination != null) {
+ spaceRequired += NetlinkConstants.alignedLengthOf(
+ StructNlAttr.NLA_HEADERLEN + mDestination.getAddress().length);
+ }
+ if (mLinkLayerAddr != null) {
+ spaceRequired += NetlinkConstants.alignedLengthOf(
+ StructNlAttr.NLA_HEADERLEN + mLinkLayerAddr.length);
+ }
+ // Currently we don't write messages with NDA_PROBES nor NDA_CACHEINFO
+ // attributes appended. Fix later, if necessary.
+ return spaceRequired;
+ }
+
+ private static void packNlAttr(short nlType, byte[] nlValue, ByteBuffer byteBuffer) {
+ final StructNlAttr nlAttr = new StructNlAttr();
+ nlAttr.nla_type = nlType;
+ nlAttr.nla_value = nlValue;
+ nlAttr.nla_len = (short) (StructNlAttr.NLA_HEADERLEN + nlAttr.nla_value.length);
+ nlAttr.pack(byteBuffer);
+ }
+
+ public void pack(ByteBuffer byteBuffer) {
+ getHeader().pack(byteBuffer) ;
+ mNdmsg.pack(byteBuffer);
+
+ if (mDestination != null) {
+ packNlAttr(NDA_DST, mDestination.getAddress(), byteBuffer);
+ }
+ if (mLinkLayerAddr != null) {
+ packNlAttr(NDA_LLADDR, mLinkLayerAddr, byteBuffer);
+ }
+ }
+
@Override
public String toString() {
final String ipLiteral = (mDestination == null) ? "" : mDestination.getHostAddress();
diff --git a/core/java/android/net/netlink/StructNdMsg.java b/core/java/android/net/netlink/StructNdMsg.java
index e66d45d..ca1cbbb 100644
--- a/core/java/android/net/netlink/StructNdMsg.java
+++ b/core/java/android/net/netlink/StructNdMsg.java
@@ -123,9 +123,7 @@
ndm_family = (byte) OsConstants.AF_UNSPEC;
}
- public boolean pack(ByteBuffer byteBuffer) {
- if (!hasAvailableSpace(byteBuffer)) { return false; }
-
+ public void pack(ByteBuffer byteBuffer) {
// The ByteOrder must have already been set by the caller. In most
// cases ByteOrder.nativeOrder() is correct, with the exception
// of usage within unittests.
@@ -136,7 +134,6 @@
byteBuffer.putShort(ndm_state);
byteBuffer.put(ndm_flags);
byteBuffer.put(ndm_type);
- return true;
}
public boolean nudConnected() {
diff --git a/core/java/android/net/netlink/StructNlAttr.java b/core/java/android/net/netlink/StructNlAttr.java
index 9aef4c7..597a6aa 100644
--- a/core/java/android/net/netlink/StructNlAttr.java
+++ b/core/java/android/net/netlink/StructNlAttr.java
@@ -116,6 +116,14 @@
}
}
+ public void pack(ByteBuffer byteBuffer) {
+ final int originalPosition = byteBuffer.position();
+ byteBuffer.putShort(nla_len);
+ byteBuffer.putShort(nla_type);
+ byteBuffer.put(nla_value);
+ byteBuffer.position(originalPosition + getAlignedLength());
+ }
+
@Override
public String toString() {
return "StructNlAttr{ "
diff --git a/core/java/android/net/netlink/StructNlMsgErr.java b/core/java/android/net/netlink/StructNlMsgErr.java
index 5da19a2..6b02650 100644
--- a/core/java/android/net/netlink/StructNlMsgErr.java
+++ b/core/java/android/net/netlink/StructNlMsgErr.java
@@ -57,9 +57,7 @@
msg = null;
}
- public boolean pack(ByteBuffer byteBuffer) {
- if (!hasAvailableSpace(byteBuffer)) { return false; }
-
+ public void pack(ByteBuffer byteBuffer) {
// The ByteOrder must have already been set by the caller. In most
// cases ByteOrder.nativeOrder() is correct, with the possible
// exception of usage within unittests.
@@ -67,7 +65,6 @@
if (msg != null) {
msg.pack(byteBuffer);
}
- return true;
}
@Override
diff --git a/core/java/android/net/netlink/StructNlMsgHdr.java b/core/java/android/net/netlink/StructNlMsgHdr.java
index 67925ac..98ab5e7 100644
--- a/core/java/android/net/netlink/StructNlMsgHdr.java
+++ b/core/java/android/net/netlink/StructNlMsgHdr.java
@@ -39,6 +39,12 @@
public static final short NLM_F_ROOT = 0x0100;
public static final short NLM_F_MATCH = 0x0200;
public static final short NLM_F_DUMP = NLM_F_ROOT|NLM_F_MATCH;
+ // Flags for a NEW request.
+ public static final short NLM_F_REPLACE = 0x100;
+ public static final short NLM_F_EXCL = 0x200;
+ public static final short NLM_F_CREATE = 0x400;
+ public static final short NLM_F_APPEND = 0x800;
+
public static String stringForNlMsgFlags(short flags) {
final StringBuilder sb = new StringBuilder();
@@ -106,9 +112,7 @@
nlmsg_pid = 0;
}
- public boolean pack(ByteBuffer byteBuffer) {
- if (!hasAvailableSpace(byteBuffer)) { return false; }
-
+ public void pack(ByteBuffer byteBuffer) {
// The ByteOrder must have already been set by the caller. In most
// cases ByteOrder.nativeOrder() is correct, with the possible
// exception of usage within unittests.
@@ -117,7 +121,6 @@
byteBuffer.putShort(nlmsg_flags);
byteBuffer.putInt(nlmsg_seq);
byteBuffer.putInt(nlmsg_pid);
- return true;
}
@Override
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 7dd4f94..4f2e968 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -160,67 +160,67 @@
}
/**
- * Registers a file descriptor callback to receive notification when file descriptor
+ * Adds a file descriptor listener to receive notification when file descriptor
* related events occur.
* <p>
* If the file descriptor has already been registered, the specified events
- * and callback will replace any that were previously associated with it.
- * It is not possible to set more than one callback per file descriptor.
+ * and listener will replace any that were previously associated with it.
+ * It is not possible to set more than one listener per file descriptor.
* </p><p>
- * It is important to always unregister the callback when the file descriptor
+ * It is important to always unregister the listener when the file descriptor
* is no longer of use.
* </p>
*
- * @param fd The file descriptor for which a callback will be registered.
+ * @param fd The file descriptor for which a listener will be registered.
* @param events The set of events to receive: a combination of the
- * {@link FileDescriptorCallback#EVENT_INPUT},
- * {@link FileDescriptorCallback#EVENT_OUTPUT}, and
- * {@link FileDescriptorCallback#EVENT_ERROR} event masks. If the requested
- * set of events is zero, then the callback is unregistered.
- * @param callback The callback to invoke when file descriptor events occur.
+ * {@link OnFileDescriptorEventListener#EVENT_INPUT},
+ * {@link OnFileDescriptorEventListener#EVENT_OUTPUT}, and
+ * {@link OnFileDescriptorEventListener#EVENT_ERROR} event masks. If the requested
+ * set of events is zero, then the listener is unregistered.
+ * @param listener The listener to invoke when file descriptor events occur.
*
- * @see FileDescriptorCallback
- * @see #unregisterFileDescriptorCallback
+ * @see OnFileDescriptorEventListener
+ * @see #removeOnFileDescriptorEventListener
*/
- public void registerFileDescriptorCallback(@NonNull FileDescriptor fd,
- @FileDescriptorCallback.Events int events,
- @NonNull FileDescriptorCallback callback) {
+ public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
+ @OnFileDescriptorEventListener.Events int events,
+ @NonNull OnFileDescriptorEventListener listener) {
if (fd == null) {
throw new IllegalArgumentException("fd must not be null");
}
- if (callback == null) {
- throw new IllegalArgumentException("callback must not be null");
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
}
synchronized (this) {
- setFileDescriptorCallbackLocked(fd, events, callback);
+ updateOnFileDescriptorEventListenerLocked(fd, events, listener);
}
}
/**
- * Unregisters a file descriptor callback.
+ * Removes a file descriptor listener.
* <p>
- * This method does nothing if no callback has been registered for the
+ * This method does nothing if no listener has been registered for the
* specified file descriptor.
* </p>
*
- * @param fd The file descriptor whose callback will be unregistered.
+ * @param fd The file descriptor whose listener will be unregistered.
*
- * @see FileDescriptorCallback
- * @see #registerFileDescriptorCallback
+ * @see OnFileDescriptorEventListener
+ * @see #addOnFileDescriptorEventListener
*/
- public void unregisterFileDescriptorCallback(@NonNull FileDescriptor fd) {
+ public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) {
if (fd == null) {
throw new IllegalArgumentException("fd must not be null");
}
synchronized (this) {
- setFileDescriptorCallbackLocked(fd, 0, null);
+ updateOnFileDescriptorEventListenerLocked(fd, 0, null);
}
}
- private void setFileDescriptorCallbackLocked(FileDescriptor fd, int events,
- FileDescriptorCallback callback) {
+ private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,
+ OnFileDescriptorEventListener listener) {
final int fdNum = fd.getInt$();
int index = -1;
@@ -236,15 +236,15 @@
}
if (events != 0) {
- events |= FileDescriptorCallback.EVENT_ERROR;
+ events |= OnFileDescriptorEventListener.EVENT_ERROR;
if (record == null) {
if (mFileDescriptorRecords == null) {
mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>();
}
- record = new FileDescriptorRecord(fd, events, callback);
+ record = new FileDescriptorRecord(fd, events, listener);
mFileDescriptorRecords.put(fdNum, record);
} else {
- record.mCallback = callback;
+ record.mListener = listener;
record.mEvents = events;
record.mSeq += 1;
}
@@ -260,12 +260,12 @@
// Get the file descriptor record and any state that might change.
final FileDescriptorRecord record;
final int oldWatchedEvents;
- final FileDescriptorCallback callback;
+ final OnFileDescriptorEventListener listener;
final int seq;
synchronized (this) {
record = mFileDescriptorRecords.get(fd);
if (record == null) {
- return 0; // spurious, no callback registered
+ return 0; // spurious, no listener registered
}
oldWatchedEvents = record.mEvents;
@@ -274,19 +274,19 @@
return oldWatchedEvents; // spurious, watched events changed
}
- callback = record.mCallback;
+ listener = record.mListener;
seq = record.mSeq;
}
- // Invoke the callback outside of the lock.
- int newWatchedEvents = callback.onFileDescriptorEvents(
+ // Invoke the listener outside of the lock.
+ int newWatchedEvents = listener.onFileDescriptorEvents(
record.mDescriptor, events);
if (newWatchedEvents != 0) {
- newWatchedEvents |= FileDescriptorCallback.EVENT_ERROR;
+ newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR;
}
- // Update the file descriptor record if the callback changed the set of
- // events to watch and the callback itself hasn't been updated since.
+ // Update the file descriptor record if the listener changed the set of
+ // events to watch and the listener itself hasn't been updated since.
if (newWatchedEvents != oldWatchedEvents) {
synchronized (this) {
int index = mFileDescriptorRecords.indexOfKey(fd);
@@ -786,23 +786,23 @@
}
/**
- * A callback which is invoked when file descriptor related events occur.
+ * A listener which is invoked when file descriptor related events occur.
*/
- public static abstract class FileDescriptorCallback {
+ public interface OnFileDescriptorEventListener {
/**
* File descriptor event: Indicates that the file descriptor is ready for input
* operations, such as reading.
* <p>
- * The callback should read all available data from the file descriptor
- * then return <code>true</code> to keep the callback active or <code>false</code>
- * to remove the callback.
+ * The listener should read all available data from the file descriptor
+ * then return <code>true</code> to keep the listener active or <code>false</code>
+ * to remove the listener.
* </p><p>
* In the case of a socket, this event may be generated to indicate
- * that there is at least one incoming connection that the callback
+ * that there is at least one incoming connection that the listener
* should accept.
* </p><p>
* This event will only be generated if the {@link #EVENT_INPUT} event mask was
- * specified when the callback was added.
+ * specified when the listener was added.
* </p>
*/
public static final int EVENT_INPUT = 1 << 0;
@@ -811,14 +811,14 @@
* File descriptor event: Indicates that the file descriptor is ready for output
* operations, such as writing.
* <p>
- * The callback should write as much data as it needs. If it could not
+ * The listener should write as much data as it needs. If it could not
* write everything at once, then it should return <code>true</code> to
- * keep the callback active. Otherwise, it should return <code>false</code>
- * to remove the callback then re-register it later when it needs to write
+ * keep the listener active. Otherwise, it should return <code>false</code>
+ * to remove the listener then re-register it later when it needs to write
* something else.
* </p><p>
* This event will only be generated if the {@link #EVENT_OUTPUT} event mask was
- * specified when the callback was added.
+ * specified when the listener was added.
* </p>
*/
public static final int EVENT_OUTPUT = 1 << 1;
@@ -831,7 +831,7 @@
* is when the remote peer of a socket or pipe closes its end of the connection.
* </p><p>
* This event may be generated at any time regardless of whether the
- * {@link #EVENT_ERROR} event mask was specified when the callback was added.
+ * {@link #EVENT_ERROR} event mask was specified when the listener was added.
* </p>
*/
public static final int EVENT_ERROR = 1 << 2;
@@ -843,35 +843,30 @@
/**
* Called when a file descriptor receives events.
- * <p>
- * The default implementation does nothing and returns 0 to unregister the callback.
- * </p>
*
* @param fd The file descriptor.
* @param events The set of events that occurred: a combination of the
* {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks.
- * @return The new set of events to watch, or 0 to unregister the callback.
+ * @return The new set of events to watch, or 0 to unregister the listener.
*
* @see #EVENT_INPUT
* @see #EVENT_OUTPUT
* @see #EVENT_ERROR
*/
- public @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events) {
- return 0;
- }
+ @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events);
}
private static final class FileDescriptorRecord {
public final FileDescriptor mDescriptor;
public int mEvents;
- public FileDescriptorCallback mCallback;
+ public OnFileDescriptorEventListener mListener;
public int mSeq;
public FileDescriptorRecord(FileDescriptor descriptor,
- int events, FileDescriptorCallback callback) {
+ int events, OnFileDescriptorEventListener listener) {
mDescriptor = descriptor;
mEvents = events;
- mCallback = callback;
+ mListener = listener;
}
}
}
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 5b26304..c24f3fe 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -25,7 +25,7 @@
import android.content.BroadcastReceiver;
import android.content.ContentProvider;
-import android.os.MessageQueue.FileDescriptorCallback;
+import android.os.MessageQueue.OnFileDescriptorEventListener;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -236,19 +236,19 @@
final FileDescriptor[] comm = createCommSocketPair();
final ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd, comm[0]);
final MessageQueue queue = handler.getLooper().getQueue();
- queue.registerFileDescriptorCallback(comm[1],
- FileDescriptorCallback.EVENT_INPUT, new FileDescriptorCallback() {
+ queue.addOnFileDescriptorEventListener(comm[1],
+ OnFileDescriptorEventListener.EVENT_INPUT, new OnFileDescriptorEventListener() {
@Override
public int onFileDescriptorEvents(FileDescriptor fd, int events) {
Status status = null;
- if ((events & FileDescriptorCallback.EVENT_INPUT) != 0) {
+ if ((events & OnFileDescriptorEventListener.EVENT_INPUT) != 0) {
final byte[] buf = new byte[MAX_STATUS];
status = readCommStatus(fd, buf);
- } else if ((events & FileDescriptorCallback.EVENT_ERROR) != 0) {
+ } else if ((events & OnFileDescriptorEventListener.EVENT_ERROR) != 0) {
status = new Status(Status.DEAD);
}
if (status != null) {
- queue.unregisterFileDescriptorCallback(fd);
+ queue.removeOnFileDescriptorEventListener(fd);
IoUtils.closeQuietly(fd);
listener.onClose(status.asIOException());
return 0;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ef0dc3e..37645b5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5417,6 +5417,14 @@
public static final String EMERGENCY_ASSISTANCE_APPLICATION = "emergency_assistance_application";
/**
+ * Specifies whether the current app context on scren (assist data) will be sent to the
+ * assist application (active voice interaction service).
+ *
+ * @hide
+ */
+ public static final String ASSIST_STRUCTURE_ENABLED = "assist_structure_enabled";
+
+ /**
* Names of the packages that the current user has explicitly allowed to
* see all of the user's notifications, separated by ':'.
*
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 7956a3f..b8493d4 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -27,6 +27,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
@@ -322,7 +326,7 @@
if (!isBound()) return;
try {
getNotificationInterface().cancelNotificationsFromListener(mWrapper,
- new String[] {key});
+ new String[] { key });
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
@@ -464,6 +468,8 @@
for (int i = 0; i < N; i++) {
Notification notification = list.get(i).getNotification();
Builder.rebuild(getContext(), notification);
+ // convert icon metadata to legacy format for older clients
+ createLegacyIconExtras(notification);
}
return list.toArray(new StatusBarNotification[N]);
} catch (android.os.RemoteException ex) {
@@ -636,6 +642,24 @@
}
}
+ /** Convert new-style Icons to legacy representations for pre-M clients. */
+ private void createLegacyIconExtras(Notification n) {
+ Icon smallIcon = n.getSmallIcon();
+ Icon largeIcon = n.getLargeIcon();
+ if (smallIcon.getType() == Icon.TYPE_RESOURCE) {
+ n.extras.putInt(Notification.EXTRA_SMALL_ICON, smallIcon.getResId());
+ n.icon = smallIcon.getResId();
+ }
+ if (largeIcon != null) {
+ Drawable d = largeIcon.loadDrawable(getContext());
+ if (d != null && d instanceof BitmapDrawable) {
+ final Bitmap largeIconBits = ((BitmapDrawable) d).getBitmap();
+ n.extras.putParcelable(Notification.EXTRA_LARGE_ICON, largeIconBits);
+ n.largeIcon = largeIconBits;
+ }
+ }
+ }
+
private class INotificationListenerWrapper extends INotificationListener.Stub {
@Override
public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
@@ -649,6 +673,9 @@
}
Notification.Builder.rebuild(getContext(), sbn.getNotification());
+ // convert icon metadata to legacy format for older clients
+ createLegacyIconExtras(sbn.getNotification());
+
// protect subclass from concurrent modifications of (@link mNotificationKeys}.
synchronized (mWrapper) {
applyUpdate(update);
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 8c89ddb..77e2125 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -77,6 +77,7 @@
public static final int START_WITH_ASSIST = 1<<0;
/**
+ * @hide
* Flag for use with {@link #showSession}: request that the session be started with
* a screen shot of the currently focused activity.
*/
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 71b7f76..f122d10 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -820,6 +820,7 @@
public void onHandleAssist(Bundle assistBundle) {
}
+ /** @hide */
public void onHandleScreenshot(Bitmap screenshot) {
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 016541f..b93a4a5 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -168,8 +168,6 @@
final Rect mFinalStableInsets = new Rect();
final Configuration mConfiguration = new Configuration();
- private boolean mWindowIsRound;
-
final WindowManager.LayoutParams mLayout
= new WindowManager.LayoutParams();
IWindowSession mSession;
@@ -640,7 +638,6 @@
// Retrieve watch round info
TypedArray windowStyle = obtainStyledAttributes(
com.android.internal.R.styleable.Window);
- mWindowIsRound = ScreenShapeHelper.getWindowIsRound(getResources());
windowStyle.recycle();
// Add window
@@ -776,7 +773,8 @@
mFinalStableInsets.set(mDispatchedStableInsets);
mFinalSystemInsets.bottom = mIWallpaperEngine.mDisplayPadding.bottom;
WindowInsets insets = new WindowInsets(mFinalSystemInsets,
- null, mFinalStableInsets, mWindowIsRound);
+ null, mFinalStableInsets,
+ getResources().getConfiguration().isScreenRound());
onApplyWindowInsets(insets);
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index d4b971a..5a587fe 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -172,6 +172,17 @@
public static final int FLAG_PRESENTATION = 1 << 3;
/**
+ * Display flag: Indicates that the display has a round shape.
+ * <p>
+ * This flag identifies displays that are circular, elliptical or otherwise
+ * do not permit the user to see all the way to the logical corners of the display.
+ * </p>
+ *
+ * @see #getFlags
+ */
+ public static final int FLAG_ROUND = 1 << 4;
+
+ /**
* Display flag: Indicates that the contents of the display should not be scaled
* to fit the physical screen dimensions. Used for development only to emulate
* devices with smaller physicals screens while preserving density.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index b9fde8a..cf17990 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -609,6 +609,9 @@
if ((flags & Display.FLAG_SCALING_DISABLED) != 0) {
result.append(", FLAG_SCALING_DISABLED");
}
+ if ((flags & Display.FLAG_ROUND) != 0) {
+ result.append(", FLAG_ROUND");
+ }
return result.toString();
}
}
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index f18b7ac..bd45007 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -80,18 +80,12 @@
/**
* The interpolator of the underlying Animator object. By default, we don't set the interpolator
- * on the Animator and just use its default interpolator. If the interpolator is ever set on
- * this Animator, then we use the interpolator that it was set to.
+ * on the Animator and just use its default interpolator. If the interpolator is set to a
+ * non-null value on this Animator, then we use the interpolator that it was set to.
*/
private TimeInterpolator mInterpolator;
/**
- * A flag indicating whether the interpolator has been set on this object. If not, we don't set
- * the interpolator on the underlying Animator, but instead just use its default interpolator.
- */
- private boolean mInterpolatorSet = false;
-
- /**
* Listener for the lifecycle events of the underlying ValueAnimator object.
*/
private Animator.AnimatorListener mListener = null;
@@ -338,7 +332,6 @@
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator setInterpolator(TimeInterpolator interpolator) {
- mInterpolatorSet = true;
mInterpolator = interpolator;
return this;
}
@@ -349,7 +342,7 @@
* @return The timing interpolator for this animation.
*/
public TimeInterpolator getInterpolator() {
- if (mInterpolatorSet) {
+ if (mInterpolator != null) {
return mInterpolator;
} else {
// Just return the default from ValueAnimator, since that's what we'd get if
@@ -897,7 +890,7 @@
if (mDurationSet) {
animator.setDuration(mDuration);
}
- if (mInterpolatorSet) {
+ if (mInterpolator != null) {
animator.setInterpolator(mInterpolator);
}
animator.start();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b7d902c..23f3b62 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -122,7 +122,6 @@
private static final String PROPERTY_PROFILE_RENDERING = "viewroot.profile_rendering";
// properties used by emulator to determine display shape
- public static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
public static final String PROPERTY_EMULATOR_WIN_OUTSET_BOTTOM_PX =
"ro.emu.win_outset_bottom_px";
@@ -341,8 +340,6 @@
/** Set to true once doDie() has been called. */
private boolean mRemoved;
- private final boolean mWindowIsRound;
-
/**
* Consistency verifier for debugging purposes.
*/
@@ -397,7 +394,6 @@
mChoreographer = Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
loadSystemProperties();
- mWindowIsRound = ScreenShapeHelper.getWindowIsRound(context.getResources());
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -1271,7 +1267,8 @@
stableInsets = mPendingStableInsets;
}
mLastWindowInsets = new WindowInsets(contentInsets,
- null /* windowDecorInsets */, stableInsets, mWindowIsRound);
+ null /* windowDecorInsets */, stableInsets,
+ mContext.getResources().getConfiguration().isScreenRound());
}
return mLastWindowInsets;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5acd79f..a93e7ef 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -24,6 +24,7 @@
import android.annotation.StringRes;
import android.annotation.StyleRes;
import android.annotation.XmlRes;
+import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
@@ -1465,16 +1466,22 @@
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PROCESS_TEXT_REQUEST_CODE) {
- CharSequence result = data != null
- ? data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT)
- : "";
- if (isTextEditable()) {
- replaceSelectionWithText(result);
- } else {
- if (result.length() > 0) {
- Toast.makeText(getContext(), String.valueOf(result), Toast.LENGTH_LONG).show();
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ CharSequence result = data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT);
+ if (result != null) {
+ if (isTextEditable()) {
+ replaceSelectionWithText(result);
+ } else {
+ if (result.length() > 0) {
+ Toast.makeText(getContext(), String.valueOf(result), Toast.LENGTH_LONG)
+ .show();
+ }
+ }
}
}
+ if (mEditor.hasSelectionController()) {
+ mEditor.startSelectionActionModeWithSelection();
+ }
}
}
@@ -9279,7 +9286,6 @@
void replaceSelectionWithText(CharSequence text) {
((Editable) mText).replace(getSelectionStart(), getSelectionEnd(), text);
- mEditor.startSelectionActionModeWithSelection();
}
/**
diff --git a/core/java/com/android/internal/logging/MetricsConstants.java b/core/java/com/android/internal/logging/MetricsConstants.java
index 65dc743..b9a75d3 100644
--- a/core/java/com/android/internal/logging/MetricsConstants.java
+++ b/core/java/com/android/internal/logging/MetricsConstants.java
@@ -208,6 +208,7 @@
public static final int APPLICATIONS_USAGE_ACCESS_DETAIL = 183;
public static final int APPLICATIONS_HIGH_POWER_APPS = 184;
public static final int FUELGAUGE_HIGH_POWER_DETAILS = 185;
+ public static final int APPLICATIONS_MANAGE_ASSIST = 186;
//aliases
public static final int DEVICEINFO_STORAGE = DEVICEINFO_MEMORY;
diff --git a/core/java/com/android/internal/util/ScreenShapeHelper.java b/core/java/com/android/internal/util/ScreenShapeHelper.java
index 1bcc7a0..58ae853 100644
--- a/core/java/com/android/internal/util/ScreenShapeHelper.java
+++ b/core/java/com/android/internal/util/ScreenShapeHelper.java
@@ -33,16 +33,4 @@
}
return 0;
}
-
- /**
- * Get whether a device has has a round screen.
- */
- public static boolean getWindowIsRound(Resources resources) {
- if (IS_EMULATOR) {
- return SystemProperties.getBoolean(ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false);
- } else {
- return resources.getBoolean(
- com.android.internal.R.bool.config_windowIsRound);
- }
- }
}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index f98fbfc..fdc3547 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -79,7 +79,6 @@
private final FloatingToolbarPopup mPopup;
private final Rect mContentRect = new Rect();
- private final Point mCoordinates = new Point();
private Menu mMenu;
private List<CharSequence> mShowingTitles = new ArrayList<CharSequence>();
@@ -87,7 +86,6 @@
private int mSuggestedWidth;
private boolean mWidthChanged = true;
- private int mOverflowDirection;
/**
* Initializes a floating toolbar.
@@ -157,11 +155,9 @@
mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth);
mShowingTitles = getMenuItemTitles(menuItems);
}
- refreshCoordinates();
- mPopup.setOverflowDirection(mOverflowDirection);
- mPopup.updateCoordinates(mCoordinates.x, mCoordinates.y);
+ mPopup.updateCoordinates(mContentRect);
if (!mPopup.isShowing()) {
- mPopup.show(mCoordinates.x, mCoordinates.y);
+ mPopup.show(mContentRect);
}
mWidthChanged = false;
return this;
@@ -209,25 +205,6 @@
}
/**
- * Refreshes {@link #mCoordinates} with values based on {@link #mContentRect}.
- */
- private void refreshCoordinates() {
- int x = mContentRect.centerX() - mPopup.getWidth() / 2;
- int y;
- if (mContentRect.top > mPopup.getHeight()) {
- y = mContentRect.top - mPopup.getHeight();
- mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_UP;
- } else if (mContentRect.top > mPopup.getToolbarHeightWithVerticalMargin()) {
- y = mContentRect.top - mPopup.getToolbarHeightWithVerticalMargin();
- mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN;
- } else {
- y = mContentRect.bottom;
- mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN;
- }
- mCoordinates.set(x, y);
- }
-
- /**
* Returns true if this floating toolbar is currently showing the specified menu items.
*/
private boolean isCurrentlyShowing(List<MenuItem> menuItems) {
@@ -345,6 +322,8 @@
}
};
+ private final Point mCoords = new Point();
+
private final Region mTouchableRegion = new Region();
private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
new ViewTreeObserver.OnComputeInternalInsetsListener() {
@@ -378,6 +357,7 @@
mShowAnimation = createGrowFadeInFromBottom(mContentContainer);
mDismissAnimation = createShrinkFadeOutFromBottomAnimation(
mContentContainer,
+ 0,
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -387,6 +367,7 @@
});
mHideAnimation = createShrinkFadeOutFromBottomAnimation(
mContentContainer,
+ 150,
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -404,6 +385,8 @@
*/
public void layoutMenuItems(List<MenuItem> menuItems,
MenuItem.OnMenuItemClickListener menuItemClickListener, int suggestedWidth) {
+ Preconditions.checkNotNull(menuItems);
+
mContentContainer.removeAllViews();
if (mMainPanel == null) {
mMainPanel = new FloatingToolbarMainPanel(mParent.getContext(), mOpenOverflow);
@@ -426,7 +409,9 @@
* Shows this popup at the specified coordinates.
* The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
*/
- public void show(int x, int y) {
+ public void show(Rect contentRect) {
+ Preconditions.checkNotNull(contentRect);
+
if (isShowing()) {
return;
}
@@ -435,6 +420,7 @@
mDismissed = false;
cancelDismissAndHideAnimations();
cancelOverflowAnimations();
+
// Make sure a panel is set as the content.
if (mContentContainer.getChildCount() == 0) {
setMainPanelAsContent();
@@ -442,8 +428,10 @@
// The "show" animation will make this visible.
mContentContainer.setAlpha(0);
}
+ updateOverflowHeight(contentRect.top - (mMarginVertical * 2));
+ refreshCoordinatesAndOverflowDirection(contentRect);
preparePopupContent();
- mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, x, y);
+ mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, mCoords.x, mCoords.y);
setTouchableSurfaceInsetsComputer();
runShowAnimation();
}
@@ -496,27 +484,17 @@
* The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
* This is a no-op if this popup is not showing.
*/
- public void updateCoordinates(int x, int y) {
+ public void updateCoordinates(Rect contentRect) {
+ Preconditions.checkNotNull(contentRect);
+
if (!isShowing() || !mPopupWindow.isShowing()) {
return;
}
cancelOverflowAnimations();
+ refreshCoordinatesAndOverflowDirection(contentRect);
preparePopupContent();
- mPopupWindow.update(x, y, getWidth(), getHeight());
- }
-
- /**
- * Sets the direction in which the overflow will open. i.e. up or down.
- *
- * @param overflowDirection Either {@link #OVERFLOW_DIRECTION_UP}
- * or {@link #OVERFLOW_DIRECTION_DOWN}.
- */
- public void setOverflowDirection(int overflowDirection) {
- mOverflowDirection = overflowDirection;
- if (mOverflowPanel != null) {
- mOverflowPanel.setOverflowDirection(mOverflowDirection);
- }
+ mPopupWindow.update(mCoords.x, mCoords.y, getWidth(), getHeight());
}
/**
@@ -540,7 +518,26 @@
return mContentContainer.getContext();
}
- int getToolbarHeightWithVerticalMargin() {
+ private void refreshCoordinatesAndOverflowDirection(Rect contentRect) {
+ int x = contentRect.centerX() - getWidth() / 2;
+ int y;
+ if (contentRect.top > getHeight()) {
+ y = contentRect.top - getHeight();
+ mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_UP;
+ } else if (contentRect.top > getToolbarHeightWithVerticalMargin()) {
+ y = contentRect.top - getToolbarHeightWithVerticalMargin();
+ mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN;
+ } else {
+ y = contentRect.bottom;
+ mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN;
+ }
+ mCoords.set(x, y);
+ if (mOverflowPanel != null) {
+ mOverflowPanel.setOverflowDirection(mOverflowDirection);
+ }
+ }
+
+ private int getToolbarHeightWithVerticalMargin() {
return getEstimatedToolbarHeight(mParent.getContext()) + mMarginVertical * 2;
}
@@ -693,16 +690,24 @@
}
// Reset position.
- if (mMainPanel != null
- && mContentContainer.getChildAt(0) == mMainPanel.getView()) {
+ if (isMainPanelContent()) {
positionMainPanel();
}
- if (mOverflowPanel != null
- && mContentContainer.getChildAt(0) == mOverflowPanel.getView()) {
+ if (isOverflowPanelContent()) {
positionOverflowPanel();
}
}
+ private boolean isMainPanelContent() {
+ return mMainPanel != null
+ && mContentContainer.getChildAt(0) == mMainPanel.getView();
+ }
+
+ private boolean isOverflowPanelContent() {
+ return mOverflowPanel != null
+ && mContentContainer.getChildAt(0) == mOverflowPanel.getView();
+ }
+
/**
* Sets the current content to be the main view panel.
*/
@@ -765,6 +770,25 @@
setContentAreaAsTouchableSurface();
}
+ private void updateOverflowHeight(int height) {
+ if (mOverflowPanel != null) {
+ mOverflowPanel.setSuggestedHeight(height);
+
+ // Re-measure the popup and it's contents.
+ boolean mainPanelContent = isMainPanelContent();
+ boolean overflowPanelContent = isOverflowPanelContent();
+ mContentContainer.removeAllViews(); // required to update popup size.
+ updatePopupSize();
+ // Reset the appropriate content.
+ if (mainPanelContent) {
+ setMainPanelAsContent();
+ }
+ if (overflowPanelContent) {
+ setOverflowPanelAsContent();
+ }
+ }
+ }
+
private void updatePopupSize() {
int width = 0;
int height = 0;
@@ -864,6 +888,8 @@
* @return The menu items that are not included in this main panel.
*/
public List<MenuItem> layoutMenuItems(List<MenuItem> menuItems, int suggestedWidth) {
+ Preconditions.checkNotNull(menuItems);
+
final int toolbarWidth = getAdjustedToolbarWidth(mContext, suggestedWidth)
// Reserve space for the "open overflow" button.
- getEstimatedOpenOverflowButtonWidth(mContext);
@@ -972,6 +998,7 @@
private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener;
private int mOverflowWidth = 0;
+ private int mSuggestedHeight;
/**
* Initializes a floating toolbar popup overflow view panel.
@@ -981,6 +1008,7 @@
*/
public FloatingToolbarOverflowPanel(Context context, Runnable closeOverflow) {
mCloseOverflow = Preconditions.checkNotNull(closeOverflow);
+ mSuggestedHeight = getScreenHeight(context);
mContentView = new LinearLayout(context);
mContentView.setOrientation(LinearLayout.VERTICAL);
@@ -1043,6 +1071,11 @@
mContentView.addView(mBackButtonContainer, index);
}
+ public void setSuggestedHeight(int height) {
+ mSuggestedHeight = height;
+ setListViewHeight();
+ }
+
/**
* Returns the content view of the overflow.
*/
@@ -1074,9 +1107,17 @@
int itemHeight = getEstimatedToolbarHeight(mContentView.getContext());
int height = mListView.getAdapter().getCount() * itemHeight;
int maxHeight = mContentView.getContext().getResources().
+ getDimensionPixelSize(R.dimen.floating_toolbar_maximum_overflow_height);
+ int minHeight = mContentView.getContext().getResources().
getDimensionPixelSize(R.dimen.floating_toolbar_minimum_overflow_height);
+ int availableHeight = mSuggestedHeight - (mSuggestedHeight % itemHeight)
+ - itemHeight; // reserve space for the back button.
ViewGroup.LayoutParams params = mListView.getLayoutParams();
- params.height = Math.min(height, maxHeight);
+ if (availableHeight >= minHeight) {
+ params.height = Math.min(Math.min(availableHeight, maxHeight), height);
+ } else {
+ params.height = Math.min(maxHeight, height);
+ }
mListView.setLayoutParams(params);
}
@@ -1224,15 +1265,16 @@
* Creates a "shrink and fade out from bottom" animation for the specified view.
*
* @param view The view to animate
+ * @param startDelay The start delay of the animation
* @param listener The animation listener
*/
private static AnimatorSet createShrinkFadeOutFromBottomAnimation(
- View view, Animator.AnimatorListener listener) {
+ View view, int startDelay, Animator.AnimatorListener listener) {
AnimatorSet shrinkFadeOutFromBottomAnimation = new AnimatorSet();
shrinkFadeOutFromBottomAnimation.playTogether(
ObjectAnimator.ofFloat(view, View.SCALE_Y, 1, 0.5f).setDuration(125),
ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(75));
- shrinkFadeOutFromBottomAnimation.setStartDelay(150);
+ shrinkFadeOutFromBottomAnimation.setStartDelay(startDelay);
shrinkFadeOutFromBottomAnimation.addListener(listener);
return shrinkFadeOutFromBottomAnimation;
}
@@ -1271,16 +1313,4 @@
private static int getScreenHeight(Context context) {
return context.getResources().getDisplayMetrics().heightPixels;
}
-
- /**
- * Returns value, restricted to the range min->max (inclusive).
- * If maximum is less than minimum, the result is undefined.
- *
- * @param value The value to clamp.
- * @param minimum The minimum value in the range.
- * @param maximum The maximum value in the range. Must not be less than minimum.
- */
- private static int clamp(int value, int minimum, int maximum) {
- return Math.max(minimum, Math.min(value, maximum));
- }
}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index a80abce5..f485460 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -16,14 +16,12 @@
package com.android.server.backup;
-import android.app.ActivityManagerNative;
import android.app.IWallpaperManager;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupAgentHelper;
import android.app.backup.FullBackup;
import android.app.backup.FullBackupDataOutput;
-import android.app.backup.RecentsBackupHelper;
import android.app.backup.WallpaperBackupHelper;
import android.content.Context;
import android.os.Environment;
@@ -45,7 +43,6 @@
// Names of the helper tags within the dataset. Changing one of these names will
// break the ability to restore from datasets that predate the change.
private static final String WALLPAPER_HELPER = "wallpaper";
- private static final String RECENTS_HELPER = "recents";
private static final String SYNC_SETTINGS_HELPER = "account_sync_settings";
private static final String PREFERRED_HELPER = "preferred_activities";
private static final String NOTIFICATION_HELPER = "notifications";
@@ -92,7 +89,6 @@
}
}
addHelper(WALLPAPER_HELPER, new WallpaperBackupHelper(this, files, keys));
- addHelper(RECENTS_HELPER, new RecentsBackupHelper(this));
addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
@@ -127,7 +123,6 @@
addHelper("system_files", new WallpaperBackupHelper(this,
new String[] { WALLPAPER_IMAGE },
new String[] { WALLPAPER_IMAGE_KEY} ));
- addHelper(RECENTS_HELPER, new RecentsBackupHelper(this));
addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
@@ -200,13 +195,4 @@
}
}
}
-
- @Override
- public void onRestoreFinished() {
- try {
- ActivityManagerNative.getDefault().systemBackupRestored();
- } catch (RemoteException e) {
- // Not possible since this code is running in the system process.
- }
- }
}
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 2785c48..04b9a95 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1,5 +1,4 @@
#define LOG_TAG "Bitmap"
-
#include "Bitmap.h"
#include "Paint.h"
@@ -23,6 +22,10 @@
#include "core_jni_helpers.h"
#include <jni.h>
+#include <memory>
+#include <string>
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
namespace android {
@@ -135,6 +138,17 @@
mPixelRef->unref();
}
+Bitmap::Bitmap(void* address, int fd,
+ const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
+ : mPixelStorageType(PixelStorageType::Ashmem) {
+ mPixelStorage.ashmem.address = address;
+ mPixelStorage.ashmem.fd = fd;
+ mPixelStorage.ashmem.size = ashmem_get_size_region(fd);
+ mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable));
+ // Note: this will trigger a call to onStrongRefDestroyed(), but
+ // we want the pixel ref to have a ref count of 0 at this point
+ mPixelRef->unref();
+}
Bitmap::~Bitmap() {
doFreePixels();
}
@@ -156,6 +170,10 @@
mPixelStorage.external.freeFunc(mPixelStorage.external.address,
mPixelStorage.external.context);
break;
+ case PixelStorageType::Ashmem:
+ munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size);
+ close(mPixelStorage.ashmem.fd);
+ break;
case PixelStorageType::Java:
JNIEnv* env = jniEnv();
LOG_ALWAYS_FATAL_IF(mPixelStorage.java.jstrongRef,
@@ -179,6 +197,15 @@
mPixelRef->setHasHardwareMipMap(hasMipMap);
}
+int Bitmap::getAshmemFd() const {
+ switch (mPixelStorageType) {
+ case PixelStorageType::Ashmem:
+ return mPixelStorage.ashmem.fd;
+ default:
+ return -1;
+ }
+}
+
const SkImageInfo& Bitmap::info() const {
assertValid();
return mPixelRef->info();
@@ -274,6 +301,7 @@
LOG_ALWAYS_FATAL("Cannot pin invalid pixels!");
break;
case PixelStorageType::External:
+ case PixelStorageType::Ashmem:
// Nothing to do
break;
case PixelStorageType::Java: {
@@ -296,6 +324,7 @@
LOG_ALWAYS_FATAL("Cannot unpin invalid pixels!");
break;
case PixelStorageType::External:
+ case PixelStorageType::Ashmem:
// Don't need to do anything
break;
case PixelStorageType::Java: {
@@ -690,6 +719,21 @@
getPremulBitmapCreateFlags(isMutable));
}
+static jobject Bitmap_copyAshmem(JNIEnv* env, jobject, jlong srcHandle) {
+ SkBitmap src;
+ reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
+ SkBitmap result;
+
+ AshmemPixelAllocator allocator(env);
+ if (!src.copyTo(&result, &allocator)) {
+ return NULL;
+ }
+ Bitmap* bitmap = allocator.getStorageObjAndReset();
+ bitmap->peekAtPixelRef()->setImmutable();
+ jobject ret = GraphicsJNI::createBitmap(env, bitmap, getPremulBitmapCreateFlags(false));
+ return ret;
+}
+
static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) {
LocalScopedBitmap bitmap(bitmapHandle);
bitmap->detachFromJava();
@@ -898,34 +942,76 @@
}
}
- android::Bitmap* nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, bitmap.get(), ctable);
- if (!nativeBitmap) {
+ int fd = p->readFileDescriptor();
+ int dupFd = dup(fd);
+ if (dupFd < 0) {
SkSafeUnref(ctable);
+ doThrowRE(env, "Could not dup parcel fd.");
return NULL;
}
+ bool readOnlyMapping = !isMutable;
+ Bitmap* nativeBitmap = GraphicsJNI::mapAshmemPixelRef(env, bitmap.get(),
+ ctable, dupFd, readOnlyMapping);
SkSafeUnref(ctable);
-
- size_t size = bitmap->getSize();
-
- android::Parcel::ReadableBlob blob;
- android::status_t status = p->readBlob(size, &blob);
- if (status) {
- nativeBitmap->detachFromJava();
- doThrowRE(env, "Could not read bitmap from parcel blob.");
+ if (!nativeBitmap) {
+ close(dupFd);
+ doThrowRE(env, "Could not allocate ashmem pixel ref.");
return NULL;
}
-
- bitmap->lockPixels();
- memcpy(bitmap->getPixels(), blob.data(), size);
- bitmap->unlockPixels();
-
- blob.release();
+ bitmap->pixelRef()->setImmutable();
return GraphicsJNI::createBitmap(env, nativeBitmap,
getPremulBitmapCreateFlags(isMutable), NULL, NULL, density);
}
+class Ashmem {
+public:
+ Ashmem(size_t sz, bool removeWritePerm) : mSize(sz) {
+ int fd = -1;
+ void *addr = nullptr;
+
+ // Create new ashmem region with read/write priv
+ fd = ashmem_create_region("bitmap", sz);
+ if (fd < 0) {
+ goto error;
+ }
+ addr = mmap(nullptr, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ goto error;
+ }
+ // If requested, remove the ability to make additional writeable to
+ // this memory.
+ if (removeWritePerm) {
+ if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
+ goto error;
+ }
+ }
+ mFd = fd;
+ mPtr = addr;
+ return;
+error:
+ if (fd >= 0) {
+ close(fd);
+ }
+ if (addr) {
+ munmap(addr, sz);
+ }
+ }
+ ~Ashmem() {
+ if (mPtr) {
+ close(mFd);
+ munmap(mPtr, mSize);
+ }
+ }
+ void *getPtr() const { return mPtr; }
+ int getFd() const { return mFd; }
+private:
+ int mFd = -1;
+ int mSize;
+ void* mPtr = nullptr;
+};
+
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
jlong bitmapHandle,
jboolean isMutable, jint density,
@@ -937,7 +1023,9 @@
android::Parcel* p = android::parcelForJavaObject(env, parcel);
SkBitmap bitmap;
- reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
+
+ android::Bitmap* androidBitmap = reinterpret_cast<Bitmap*>(bitmapHandle);
+ androidBitmap->getSkBitmap(&bitmap);
p->writeInt32(isMutable);
p->writeInt32(bitmap.colorType());
@@ -959,25 +1047,26 @@
}
}
- size_t size = bitmap.getSize();
-
- android::Parcel::WritableBlob blob;
- android::status_t status = p->writeBlob(size, &blob);
- if (status) {
- doThrowRE(env, "Could not write bitmap to parcel blob.");
- return JNI_FALSE;
- }
-
- bitmap.lockPixels();
- const void* pSrc = bitmap.getPixels();
- if (pSrc == NULL) {
- memset(blob.data(), 0, size);
+ bool ashmemSrc = androidBitmap->getAshmemFd() >= 0;
+ if (ashmemSrc && !isMutable) {
+ p->writeDupFileDescriptor(androidBitmap->getAshmemFd());
} else {
- memcpy(blob.data(), pSrc, size);
- }
- bitmap.unlockPixels();
+ Ashmem dstAshmem(bitmap.getSize(), !isMutable);
+ if (!dstAshmem.getPtr()) {
+ doThrowRE(env, "Could not allocate ashmem for new bitmap.");
+ return JNI_FALSE;
+ }
- blob.release();
+ bitmap.lockPixels();
+ const void* pSrc = bitmap.getPixels();
+ if (pSrc == NULL) {
+ memset(dstAshmem.getPtr(), 0, bitmap.getSize());
+ } else {
+ memcpy(dstAshmem.getPtr(), pSrc, bitmap.getSize());
+ }
+ bitmap.unlockPixels();
+ p->writeDupFileDescriptor(dstAshmem.getFd());
+ }
return JNI_TRUE;
}
@@ -1193,6 +1282,8 @@
(void*)Bitmap_creator },
{ "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;",
(void*)Bitmap_copy },
+ { "nativeCopyAshmem", "(J)Landroid/graphics/Bitmap;",
+ (void*)Bitmap_copyAshmem },
{ "nativeDestructor", "(J)V", (void*)Bitmap_destructor },
{ "nativeRecycle", "(J)Z", (void*)Bitmap_recycle },
{ "nativeReconfigure", "(JIIIIZ)V", (void*)Bitmap_reconfigure },
diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h
index efeb898..95b5fae 100644
--- a/core/jni/android/graphics/Bitmap.h
+++ b/core/jni/android/graphics/Bitmap.h
@@ -29,6 +29,7 @@
Invalid,
External,
Java,
+ Ashmem,
};
class WrappedPixelRef;
@@ -50,6 +51,8 @@
const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
Bitmap(void* address, void* context, FreeFunc freeFunc,
const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
+ Bitmap(void* address, int fd, const SkImageInfo& info, size_t rowBytes,
+ SkColorTable* ctable);
const SkImageInfo& info() const;
@@ -76,6 +79,7 @@
bool hasHardwareMipMap();
void setHasHardwareMipMap(bool hasMipMap);
+ int getAshmemFd() const;
private:
friend class WrappedPixelRef;
@@ -104,6 +108,11 @@
FreeFunc freeFunc;
} external;
struct {
+ void* address;
+ int fd;
+ size_t size;
+ } ashmem;
+ struct {
JavaVM* jvm;
jweak jweakRef;
jbyteArray jstrongRef;
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 1c6f7de..ff22ef3 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -1,5 +1,8 @@
#define LOG_TAG "GraphicsJNI"
+#include <unistd.h>
+#include <sys/mman.h>
+
#include "jni.h"
#include "JNIHelp.h"
#include "GraphicsJNI.h"
@@ -10,6 +13,7 @@
#include "SkMath.h"
#include "SkRegion.h"
#include <android_runtime/AndroidRuntime.h>
+#include <cutils/ashmem.h>
#include <Caches.h>
#include <TextureCache.h>
@@ -572,6 +576,82 @@
return true;
}
+android::Bitmap* GraphicsJNI::allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
+ SkColorTable* ctable) {
+ int fd;
+
+ const SkImageInfo& info = bitmap->info();
+ if (info.fColorType == kUnknown_SkColorType) {
+ doThrowIAE(env, "unknown bitmap configuration");
+ return nullptr;
+ }
+
+ size_t size;
+ if (!computeAllocationSize(*bitmap, &size)) {
+ return nullptr;
+ }
+
+ // we must respect the rowBytes value already set on the bitmap instead of
+ // attempting to compute our own.
+ const size_t rowBytes = bitmap->rowBytes();
+
+ // Create new ashmem region with read/write priv
+ fd = ashmem_create_region("bitmap", size);
+ if (fd < 0) {
+ return nullptr;
+ }
+
+ void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ close(fd);
+ return nullptr;
+ }
+
+ if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
+ munmap(addr, size);
+ close(fd);
+ return nullptr;
+ }
+
+ android::Bitmap* wrapper = new android::Bitmap(addr, fd, info, rowBytes, ctable);
+ wrapper->getSkBitmap(bitmap);
+ // since we're already allocated, we lockPixels right away
+ // HeapAllocator behaves this way too
+ bitmap->lockPixels();
+
+ return wrapper;
+}
+
+android::Bitmap* GraphicsJNI::mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
+ SkColorTable* ctable, int fd, bool readOnly) {
+ int flags;
+
+ const SkImageInfo& info = bitmap->info();
+ if (info.fColorType == kUnknown_SkColorType) {
+ doThrowIAE(env, "unknown bitmap configuration");
+ return nullptr;
+ }
+
+ // Create new ashmem region with read/write priv
+ flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE);
+ void* addr = mmap(NULL, ashmem_get_size_region(fd), flags, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ return nullptr;
+ }
+
+ // we must respect the rowBytes value already set on the bitmap instead of
+ // attempting to compute our own.
+ const size_t rowBytes = bitmap->rowBytes();
+
+ android::Bitmap* wrapper = new android::Bitmap(addr, fd, info, rowBytes, ctable);
+ wrapper->getSkBitmap(bitmap);
+ // since we're already allocated, we lockPixels right away
+ // HeapAllocator behaves this way too
+ bitmap->lockPixels();
+
+ return wrapper;
+}
+
///////////////////////////////////////////////////////////////////////////////
JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) {
@@ -594,6 +674,25 @@
////////////////////////////////////////////////////////////////////////////////
+AshmemPixelAllocator::AshmemPixelAllocator(JNIEnv *env) {
+ LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJavaVM) != JNI_OK,
+ "env->GetJavaVM failed");
+}
+
+AshmemPixelAllocator::~AshmemPixelAllocator() {
+ if (mStorage) {
+ mStorage->detachFromJava();
+ }
+}
+
+bool AshmemPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
+ JNIEnv* env = vm2env(mJavaVM);
+ mStorage = GraphicsJNI::allocateAshmemPixelRef(env, bitmap, ctable);
+ return mStorage != nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
static jclass make_globalref(JNIEnv* env, const char classname[])
{
jclass c = env->FindClass(classname);
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index ef9c2a9..1938e85 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -95,6 +95,12 @@
static android::Bitmap* allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
SkColorTable* ctable);
+ static android::Bitmap* allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
+ SkColorTable* ctable);
+
+ static android::Bitmap* mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
+ SkColorTable* ctable, int fd, bool readOnly);
+
/**
* Given a bitmap we natively allocate a memory block to store the contents
* of that bitmap. The memory is then attached to the bitmap via an
@@ -138,6 +144,23 @@
android::Bitmap* mStorage = nullptr;
};
+class AshmemPixelAllocator : public SkBitmap::Allocator {
+public:
+ AshmemPixelAllocator(JNIEnv* env);
+ ~AshmemPixelAllocator();
+ virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
+ android::Bitmap* getStorageObjAndReset() {
+ android::Bitmap* result = mStorage;
+ mStorage = NULL;
+ return result;
+ };
+
+private:
+ JavaVM* mJavaVM;
+ android::Bitmap* mStorage = nullptr;
+};
+
+
enum JNIAccess {
kRO_JNIAccess,
kRW_JNIAccess
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp
index 90a020e..cf02e39 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -206,15 +206,20 @@
static jlong Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel)
{
- if (parcel == NULL) {
- return NULL;
+ if (parcel == nullptr) {
+ return 0;
}
android::Parcel* p = android::parcelForJavaObject(env, parcel);
SkRegion* region = new SkRegion;
size_t size = p->readInt32();
- region->readFromMemory(p->readInplace(size), size);
+ size_t actualSize = region->readFromMemory(p->readInplace(size), size);
+
+ if (size != actualSize) {
+ delete region;
+ return 0;
+ }
return reinterpret_cast<jlong>(region);
}
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 74a9e4e..dca04f5 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -615,6 +615,10 @@
const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL;
+ // Constants duplicated from Java class android.content.res.Configuration.
+ static const jint kScreenLayoutRoundMask = 0x300;
+ static const jint kScreenLayoutRoundShift = 8;
+
config.mcc = (uint16_t)mcc;
config.mnc = (uint16_t)mnc;
config.orientation = (uint8_t)orientation;
@@ -632,6 +636,13 @@
config.uiMode = (uint8_t)uiMode;
config.sdkVersion = (uint16_t)sdkVersion;
config.minorVersion = 0;
+
+ // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
+ // in C++. We must extract the round qualifier out of the Java screenLayout and put it
+ // into screenLayout2.
+ config.screenLayout2 =
+ (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
+
am->setConfiguration(config, locale8);
if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 608d718..595f9f0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -898,7 +898,9 @@
android:label="@string/permlab_changeWimaxState"
android:protectionLevel="dangerous" />
- <!-- Allows applications to act as network scorers. @hide @SystemApi-->
+ <!--@SystemApi Allows applications to the the local WiFi and Bluetooth MAC address.
+ @hide
+ -->
<permission android:name="android.permission.SCORE_NETWORKS"
android:protectionLevel="signature|system" />
@@ -2447,6 +2449,10 @@
<permission android:name="android.permission.KILL_UID"
android:protectionLevel="signature" />
+ <!-- Allows applications to act as network scorers. @hide @SystemApi-->
+ <permission android:name="android.permission.LOCAL_MAC_ADDRESS"
+ android:protectionLevel="signature" />
+
<!-- The system process is explicitly the only one allowed to launch the
confirmation UI for full backup/restore -->
<uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index d292d4e..a8d2ee3 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -132,7 +132,7 @@
android:ellipsize="end"
android:visibility="gone"
android:layout_weight="1"
- android:text="@android:string/ellipsis"
+ android:text="@android:string/notification_inbox_ellipsis"
/>
<FrameLayout
android:id="@+id/inbox_end_pad"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 84e4ca9..f790d74 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -423,11 +423,17 @@
point on the move. A value of 0 means no periodic scans will be used in the framework. -->
<integer translatable="false" name="config_wifi_framework_scan_interval">300000</integer>
- <!-- Integer indicating disconnect mode scan interval in milliseconds -->
- <integer translatable="false" name="config_wifi_disconnected_scan_interval">15000</integer>
+ <!-- Integer indicating disconnect mode short scan interval in milliseconds -->
+ <integer translatable="false" name="config_wifi_disconnected_short_scan_interval">15000</integer>
- <!-- Integer indicating associated partial scan interval in milliseconds -->
- <integer translatable="false" name="config_wifi_framework_associated_scan_interval">20000</integer>
+ <!-- Integer indicating disconnect mode long scan interval in milliseconds -->
+ <integer translatable="false" name="config_wifi_disconnected_long_scan_interval">120000</integer>
+
+ <!-- Integer indicating associated partial scan short interval in milliseconds -->
+ <integer translatable="false" name="config_wifi_associated_short_scan_interval">20000</integer>
+
+ <!-- Integer indicating associated partial scan long interval in milliseconds -->
+ <integer translatable="false" name="config_wifi_associated_long_scan_interval">180000</integer>
<!-- Integer indicating associated full scan backoff, representing a fraction: xx/8 -->
<integer translatable="false" name="config_wifi_framework_associated_full_scan_backoff">12</integer>
@@ -2151,4 +2157,10 @@
format is UMTS|LTE|... -->
<string translatable="false" name="config_radio_access_family"></string>
+ <!-- Whether the main built-in display is round. This will affect
+ Configuration.screenLayout's SCREENLAYOUT_ROUND_MASK flags for Configurations on the
+ main built-in display. Change this in device-specific overlays.
+ Defaults to the older, deprecated config_windowIsRound already used in
+ some existing device-specific resource overlays. -->
+ <bool name="config_mainBuiltInDisplayIsRound">@bool/config_windowIsRound</bool>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 84747f1..813591b 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -392,7 +392,8 @@
<dimen name="floating_toolbar_text_size">14sp</dimen>
<dimen name="floating_toolbar_menu_button_minimum_width">48dp</dimen>
<dimen name="floating_toolbar_preferred_width">328dp</dimen>
- <dimen name="floating_toolbar_minimum_overflow_height">144dp</dimen>
+ <dimen name="floating_toolbar_minimum_overflow_height">96dp</dimen>
+ <dimen name="floating_toolbar_maximum_overflow_height">192dp</dimen>
<dimen name="floating_toolbar_horizontal_margin">16dp</dimen>
<dimen name="floating_toolbar_vertical_margin">8dp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0e6b2df..2b8665e 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4127,4 +4127,8 @@
<!-- Content description for the button that closes the floating toolbar overflow. [CHAR LIMIT=NONE] -->
<string name="floating_toolbar_close_overflow_description">Close overflow</string>
+ <!-- Ellipsis character to appear in notification templates, e.g.
+ notification_template_material_inbox.xml.
+ DO NOT TRANSLATE -->
+ <string name="notification_inbox_ellipsis">\u2026</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0d306c6..6124b5b 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -294,7 +294,6 @@
<java-symbol type="bool" name="config_wifi_batched_scan_supported" />
<java-symbol type="bool" name="config_enableMultiUserUI"/>
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
- <java-symbol type="bool" name="config_windowIsRound" />
<java-symbol type="bool" name="config_hasRecents" />
<java-symbol type="bool" name="config_windowShowCircularMask" />
<java-symbol type="bool" name="config_windowEnableCircularEmulatorDisplayOverlay" />
@@ -310,7 +309,10 @@
<java-symbol type="integer" name="config_wifi_framework_current_association_hysteresis_low" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_factor" />
- <java-symbol type="integer" name="config_wifi_framework_associated_scan_interval" />
+ <java-symbol type="integer" name="config_wifi_disconnected_short_scan_interval" />
+ <java-symbol type="integer" name="config_wifi_disconnected_long_scan_interval" />
+ <java-symbol type="integer" name="config_wifi_associated_short_scan_interval" />
+ <java-symbol type="integer" name="config_wifi_associated_long_scan_interval" />
<java-symbol type="integer" name="config_wifi_framework_associated_full_scan_backoff" />
<java-symbol type="integer" name="config_wifi_framework_associated_full_scan_max_interval" />
<java-symbol type="integer" name="config_wifi_framework_associated_full_scan_max_total_dwell_time" />
@@ -380,7 +382,6 @@
<java-symbol type="integer" name="config_shortPressOnSleepBehavior" />
<java-symbol type="integer" name="config_wifi_framework_scan_interval" />
<java-symbol type="integer" name="config_wifi_supplicant_scan_interval" />
- <java-symbol type="integer" name="config_wifi_disconnected_scan_interval" />
<java-symbol type="integer" name="config_wifi_scan_interval_p2p_connected" />
<java-symbol type="bool" name="config_wifi_hal_pno_enable" />
<java-symbol type="integer" name="db_connection_pool_size" />
@@ -443,8 +444,6 @@
<java-symbol type="string" name="app_running_notification_text" />
<java-symbol type="string" name="delete" />
<java-symbol type="string" name="deleteText" />
- <java-symbol type="string" name="ellipsis_two_dots" />
- <java-symbol type="string" name="ellipsis" />
<java-symbol type="string" name="grant_permissions_header_text" />
<java-symbol type="string" name="list_delimeter" />
<java-symbol type="string" name="menu_delete_shortcut_label" />
@@ -2256,6 +2255,7 @@
<java-symbol type="dimen" name="floating_toolbar_menu_button_minimum_width" />
<java-symbol type="dimen" name="floating_toolbar_preferred_width" />
<java-symbol type="dimen" name="floating_toolbar_minimum_overflow_height" />
+ <java-symbol type="dimen" name="floating_toolbar_maximum_overflow_height" />
<java-symbol type="dimen" name="floating_toolbar_horizontal_margin" />
<java-symbol type="dimen" name="floating_toolbar_vertical_margin" />
@@ -2277,4 +2277,6 @@
<java-symbol type="bool" name="config_supportDoubleTapWake" />
<java-symbol type="drawable" name="ic_perm_device_info" />
<java-symbol type="string" name="config_radio_access_family" />
+ <java-symbol type="string" name="notification_inbox_ellipsis" />
+ <java-symbol type="bool" name="config_mainBuiltInDisplayIsRound" />
</resources>
diff --git a/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java b/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
index 0634281..a7bebad 100644
--- a/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
+++ b/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
@@ -26,9 +26,11 @@
import libcore.util.HexEncoding;
import java.net.InetAddress;
+import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.util.Arrays;
import junit.framework.TestCase;
@@ -133,7 +135,7 @@
public static final byte[] RTM_GETNEIGH_RESPONSE =
HexEncoding.decode(RTM_GETNEIGH_RESPONSE_HEX.replaceAll(" ", "").toCharArray(), false);
- public void testParseRtNetlinkNeighborRtmDelNeigh() {
+ public void testParseRtmDelNeigh() {
final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_DELNEIGH);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer);
@@ -159,7 +161,7 @@
assertEquals(InetAddress.parseNumericAddress("192.168.159.254"), destination);
}
- public void testParseRtNetlinkNeighborRtmNewNeigh() {
+ public void testParseRtmNewNeigh() {
final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_NEWNEIGH);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer);
@@ -185,7 +187,7 @@
assertEquals(InetAddress.parseNumericAddress("fe80::86c9:b2ff:fe6a:ed4b"), destination);
}
- public void testParseRtNetlinkNeighborRtmGetNeighResponse() {
+ public void testParseRtmGetNeighResponse() {
final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_GETNEIGH_RESPONSE);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
@@ -208,4 +210,48 @@
// TODO: add more detailed spot checks.
assertEquals(14, messageCount);
}
+
+ public void testCreateRtmNewNeighMessage() {
+ final int seqNo = 2635;
+ final int ifIndex = 14;
+ final byte[] llAddr =
+ new byte[] { (byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5, (byte) 6 };
+
+ // Hexadecimal representation of our created packet.
+ final String expectedNewNeighHex =
+ // struct nlmsghdr
+ "30000000" + // length = 48
+ "1c00" + // type = 28 (RTM_NEWNEIGH)
+ "0101" + // flags (NLM_F_REQUEST | NLM_F_REPLACE)
+ "4b0a0000" + // seqno
+ "00000000" + // pid (0 == kernel)
+ // struct ndmsg
+ "02" + // family
+ "00" + // pad1
+ "0000" + // pad2
+ "0e000000" + // interface index (14)
+ "0800" + // NUD state (0x08 == NUD_DELAY)
+ "00" + // flags
+ "00" + // type
+ // struct nlattr: NDA_DST
+ "0800" + // length = 8
+ "0100" + // type (1 == NDA_DST, for neighbor messages)
+ "7f000001" + // IPv4 address (== 127.0.0.1)
+ // struct nlattr: NDA_LLADDR
+ "0a00" + // length = 10
+ "0200" + // type (2 == NDA_LLADDR, for neighbor messages)
+ "010203040506" + // MAC Address (== 01:02:03:04:05:06)
+ "0000"; // padding, for 4 byte alignment
+ final byte[] expectedNewNeigh =
+ HexEncoding.decode(expectedNewNeighHex.toCharArray(), false);
+
+ final byte[] bytes = RtNetlinkNeighborMessage.newNewNeighborMessage(
+ seqNo, Inet4Address.LOOPBACK, StructNdMsg.NUD_DELAY, ifIndex, llAddr);
+ if (!Arrays.equals(expectedNewNeigh, bytes)) {
+ assertEquals(expectedNewNeigh.length, bytes.length);
+ for (int i = 0; i < Math.min(expectedNewNeigh.length, bytes.length); i++) {
+ assertEquals(expectedNewNeigh[i], bytes[i]);
+ }
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index af6df1a..0b94f8b 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -16,9 +16,13 @@
package android.widget;
+import android.app.Activity;
+import android.content.Intent;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.GetChars;
+import android.text.Selection;
+import android.text.Spannable;
/**
* TextViewTest tests {@link TextView}.
@@ -54,4 +58,66 @@
assertEquals('o', c2[4]);
assertEquals('\0', c2[5]);
}
+
+ public void testProcessTextActivityResultNonEditable() {
+ TextView tv = new TextView(mContext);
+ CharSequence originalText = "This is some text.";
+ tv.setText(originalText, TextView.BufferType.SPANNABLE);
+ assertEquals(originalText, tv.getText().toString());
+ tv.setTextIsSelectable(true);
+ Selection.setSelection((Spannable) tv.getText(), 0, tv.getText().length());
+
+ CharSequence newText = "Text is replaced.";
+ Intent data = new Intent();
+ data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText);
+ tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, data);
+
+ // This is a TextView, which can't be modified. Hence no change should have been made.
+ assertEquals(originalText, tv.getText().toString());
+ }
+
+ public void testProcessTextActivityResultEditable() {
+ EditText tv = new EditText(mContext);
+ CharSequence originalText = "This is some text.";
+ tv.setText(originalText, TextView.BufferType.SPANNABLE);
+ assertEquals(originalText, tv.getText().toString());
+ tv.setTextIsSelectable(true);
+ Selection.setSelection(tv.getText(), 0, tv.getText().length());
+
+ CharSequence newText = "Text is replaced.";
+ Intent data = new Intent();
+ data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText);
+ tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, data);
+
+ assertEquals(newText, tv.getText().toString());
+ }
+
+ public void testProcessTextActivityResultCancel() {
+ EditText tv = new EditText(mContext);
+ CharSequence originalText = "This is some text.";
+ tv.setText(originalText, TextView.BufferType.SPANNABLE);
+ assertEquals(originalText, tv.getText().toString());
+ tv.setTextIsSelectable(true);
+ Selection.setSelection(tv.getText(), 0, tv.getText().length());
+
+ CharSequence newText = "Text is replaced.";
+ Intent data = new Intent();
+ data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText);
+ tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_CANCELED, data);
+
+ assertEquals(originalText, tv.getText().toString());
+ }
+
+ public void testProcessTextActivityNoData() {
+ EditText tv = new EditText(mContext);
+ CharSequence originalText = "This is some text.";
+ tv.setText(originalText, TextView.BufferType.SPANNABLE);
+ assertEquals(originalText, tv.getText().toString());
+ tv.setTextIsSelectable(true);
+ Selection.setSelection(tv.getText(), 0, tv.getText().length());
+
+ tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, null);
+
+ assertEquals(originalText, tv.getText().toString());
+ }
}
diff --git a/docs/html/about/about_toc.cs b/docs/html/about/about_toc.cs
index b1357f2..a4bc4a5 100644
--- a/docs/html/about/about_toc.cs
+++ b/docs/html/about/about_toc.cs
@@ -1,11 +1,5 @@
<ul id="nav">
-<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot?>about/index.html">Welcome</a></div>
- <ul>
- <li><a href="<?cs var:toroot?>about/start.html">Get Started</a></li>
- </ul>
- </li>
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>about/versions/lollipop.html"
zh-tw-lang="Lollipop"
@@ -46,14 +40,20 @@
</ul>
</li>
- <li class="nav-section">
+ <!-- <li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>about/versions/android-4.0-highlights.html">
<span class="en">Ice Cream Sandwich</span></a></div>
<ul>
<li><a href="<?cs var:toroot ?>about/versions/android-4.0.3.html">Android 4.0.3 APIs</a></li>
<li><a href="<?cs var:toroot ?>about/versions/android-4.0.html">Android 4.0 APIs</a> </li>
</ul>
+ </li> -->
+
+ <li class="nav-section">
+ <div class="nav-section-header empty"><a href="<?cs
+var:toroot?>about/android.html">About Android</a></div>
</li>
+
<li class="nav-section">
<div class="nav-section-header empty"><a href="<?cs
var:toroot?>about/dashboards/index.html">Dashboards</a></div>
diff --git a/docs/html/about/android.jd b/docs/html/about/android.jd
new file mode 100644
index 0000000..ad0ea7c
--- /dev/null
+++ b/docs/html/about/android.jd
@@ -0,0 +1,111 @@
+page.title=Android, the world's most popular mobile platform
+excludeFromSuggestions=true
+walkthru=0
+header.hide=0
+
+@jd:body
+
+
+<p>Android powers hundreds of millions of mobile devices in more than 190
+countries around the world. It's the largest installed base of any mobile platform
+and growing fast—every day another million users power up their
+Android devices for the first time and start looking for apps, games,
+and other digital content. </p>
+
+
+<p>Android gives you a world-class platform for creating apps and games for
+Android users everywhere, as well as an open marketplace for distributing
+to them instantly.</p>
+
+<h3>Global partnerships and large installed base</h3>
+
+<p>Building on the contributions of the open-source Linux community and more
+than 300 hardware, software, and carrier partners, Android has rapidly become
+the fastest-growing mobile OS.</p>
+
+<blockquote>Every day more than a million new Android devices are activated worldwide.</blockquote>
+
+<p>Android’s openness has made it a favorite for consumers and developers alike,
+driving strong growth in app consumption. Android users download more than
+billions of apps and games from Google Play each month. </p>
+
+<p>With its partners, Android is continuously pushing the boundaries of hardware and software
+forward to bring new capabilities to users and developers. For developers,
+Android innovation lets you build powerful, differentiated applications
+that use the latest mobile technologies.</p>
+
+<h3>Rapid innovation</h3>
+
+<p>Android is continuously pushing the boundaries of hardware and software
+forward, to bring new capabilities to users and developers. For developers, the
+rapid evolution of Android technology lets you stay in front with powerful,
+differentiated applications.</p>
+
+<p>Android gives you access to the latest technologies and innovations across a
+multitude of device form-factors, chipset architectures, and price points. From
+multicore processing and high-performance graphics to state-of-the-art sensors,
+vibrant touchscreens, and emerging mobile technologies.</p>
+
+<h3>Powerful development framework</h3>
+
+<blockquote>Easily optimize a single binary for phones, tablets, and other devices.</blockquote>
+
+<p>Android gives you everything you need to build best-in-class app experiences.
+It gives you a single application model that lets you deploy
+your apps broadly to hundreds of millions of users across a wide range of
+devices—from phones to tablets and beyond.</p>
+
+<p>Android also gives you tools for creating apps that look great and take
+advantage of the hardware capabilities available on each device. It
+automatically adapts your UI to look its best on each device, while giving you
+as much control as you want over your UI on different device
+types. </p>
+
+<p>For example, you can create a single app binary that's optimized for
+both phone and tablet form factors. You declare your UI in lightweight sets of XML
+resources, one set for parts of the UI that are common to all form factors and
+other sets for optimzations specific to phones or tablets.
+At runtime, Android applies the correct resource sets based on its screen size,
+density, locale,
+and so on.</p>
+
+
+<p>To help you develop efficiently, the <a href="{@docRoot}tools/index.html">Android
+ Developer Tools</a>
+offer a full Java IDE with advanced features for developing, debugging, and
+packaging Android apps. Using the IDE, you can develop on any available Android
+device or create virtual devices that emulate any hardware configuration.</p>
+
+<blockquote>1.5 billion downloads a month and growing. Get your apps in front
+of millions of users at Google's scale.</blockquote>
+
+<h3>Open marketplace for distributing your apps</h3>
+
+<p>Google Play is the premier marketplace for selling and distributing Android apps.
+When you publish an app on Google Play, you reach the huge installed base of
+Android.</p>
+
+<div style="float:left;margin-right:24px;margin-top:12px;">
+<img src="{@docRoot}images/gp-device.png">
+</div>
+
+<p>As an open marketplace, Google Play puts you in control of how you sell your
+products. You can publish whenever you want, as often as you want, and to the
+customers you want. You can distribute broadly to all markets and
+devices or focus on specific segments, devices, or ranges of hardware
+capabilities.</p>
+
+<p>You can monetize in the way that works best for your business—priced or
+free, with in-app products or subscriptions—for highest engagement and
+revenues. You also have complete control of the pricing for your apps
+and in-app products and can set or change prices in any supported currency at
+any time.<p>
+
+<p>Beyond growing your customer base, Google Play helps you build visibility and
+engagement across your apps and brand. As your apps rise in popularity, Google
+Play gives them higher placement in weekly "top" charts and rankings, and for
+the best apps promotional slots in curated collections.
+</p>
+
+<p>Preinstalled on hundreds of billions of Android devices around the world,
+Google Play can be a growth engine for your business.</p>
\ No newline at end of file
diff --git a/docs/html/about/versions/lollipop.jd b/docs/html/about/versions/lollipop.jd
index 1ad5d24..63a6fe9 100644
--- a/docs/html/about/versions/lollipop.jd
+++ b/docs/html/about/versions/lollipop.jd
@@ -3,47 +3,10 @@
@jd:body
-
-
-
-
-
-
-
-
- <div style="padding:0px 0px 0px 20px;float:right;margin:0 -10px 0 0">
- <img src="{@docRoot}images/home/l-hero_2x.png" srcset="{@docRoot}images/home/l-hero.png 1x, {@docRoot}images/home/l-hero_2x.png 2x" width="460" height="300" >
- </div>
-
- <div class="landing-docs" style="float:right;clear:both;margin:68px 0 2em 3em;">
- <div class="col-4 normal-links highlights" style="font-size:12px;">
- <h3 id="thisd" >Key Developer Features</h3>
- <ul style="list-style-type:none;">
- <li><a href="#Material">Material design</a></li>
- <li><a href="#Perf">Performance focus</a></li>
- <li><a href="#Notifications">Notifications</a></li>
- <li><a href="#TV">Your apps on the big screen</a></li>
- <li><a href="#Documents">Document-centric apps</a></li>
- <li><a href="#Connectivity">Advanced connectivity</a></li>
- <li><a href="#Graphics">High-performance graphics</a></li>
- <li><a href="#Audio">More powerful audio</a></li>
- <li><a href="#Camera">Enhanced camera & video</a></li>
- <li><a href="#Work">Android in the workplace</a></li>
- <li><a href="#ScreenCapture">Screen capturing and sharing</a></li>
- <li><a href="#Sensors">New types of sensors</a></li>
- <li><a href="#WebView">Chromium WebView</a></li>
- <li><a href="#Accessibility">Accessibility & input</a></li>
- <li><a href="#Battery">Tools for battery-efficient apps</a></li>
- </ul>
- </div>
+<div style="float:right;">
+ <img src="{@docRoot}images/home/l-hero_2x.png" srcset="/images/home/l-hero.png 1x, /images/home/l-hero_2x.png 2x">
</div>
-
-
-
-
-
-
<p>Welcome to Android 5.0 Lollipop—the largest and most ambitious release for Android yet!</p>
<p>This release is packed with new features for users and thousands of new APIs for developers. It extends Android even further, from phones, tablets, and wearables, to TVs and cars.</p>
@@ -55,42 +18,63 @@
<a href="http://www.android.com/versions/lollipop-5-0/"
>www.android.com</a>.</p>
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>Key developer features</h2>
+ <ol>
+ <ul style="list-style-type:none;">
+ <li><a href="#Material">Material design</a></li>
+ <li><a href="#Perf">Performance focus</a></li>
+ <li><a href="#Notifications">Notifications</a></li>
+ <li><a href="#TV">Your apps on the big screen</a></li>
+ <li><a href="#Documents">Document-centric apps</a></li>
+ <li><a href="#Connectivity">Advanced connectivity</a></li>
+ <li><a href="#Graphics">High-performance graphics</a></li>
+ <li><a href="#Audio">More powerful audio</a></li>
+ <li><a href="#Camera">Enhanced camera & video</a></li>
+ <li><a href="#Work">Android in the workplace</a></li>
+ <li><a href="#ScreenCapture">Screen capturing and sharing</a></li>
+ <li><a href="#Sensors">New types of sensors</a></li>
+ <li><a href="#WebView">Chromium WebView</a></li>
+ <li><a href="#Accessibility">Accessibility & input</a></li>
+ <li><a href="#Battery">Tools for battery-efficient apps</a></li>
+ </ol>
+</div>
+</div>
+<p class="note">
+ <strong>Note:</strong> The Android 5.1 Lollipop MR1 update is available with additional features
+ and fixes. For more information, see the
+ <a href="{@docRoot}about/versions/android-5.1.html">Android 5.1 API Overview</a>.
+</p>
<h2 id="Material">Material design</h2>
<p>Android 5.0 brings <a href="http://www.google.com/design/spec">Material design</a> to Android and gives you an expanded UI toolkit for integrating the new design patterns easily in your apps. </p>
-
-
<p>New <strong>3D views</strong> let you set a z-level to raise elements off of the view hierarchy and cast <strong>realtime shadows</strong>, even as they move.</p>
-
<p>Built-in <strong>activity transitions</strong> take the user seamlessly from one state to another with beautiful, animated motion. The material theme adds transitions for your activities, including the ability to use <strong>shared visual elements</strong> across activities.</p>
-
-
-<div style="width:290px;margin-right:35px;float:left">
+<div style="float:left;max-width:280px;margin-right:1em;">
<div class="framed-nexus5-port-span-5">
<video class="play-on-hover" autoplay="">
- <source src="/design/material/videos/ContactsAnim.mp4">
- <source src="/design/videos/ContactsAnim.webm">
- <source src="/design/videos/ContactsAnim.ogv">
+ <source src="{@docRoot}design/material/videos/ContactsAnim.mp4">
+ <source src="{@docRoot}design/videos/ContactsAnim.webm">
+ <source src="{@docRoot}design/videos/ContactsAnim.ogv">
</video>
- </div>
- <div style="font-size:10pt;margin-left:20px;margin-bottom:30px">
+</div>
+ <p style="img-caption">
<em>To replay the movie, click on the device screen</em>
- </div>
+ </p>
</div>
-
-<p>Ripple animations are available for buttons, checkboxes, and other touch controls in your app.
+<p>Ripple animations are available for buttons, checkboxes, and other touch controls in your app.</p>
<p>You can also define vector drawables in XML and animate them in a variety of ways. Vector drawables scale without losing definition, so they are perfect for single-color in-app icons.</p>
<p>A new system-managed processing thread called <strong>RenderThread</strong> keeps animations smooth even when there are delays in the main UI thread. </p>
-
<h2 id="Perf">Performance focus</h2>
<p>Android 5.0 provides a faster, smoother and more powerful computing experience.</p>
@@ -107,9 +91,12 @@
<h2 id="Notifications">Notifications</h2>
+<div style="float:right;clear:left;margin:1em;">
+<img src="{@docRoot}images/versions/notification-headsup.png" />
+</div>
+
<p>Notifications in Android 5.0 are more visible, accessible, and configurable. </p>
-<img src="{@docRoot}images/versions/notification-headsup.png" style="float:right; margin:0 0 40px 60px" width="300" height="224" />
<p>Varying notification details may appear <strong>on the lock screen</strong> if desired by the user. Users may elect to allow none, some, or all notification content to be shown on a secure lock screen. </p>
@@ -119,8 +106,6 @@
<p>A new media notification template provides consistent media controls for notifications with up to 6 action buttons, including custom controls such as "thumbs up"—no more need for RemoteViews!</p>
-
-
<h2 id="TV">Your apps on the big screen</h2>
<p><a href="http://developer.android.com/tv/index.html">Android TV</a> provides a complete TV platform for your app's big screen experience. Android TV is centered around a simplified home screen experience that allows users to discover content easily, with personalized recommendations and voice search.</p>
@@ -131,12 +116,14 @@
<p>The TV Input Framework provides access to a wide variety of live TV input sources and brings them together in a single user interface for users to browse, view, and enjoy content. Building a TV input service for your content can help make your content more accessible on TV devices.</p>
-
-
-<img src="{@docRoot}images/versions/recents_screen_2x.png" srcset="{@docRoot}images/versions/recents_screen.png 1x, {@docRoot}images/versions/recents_screen_2x.png 2x" style="float:right; margin:0 0 40px 60px" width="300" height="521" />
-
<h2 id="Documents">Document-centric apps</h2>
+<div style="float:right;margin:1em;max-width:320px">
+<img src="{@docRoot}images/versions/recents_screen_2x.png"
+ srcset="/images/versions/recents_screen.png 1x, /images/versions/recents_screen_2x.png 2x" />
+<p class="img-caption">Document-centric recents.</p>
+</div>
+
<p>Android 5.0 introduces a redesigned Overview space (formerly called Recents) that’s more versatile and useful for multitasking.</p>
<p>New APIs allow you to show separate activities in your app as individual documents alongside other recent screens.</p>
@@ -144,7 +131,6 @@
<p>You can take advantage of concurrent documents to provide users instant access to more of your content or services. For example, you might use concurrent documents to represent files in a productivity app, player matches in a game, or chats in a messaging app. </p>
-
<h2 id="Connectivity">Advanced connectivity</h2>
<p>Android 5.0 adds new APIs that allow apps to perform concurrent operations with <strong>Bluetooth Low Energy</strong> (BLE), allowing both scanning (central mode) and advertising (peripheral mode).</p>
@@ -159,14 +145,13 @@
<p>Support for <strong><a href="http://www.khronos.org/opengles/3_X/">Khronos OpenGL ES 3.1</a></strong> now provides games and other apps the highest-performance 2D and 3D graphics capabilities on supported devices. </p>
-<p>OpenGL ES 3.1 adds compute shaders, stencil textures, accelerated visual effects, high quality ETC2/EAC texture compression, advanced texture rendering, standardized texture size and render-buffer formats, and more.</p>
-
-
-<div class="figure" style="width:350px; margin:0 0 0 60px">
-<img src="{@docRoot}images/versions/rivalknights.png" style="float:right;" width="350" height="525" />
+<div style="float:right;margin:1em;max-width:350px">
+<img src="{@docRoot}images/versions/rivalknights.png" />
<p class="img-caption">Gameloft's Rival Knights uses ASTC (Adaptive Scalable Texture Compression) from AEP and Compute Shaders from ES 3.1 to deliver HDR (High Dynamic Range) Bloom effects and provide more graphical detail.</p>
</div>
+<p>OpenGL ES 3.1 adds compute shaders, stencil textures, accelerated visual effects, high quality ETC2/EAC texture compression, advanced texture rendering, standardized texture size and render-buffer formats, and more.</p>
+
<p>Android 5.0 also introduces the <strong>Android Extension Pack</strong> (AEP), a set of OpenGL ES extensions that give you access to features like tessellation shaders, geometry shaders, ASTC texture compression, per-sample interpolation and shading, and other advanced rendering capabilities. With AEP you can deliver high-performance graphics across a range of GPUs.</p>
@@ -182,7 +167,7 @@
<p>Android now includes support for standard <strong>USB audio</strong> peripherals, allowing users to connect USB headsets, speakers, microphones, or other high performance digital peripherals. Android 5.0 also adds support for <strong>Opus</strong> audio codecs.</p>
-<p>New <strong>{@link android.media.session.MediaSession}</strong> APIs for controlling media playback now make it easier to provide consistent media controls across screens and other controllers.</p>
+<p>New <strong><code><a href="{@docRoot}reference/android/media/session/MediaSession.html">MediaSession</a></code></strong> APIs for controlling media playback now make it easier to provide consistent media controls across screens and other controllers.</p>
<h2 id="Camera">Enhanced camera & video</h2>
@@ -198,19 +183,17 @@
<p>Android 5.0 also adds support for <strong>multimedia tunneling</strong> to provide the best experience for ultra-high definition (4K) content and the ability to play compressed audio and video data together. </p>
+<h2 id="Work">Android in the workplace</h2>
-<div class="figure" style="width:320px; margin:1em 0 0 20px;padding-left:2em;">
-<img style="float:right; margin:0 1em 1em 2em"
+<div style="float:right;margin:1em;max-width:330px">
+<img
src="{@docRoot}images/android-5.0/managed_apps_launcher@2x.png"
- srcset="{@docRoot}images/android-5.0/managed_apps_launcher@2x.png 2x"
- alt="" width="300" />
+ srcset="/images/android-5.0/managed_apps_launcher@2x.png 2x"
+ alt="" />
<p class="img-caption">Users have a unified view of their personal and work apps, which are
badged for easy identification.</p>
</div>
-
-<h2 id="Work">Android in the workplace</h2>
-
<p>To enable bring-your-own-device for enterprise environments, a new
<a href="{@docRoot}about/versions/android-5.0.html#Enterprise">managed provisioning process</a>
creates a secure work profile on the device. In the launcher, apps are shown with a Work badge to
@@ -226,8 +209,6 @@
issue these devices with a device owner app already installed that
can configure global device settings.</p>
-
-
<h2 id="ScreenCapture">Screen capturing and sharing</h2>
<p>Android 5.0 lets you add screen capturing and screen sharing capabilities to your app. </p>
@@ -242,14 +223,13 @@
<p>New <strong>interaction composite sensors</strong> are now available to detect special interactions such as a <em>wake up</em> gesture, a <em>pick up</em> gesture, and a <em>glance</em> gesture.</p>
-
<h2 id="WebView">Chromium WebView</h2>
<div style="float:right;margin:1em 2em 1em 2em;">
- <img src="/images/kk-chromium-icon.png" alt="" height="160" style="margin-bottom:0em;">
+ <img src="{@docRoot}images/kk-chromium-icon.png" alt="" height="160" style="margin-bottom:0em;">
</div>
-<p>The initial release for Android 5.0 includes a version of Chromium for {@link android.webkit.WebView} based on the Chromium M37 release, adding support for <strong>WebRTC</strong>, <strong>WebAudio</strong>, and <strong>WebGL</strong>. </p>
+<p>The initial release for Android 5.0 includes a version of Chromium for <code><a href="{@docRoot}reference/android/webkit/WebView.html">WebView</a></code> based on the Chromium M37 release, adding support for <strong>WebRTC</strong>, <strong>WebAudio</strong>, and <strong>WebGL</strong>. </p>
<p>Chromium M37 also includes native support for all of the <strong>Web Components</strong> specifications: Custom Elements, Shadow DOM, HTML Imports, and Templates. This means you can use <a href="http://polymer-project.org/">Polymer</a> and its <a href="https://www.polymer-project.org/docs/elements/material.html">material design elements</a> in a WebView without needing polyfills.</p>
@@ -257,8 +237,6 @@
<p>As new versions of Chromium become available, users can update from Google Play to ensure they get the latest enhancements and bug fixes for WebView, providing the latest web APIs and bug fixes for apps using WebView on Android 5.0 and higher.</p>
-
-
<h2 id="Accessibility">Accessibility & input</h2>
<p>New accessibility APIs can retrieve detailed information about the properties of windows on the screen that sighted users can interact with and define standard or customized input actions for UI elements.</p>
@@ -266,13 +244,12 @@
<p>New Input method editor (IME) APIs enable faster switching to other IMEs directly from the input method.</p>
-
<h2 id="Battery">Tools for building battery-efficient apps</h2>
<p>New <strong>job scheduling</strong> APIs allow you optimize battery life by deferring jobs for the system to run at a later time or under specified conditions, such as when the device is charging or connected to Wi-Fi.</p>
<p>A new <code>dumpsys batterystats</code> command generates <strong>battery usage statistics</strong> that you can use to understand system-wide power use and understand the impact of your app on the device battery. You can look at a history of power events, approximate power use per UID and system component, and more.</p>
-<img src="{@docRoot}images/versions/battery_historian.png" srcset="{@docRoot}images/versions/battery_historian@2x.png 2x" alt="" width="760" height="462" />
+<img src="{@docRoot}images/versions/battery_historian.png" srcset="/images/versions/battery_historian@2x.png 2x" alt="" width="760" height="462" />
<p class="img-caption">Battery Historian is a new tool to convert the statistics from <code>dumpsys batterystats</code> into a visualization for battery-related debugging. You can find it at <a href="https://github.com/google/battery-historian"
->https://github.com/google/battery-historian</a>.</p>
+>https://github.com/google/battery-historian</a>.</p>
\ No newline at end of file
diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd
index d66f8f4..f516677 100644
--- a/docs/html/develop/index.jd
+++ b/docs/html/develop/index.jd
@@ -13,7 +13,8 @@
<div class="wrap">
<div class="cols dac-hero-content">
<div class="col-1of2 col-push-1of2 dac-hero-figure">
- <img class="dac-hero-image" src="{@docRoot}images/develop/studio-open.png">
+ <img class="dac-hero-image" src="{@docRoot}images/develop/hero_image_studio5_2x.png"
+ srcset="/images/develop/hero_image_studio5.png 1x, /images/develop/hero_image_studio5_2x.png 2x" />
</div>
<div class="col-1of2 col-pull-1of2">
<h1 class="dac-hero-title">Get Started with Android Studio</h1>
@@ -60,7 +61,7 @@
data-maxResults="3"></div>
</div></section>
-<section class="dac-section dac-section-light"><div class="wrap">
+<section class="dac-section dac-light"><div class="wrap">
<h1 class="dac-section-title">Tools for building apps</h1>
<div class="dac-section-subtitle">
Insights into Android's tools and libraries to speed your development.
@@ -74,7 +75,7 @@
</ul>
</div></section>
-<section class="dac-section dac-light"><div class="wrap">
+<section class="dac-section dac-section-light"><div class="wrap">
<h1 class="dac-section-title">Android performance patterns</h1>
<div class="dac-section-subtitle">
Everything you need to know about improving your app’s performance.
diff --git a/docs/html/distribute/analyze/index.jd b/docs/html/distribute/analyze/index.jd
index f948dbd..c40a699 100644
--- a/docs/html/distribute/analyze/index.jd
+++ b/docs/html/distribute/analyze/index.jd
@@ -31,8 +31,6 @@
data in AdMob</a> and have the full picture of your app revenue.
</p>
-<div class="dynamic-grid">
-
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/analyzelanding"
data-cardSizes="6x6"
diff --git a/docs/html/distribute/analyze/start.jd b/docs/html/distribute/analyze/start.jd
index 2a5a9f4..c3a1f87 100644
--- a/docs/html/distribute/analyze/start.jd
+++ b/docs/html/distribute/analyze/start.jd
@@ -1,7 +1,7 @@
page.title=Get Started with Analytics
page.metaDescription=Unlock the power of Analytics by choosing the implementation that works best for your app.
page.tags="analytics, user behavior"
-page.image=distribute/images/gp-analytics-logo.jpg
+page.image=images/cards/card-analytics_2x.png
@jd:body
diff --git a/docs/html/distribute/engage/ads.jd b/docs/html/distribute/engage/ads.jd
index 9ca72f3..ad6940f 100644
--- a/docs/html/distribute/engage/ads.jd
+++ b/docs/html/distribute/engage/ads.jd
@@ -15,21 +15,26 @@
app engagement campaigns</a>.
</p>
-<div>
- <div class="figure-left" style="width:46%;">
+
+
+<div class="wrap">
+ <div class="cols" style="margin-top:2em;">
+ <div class="col-1of2">
<h3>From search</h3>
<img src="/images/distribute/promote_ads.png">
<p class="figure-caption">Add deep links to your app, then bring users straight
to relevant app content when they’re searching.</p>
- </div>
- <div class="figure-right" style="width:46%;">
+ </div>
+ <div class="col-1of2">
<h3>From apps</h3>
<img src="/images/distribute/promote_ads_inapp.png">
<p class="figure-caption">Use remarketing and deep links to bring users to just the right
place in your app to re-engage and convert, from other apps and games they love.</p>
+ </div>
</div>
</div>
+
<h3 id="tips">Tips</h2>
<ul>
diff --git a/docs/html/distribute/engage/appindexing.jd b/docs/html/distribute/engage/appindexing.jd
deleted file mode 100644
index 2b8f315..0000000
--- a/docs/html/distribute/engage/appindexing.jd
+++ /dev/null
@@ -1,61 +0,0 @@
-page.title=Bring Users from Google Search
-page.metaDescription=Use search to bring your existing users back into your app.
-page.image=images/cards/adwords_2x.jpg
-page.tags="engagement, search"
-@jd:body
-
-<p>Use the features of Google Search for Android to drive the use of your apps: </p>
-
-<ul>
-<li>Once users have installed your app, search can bring them back with <strong>deep-links</strong> direct to your app. </li>
- <li>When users use <strong>voice commands</strong> to ask Google to perform a task, your app can be one of those
-completing the task.</li>
-
-<li>You can also take advantage of <strong>Google Now</strong> to
-display cards for event, flight, hotel, and restaurant reservations you notify
-to users’ gmail addresses, and bring users back from the email linked to the
-card.</li>
-</ul>
-
-<p>Start now by <a href="https://developers.google.com/app-indexing/">indexing your app</a>, then take advantage of <a href="https://developers.google.com/voice-actions/">Voice Actions</a>, the <a href="https://developers.google.com/app-indexing/webmasters/appindexingapi">App Indexing API</a>, and <a href="https://developers.google.com/schemas/now/cards">Google Now Cards</a>.</p>
-
-
-<h2 id="help_users_find_your_information">Help Users Find Your Information</h2>
-
-<p>Re-engage with your users with deep-links displayed in search results, links
-that take users directly to content within your app.</p>
-
-
-<div style="margin-top:1.5em;margin-left:24px">
-<img src="{@docRoot}images/distribute/more-app-engagement.png">
-</div>
-<h2 id="empower_users_in_your_app">Empower your users to get things done in your app</h2>
-
-<p>Brings your users into your app to take action with voice actions such as “Ok
-Google, play a song” with the music app of choice, or “Ok Google, search for
-hotels in Maui on TripAdvisor” in the TripAdvisor app.</p>
-
-<div style="margin-top:1em">
-<img src="{@docRoot}images/distribute/music-action.png">
-</div>
-
-
-<h2 id="assist_your_users">Assist your users where and when they need it</h2>
-
-<div class="figure">
-<img src="https://developers.google.com/schemas/images/now_eventconfirmation.png">
-</div>
-
-<p>Inform your users of their reservations with cards created from structured data
-markup delivered in Gmail notifications. Cards also lead users quickly back to
-your email message, for further engagement.</p>
-
-<h2 style="clear:both" id="related-resources">Related Resources</h2>
-
-<div class="resource-widget resource-flow-layout col-13"
- data-query="collection:distribute/engage/appindexing"
- data-sortOrder="-timestamp"
- data-cardSizes="9x3"
- data-maxResults="6"></div>
-
-
diff --git a/docs/html/distribute/engage/deep-linking.jd b/docs/html/distribute/engage/deep-linking.jd
index 0c78a50..701cb99 100644
--- a/docs/html/distribute/engage/deep-linking.jd
+++ b/docs/html/distribute/engage/deep-linking.jd
@@ -1,4 +1,4 @@
-page.title=Drive Usage with Search
+page.title=Increase Usage with Search
page.metaDescription=Use search to bring your existing users back into your app.
page.image=images/cards/google-search_2x.png
page.tags=engagement, appindexing, search
@@ -47,13 +47,12 @@
<h2 id="assist_your_users">Google Now</h2>
-<div style="margin-top:1em">
-<img src="{@docRoot}images/distribute/google-now-engagement.png">
-</div>
-
<p>If you’re building travel, entertainment, or restaurant apps, Google Now cards
can re-engage your users via structured data markup delivered in email notifications.</p>
+<div style="margin-top:1em">
+<img src="{@docRoot}images/distribute/google-now-engagement.png">
+</div>
<h2 id="tips">Tips</h2>
@@ -70,7 +69,7 @@
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/engage/appindexing"
data-sortOrder="-timestamp"
- data-cardSizes="9x3"
- data-maxResults="6"></div>
+ data-cardSizes="6x2"
+ data-maxResults="3"></div>
diff --git a/docs/html/distribute/engage/easy-signin.jd b/docs/html/distribute/engage/easy-signin.jd
index 2bfa5d1..924c5b4 100644
--- a/docs/html/distribute/engage/easy-signin.jd
+++ b/docs/html/distribute/engage/easy-signin.jd
@@ -1,105 +1,76 @@
-page.title=Make Signing In Easy
+page.title=Add Quick and Secure Google Sign-in
page.metaDescription=Increase conversion rates while helping users minimize typing by letting users sign in with Google+.
-page.tags="google+"
+page.tags="google", "identity", "signin"
page.image=images/cards/google-sign-in_2x.png
-
@jd:body
-<div class="sidebox-wrapper" style="float:right;">
- <div class="sidebox" style="width:360px;">
- <p>
- <strong>Tip:</strong> For game developers, Google+ signin is already
- included as part of Google Play game services.
- </p>
+<p>Get people into your apps quickly and securely, using a registration system they
+already use and trust – their Google account. With minimal effort, you can increase
+registration and sign-in conversion by adding trusted registration system that's
+familiar to users, consistent across devices, and quick and easy to use.</p>
+
+<p>Get started <a href="https://developers.google.com/identity/sign-in/">integrating
+Google sign-in into your apps and games</a>.</p>
+
+<div class="wrap">
+ <div class="cols" style="margin-top:2em;">
+ <div class="col-3of12">
+ <h3>Quick and secure app access</h3>
+ <p>A secure authentication system that makes sign-in easy for your users by
+ letting them use their Google account, which they already use with Gmail,
+ Play, Google+, and other Google services.</p>
+ </div>
+ <div class="col-8of12 col-push-1of12">
+ <img src="{@docRoot}images/distribute/signin-secure.png" style="padding-top:1em;">
+ </div>
+ </div>
+
+ <div class="cols" style="margin-top:2em;">
+ <div class="col-3of12">
+ <h3>Seamless experience across screens</h3>
+ <p>Keep your users engaged, no matter what device they pick up or sit down at.
+ Offer a seamless app experience across devices and into your website, securely
+ from a one-time consent. </p>
+ </div>
+ <div class="col-8of12 col-push-1of12">
+ <img src="{@docRoot}images/distribute/signin-seamless.png" style="padding-top:1em;">
+ </div>
+ </div>
+
+ <div class="cols" style="margin-top:2em;">
+ <div class="col-3of12">
+ <h3>Help your users take action with Google</h3>
+ <p>Securely connect users with Google services and let them pay with Google
+ Wallet, share with Google contacts, save files to Drive, add events to
+ Calendar, and more.</p>
+ </div>
+ <div class="col-8of12 col-push-1of12">
+ <img src="{@docRoot}images/distribute/signin-apps.png" style="padding-top:1em;">
+ </div>
</div>
</div>
-<p>
- Increase conversion rates while helping users minimize typing by letting
- users sign in with Google+. The <a href=
- "{@docRoot}google/play-services/plus.html">Google+ platform for Android</a>
- authenticates users with their Google credentials safely and securely. With
- your <a href="https://developers.google.com/+/mobile/android/sign-in">users
- signing in with Google</a>, you can create more engaging experiences and
- drive the use of your apps .
-</p>
-<div style="width:450px;">
- <img src="{@docRoot}images/google/gps-googleplus.png" style="padding-top:1em;">
-</div>
-
-<p>
- Use the Google+ social graph to welcome users by name, display their
- pictures, connect them with friends and more. Users authenticate once and
- then are signed-in automatically when they come back, eliminating the need to
- remember and type names and passwords.
-</p>
-
-<div class="headerLine">
- <h2>
- And Spreading the Word a Snap
- </h2>
-
-
-</div>
-
-
-<div class="figure" style="float:right;">
- <img src="{@docRoot}images/gp-engage-share-plus.png" style=
- "width:160px;padding-top:1em;">
- <p class="img-caption">
- Easy sharing through Google+
- </p>
-</div>
-
-
-<p>
- Using Google+ can help users spread the word about your apps to their
- friends, attracting them to your apps, right from within your apps:
-</p>
-
-<p>
- Google+ is also a great way to build a community of loyal fans that will help
- you with <a href=
- "https://support.google.com/googleplay/android-developer/answer/3131213">beta
- testing</a>.
-</p>
+<h2>Tips</h2>
<ul>
- <li>Using a <a href=
- "https://developers.google.com/+/mobile/android/recommend">native +1
- button</a> to let users make a recommendation for your apps or their content.
- </li>
-
- <li>
- <a href="https://developers.google.com/+/mobile/android/share/">Share rich
- content</a> to the Google+ stream, including text, photos, URL attachments,
- and location.
- </li>
-
- <li>Create <a href=
- "https://developers.google.com/+/mobile/android/share/interactive-post">Interactive
- posts</a> to share your website or apps, users can even invite friends to
- "listen," "RSVP," "check-in," or one of over 100 actions.
- </li>
+<li>Add <strong>over-the-air installs</strong> to your website. After signing in with Google
+ on the web, users will have the option to send your Android app to their device instantly,
+ without them ever leaving your website.</li>
+ <li>With Google sign-in, you can take advantage of other <strong>Google+ platform
+ features</strong> like adding a +1 button so users can make recommendations and the ability
+ to share rich content to the Google+ stream.</li>
</ul>
-<p style="clear:both">
-</p>
- <div class="headerLine">
- <h2 id="related-resources">
- Related Resources
- </h2>
+<h2 style="clear:both" id="related-resources">Related Resources</h2>
- </div>
-
- <div class="resource-widget resource-flow-layout col-13"
+<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/engage/gplus"
data-sortorder="-timestamp"
data-cardsizes="9x3"
- data-maxresults="6">
- </div>
+ data-maxresults="4">
</div>
+
diff --git a/docs/html/distribute/engage/engage_toc.cs b/docs/html/distribute/engage/engage_toc.cs
index 7094713..f8607f5 100644
--- a/docs/html/distribute/engage/engage_toc.cs
+++ b/docs/html/distribute/engage/engage_toc.cs
@@ -20,7 +20,7 @@
<li class="nav-section">
<div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs
var:toroot?>distribute/engage/easy-signin.html">
- <span class="en">Make Signing In Easy</span></a>
+ <span class="en">Add Google Sign-in</span></a>
</div>
</li>
<li class="nav-section">
@@ -41,7 +41,6 @@
<span class="en">Use the Power of Intents</span></a>
</div>
</li>
-
<li class="nav-section">
<div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs
var:toroot?>distribute/engage/analytics.html">
diff --git a/docs/html/distribute/engage/index.jd b/docs/html/distribute/engage/index.jd
index a47e004..165cc0e 100644
--- a/docs/html/distribute/engage/index.jd
+++ b/docs/html/distribute/engage/index.jd
@@ -11,8 +11,6 @@
techniques to keep your users coming back.
</p>
-<div class="dynamic-grid">
-
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/engagelanding"
data-cardSizes="6x6"
@@ -20,22 +18,14 @@
</div>
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/engagelanding"
- data-cardSizes="6x2"
+ data-cardSizes="6x3"
data-maxResults="20">
</div>
- <h3>Related Resources</h3>
+<!-- <h2>Related Resources</h2>
<div class="resource-widget resource-flow-layout col-16"
- data-query="type:youtube+tag:engagement"
- data-sortOrder="-timestamp"
- data-cardSizes="6x3"
+ data-query="tag:engagement"
+ data-sortOrder="random"
+ data-cardSizes="6x2"
data-maxResults="3">
- </div>
- <div class="resource-widget resource-flow-layout col-16"
- data-query="type:blog+tag:engagement"
- data-sortdOrder="-timestamp"
- data-cardSizes="6x3"
- data-maxResults="3">
- </div>
-
-</div>
\ No newline at end of file
+ </div> -->
diff --git a/docs/html/distribute/essentials/best-practices/apps.jd b/docs/html/distribute/essentials/best-practices/apps.jd
deleted file mode 100644
index bbac727..0000000
--- a/docs/html/distribute/essentials/best-practices/apps.jd
+++ /dev/null
@@ -1,260 +0,0 @@
-page.title=App Developer Best Practices
-page.image=/distribute/images/gp-app-practices.png
-page.metaDescription=Essential tips for launching successful apps in Google Play.
-@jd:body
-
-<div id="qv-wrapper"><div id="qv">
-<h2>Best Practices</h2>
-<ol>
-<li><a href="#essentials">Get the Essentials Right</a></li>
-<li><a href="#users">Get Users</a></li>
-<li><a href="#engage">Engage and Retain</a></li>
-<li><a href="#beyond">Beyond the Basics</a></li>
-<li><a href="#related-resources">Related Resources</a></li>
-</ol>
-</div></div>
-
-<p>The following best practices have enabled developers worldwide to build great, successful apps for Google Play.</p>
-
-<div class="headerLine">
-<h2 id="essentials">Get the Essentials Right</h2>
-</div>
-
-<h3>1. Make it Android</h3>
-
-<ul>
- <li>
- <p>
- Build your apps to make best use of the unique Android features, such as
- <a href="{@docRoot}distribute/engage/widgets.html">widgets</a>, <a href=
- "{@docRoot}distribute/engage/notifications.html">rich notifications</a>,
- <a href=
- "http://android-developers.blogspot.com/2012/02/share-with-intents.html">sharing
- through Intents</a>, and more.
- </p>
- </li>
-
- <li>
- <p>
- Add the power of Google features your users already love, such as
- <a href="https://developers.google.com/maps/documentation/android/">Google
- Maps</a>, <a href="https://developers.google.com/drive/">Google
- Drive</a>, and more, all with <a href=
- "https://developers.google.com/+/mobile/android/sign-in">single sign
- on</a>.
- </p>
- </li>
-</ul>
-
-<h3>
- 2. Make it quality
-</h3>
-
-<ul>
- <li>
- <p>
- Make sure your apps follow the <a href=
- "{@docRoot}distribute/essentials/quality/core.html">Core App Quality</a>
- guidelines.
- </p>
- </li>
-
- <li>
- <p>
- Create apps that are available on all form factors and screen sizes, by
- following the <a href=
- "{@docRoot}distribute/essentials/quality/tablets.html">Tablet App
- Quality</a> guidelines.
- </p>
- </li>
-
- <li>
- <p>
- Test and <a href=
- "{@docRoot}distribute/essentials/optimizing-your-app.html">optimize your
- quality</a> at every step and make use of the Google Play <a href=
- "{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">beta-testing</a>
- and <a href=
- "{@docRoot}distribute/googleplay/developer-console.html#staged-rollouts">staged
- rollouts</a> features to test with users before launch.
- </p>
- </li>
-</ul>
-
-<div class="headerLine">
- <h2 id="users">
- Get Users
- </h2>
-
-
-</div>
-
-<h3>
- 1. Build buzz
-</h3>
-
-<ul>
- <li>
- <p>
- Create a great <a href="{@docRoot}distribute/users/your-listing.html">app
- listing page</a> to showcase your apps and grab users’ attention. Don’t
- forget to include a <a href=
- "{@docRoot}distribute/engage/video.html">YouTube video</a>.
- </p>
- </li>
-
- <li>
- <p>
- <a href="{@docRoot}distribute/tools/launch-checklist.html">Launch</a> on
- multiple platforms simultaneously to maximize your reach.
- </p>
- </li>
-
- <li>
- <p>
- Promote your apps with the official <a href=
- "{@docRoot}distribute/tools/promote/badges.html">Google Play badge</a>
- and <a href="{@docRoot}distribute/tools/promote/linking.html">link to
- your products</a> on Google Play.
- </p>
- </li>
-
- <li>
- <p>
- Build a community with social media, <a href=
- "http://groups.google.com/">forums</a>, and <a href=
- "http://plus.google.com">communities</a> to get and keep users talking.
- </p>
- </li>
-</ul>
-
-<h3>
- 2. Optimize for great ratings
-</h3>
-
-<ul>
- <li>
- <p>
- Get to <a href="{@docRoot}distribute/users/know-your-user.html">know your
- users</a>, listen to and <a href=
- "{@docRoot}distribute/engage/app-updates.html">update your apps</a> from
- their feedback.
- </p>
- </li>
-
- <li>
- <p>
- Focus on your strength markets first, get these right before expanding.
- </p>
- </li>
-</ul>
-
-<div class="headerLine">
- <h2 id="engage">
- Engage and Retain
- </h2>
-
-
-</div>
-
-<h3>
- 1. Keep users coming back
-</h3>
-
-<ul>
- <li>
- <p>
- Use <a href="{@docRoot}google/play/billing/index.html">Google Play In-app
- Billing</a> to offer subscriptions to extended features.
- </p>
- </li>
-
- <li>
- <p>
- Hold competitions and offer promotions, then announce them through
- <a href="{@docRoot}design/patterns/notifications.html">notifications</a>.
- </p>
- </li>
-</ul>
-
-<h3>
- 2. Earn users’ love
-</h3>
-
-<ul>
- <li>
- <p>
- <a href=
- "http://android-developers.blogspot.com/2013/05/all-google-play-developers-can-now.html">
- Respond to reviews</a> and get valuable feedback from the community
- you've built.
- </p>
- </li>
-
- <li>
- <p>
- <a href=
- "http://android-developers.blogspot.com/2013/10/improved-app-insight-by-linking-google.html">
- Measure</a> your campaigns to see what is driving users to install your
- apps.
- </p>
- </li>
-
- <li>
- <p>
- <a href=
- "{@docRoot}distribute/essentials/optimizing-your-app.html#measuring-analyzing-responding">
- Analyze in-app use</a> to steer content updates and prolong the life of
- your apps.
- </p>
- </li>
-</ul>
-
-<div class="headerLine">
- <h2 id="beyond">
- Beyond the Basics
- </h2>
-
-
-</div>
-
-<ul>
- <li>
- <p>
- After you’ve launched in your market of strength, <a href=
- "{@docRoot}distribute/users/expand-to-new-markets.html">expand into other
- markets</a> strategically and <a href=
- "{@docRoot}distribute/tools/localization-checklist.html">localize</a>
- your apps as you go.
- </p>
- </li>
-
- <li>
- <p>
- Keep users engaged, and stay ahead of the competition, by continually
- <a href=
- "{@docRoot}distribute/essentials/optimizing-your-app.html">optimizing
- your apps</a> to offer new and better features, or retire those that
- users aren’t using.
- </p>
- </li>
-
- <li>
- <p>
- Build educational apps: learn <a href=
- "{@docRoot}distribute/googleplay/edu/start.html">how to make apps for
- Google Play for Education</a>.
- </p>
- </li>
-</ul>
-
-<div class="headerLine">
-<h2 id="related-resources">Related Resources</h2>
-</div>
-
-<div class="resource-widget resource-flow-layout col-13"
- data-query="collection:distribute/toolsreference/bestpractices/apps"
- data-sortOrder="-timestamp"
- data-cardSizes="9x3,9x3"
- data-maxResults="6"></div>
-
diff --git a/docs/html/distribute/essentials/best-practices/games.jd b/docs/html/distribute/essentials/best-practices/games.jd
deleted file mode 100644
index c4ce66e..0000000
--- a/docs/html/distribute/essentials/best-practices/games.jd
+++ /dev/null
@@ -1,259 +0,0 @@
-page.title=Game Developer Best Practices
-page.image=/distribute/images/gp-games-practices.png
-page.metaDescription=Essential tips for launching successful games in Google Play.
-
-@jd:body
-
-<div id="qv-wrapper"><div id="qv">
-<h2>Best Practices</h2>
-<ol>
-<li><a href="#users">Get Users</a></li>
-<li><a href="#engage">Engage and Retain</a></li>
-<li><a href="#beyond">Beyond the Basics</a></li>
-<li><a href="#related-resources">Related Resources</a></li>
-</ol>
-</div></div>
-
-<p>
- The following best practices have enabled developers worldwide to build
- great, successful games for Google Play.
-</p>
-
-<div class="headerLine">
- <h2 id="users">
- Get Users
- </h2>
-
-
-</div>
-
-<h3>
- 1. Optimize for great ratings
-</h3>
-
-<ul>
- <li>
- <p>
- <a href=
- "{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">Beta
- test</a> to ensure your games are ready and poised for great ratings.
- </p>
- </li>
-
- <li>
- <p>
- Optimize graphics, frame rates, and responsiveness with the <a href=
- "http://android-developers.blogspot.com/2013/09/using-hardware-scaler-for-performance.html">
- Hardware Scaler</a> and <a href=
- "{@docRoot}training/graphics/opengl/index.html">OpenGL ES</a>.
- </p>
- </li>
-
- <li>
- <p>
- Be sure your APK is small, then provide game content through over-the-air
- downloads.
- </p>
- </li>
-</ul>
-
-<h3>
- 2. Build buzz
-</h3>
-
-<ul>
- <li>
- <p>
- Build a community with social media, <a href=
- "{@docRoot}distribute/users/build-community.html">communities</a> to get
- and keep users talking.
- </p>
- </li>
-
- <li>
- <p>
- Promote your games with official <a href=
- "{@docRoot}distribute/tools/promote/badges.html">Google Play badges</a>
- and <a href="{@docRoot}distribute/tools/promote/linking.html">links to
- your products</a> on Google Play.
- </p>
- </li>
-
- <li>
- <p>
- If you ship on multiple platforms, doing so at the same time can maximize
- your marketing impact.
- </p>
- </li>
-</ul>
-
-<h3>
- 3. Get Visibility
-</h3>
-
-<ul>
- <li>
- <p>
- First impressions count: <a href=
- "{@docRoot}distribute/users/your-listing.html">highlight</a> the game's
- best features in screenshots, videos, and description.
- </p>
- </li>
-
- <li>
- <p>
- Integrate Google Play Game Services, so your game is displayed in the
- <a href=
- "https://play.google.com/store/apps/details?id=com.google.android.play.games">
- Google Play Games App</a>.
- </p>
- </li>
-</ul>
-
-<div class="headerLine">
- <h2 id="engage">
- Engage and Retain
- </h2>
-
-
-</div>
-
-<h3>
- 1. Keep users coming back
-</h3>
-
-<ul>
- <li>
- <p>
- <a href=
- "https://developers.google.com/games/services/common/concepts/achievements">
- Achievements</a>, <a href=
- "https://developers.google.com/games/services/common/concepts/leaderboards">
- leaderboards</a>, <a href=
- "https://developers.google.com/games/services/common/concepts/realtimeMultiplayer">
- multiplayer</a>, and <a href=
- "https://developers.google.com/games/services/common/concepts/cloudsave">cloud
- save</a> help engage users and bring them back.
- </p>
- </li>
-
- <li>
- <p>
- Hold tournaments and offer promotions, then announce them through
- <a href="{@docRoot}design/patterns/notifications.html">notifications</a>.
- </p>
- </li>
-
- <li>
- <p>
- Sign in users early, then automatically. Before their first sign-in, save
- progress locally.
- </p>
- </li>
-</ul>
-
-<h3>
- 2. Give users a reason to invest their money
-</h3>
-
-<ul>
- <li>
- <p>
- A majority of the top grossing games use in-app purchases. Use them to
- unlock content and allow players to enhance their game play.
- </p>
- </li>
-
- <li>
- <p>
- <a href="{@docRoot}google/play/billing/index.html">Google Play In-app
- Billing</a> makes purchasing easy with several forms of payment.
- </p>
- </li>
-
- <li>
- <p>
- Provide content updates regularly to give users limited edition items to
- win or purchase.
- </p>
- </li>
-</ul>
-
-<h3>
- 3. Earn players’ love
-</h3>
-
-<ul>
- <li>
- <p>
- <a href=
- "http://android-developers.blogspot.com/2013/10/improved-app-insight-by-linking-google.html">
- Measure</a> your campaigns to see what’s driving quality users to install
- your games.
- </p>
- </li>
-
- <li>
- <p>
- <a href=
- "{@docRoot}distribute/essentials/optimizing-your-app.html#measuring-analyzing-responding">
- Analyze in-game use</a> to steer content updates and prolong the life of your
- games.
- </p>
- </li>
-
- <li>
- <p>
- <a href=
- "http://android-developers.blogspot.com/2013/05/all-google-play-developers-can-now.html">
- Respond to reviews</a> and get valuable feedback from the community
- you’ve built.
- </p>
- </li>
-</ul>
-
-<div class="headerLine">
- <h2 id="beyond">
- Beyond the Basics
- </h2>
-
-
-</div>
-
-<ul>
- <li>
- <p>
- After you've launched in your market of strength, <a href=
- "{@docRoot}distribute/users/expand-to-new-markets.html">expand into other
- markets</a> strategically and <a href=
- "{@docRoot}distribute/tools/localization-checklist.html">localize</a>
- your apps as you go.
- </p>
- </li>
-
- <li>
- <p>
- Provide content <a href=
- "{@docRoot}distribute/engage/app-updates.html">updates on a regular
- basis</a> to keep users engaged.
- </p>
- </li>
-
- <li>
- <p>
- Building educational games? See the <a href=
- "{@docRoot}distribute/essentials/gpfe-guidelines.html">Education
- Guidelines</a>.
- </p>
- </li>
-</ul>
-
-<div class="headerLine">
-<h2 id="related-resources">Related Resources</h2>
-</div>
-
-<div class="resource-widget resource-flow-layout col-13"
- data-query="collection:distribute/toolsreference/bestpractices/games"
- data-sortOrder="-timestamp"
- data-cardSizes="9x3,9x3"
- data-maxResults="6"></div>
diff --git a/docs/html/distribute/essentials/essentials_toc.cs b/docs/html/distribute/essentials/essentials_toc.cs
index fe3fc87..baca18f 100644
--- a/docs/html/distribute/essentials/essentials_toc.cs
+++ b/docs/html/distribute/essentials/essentials_toc.cs
@@ -34,18 +34,7 @@
</a>
</div>
</li>
- <li class="nav-section">
- <div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/essentials/best-practices/apps.html">
- <span class="en">App Best Practices</span>
- </a>
- </div>
- </li>
- <li class="nav-section">
- <div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/essentials/best-practices/games.html">
- <span class="en">Game Best Practices</span>
- </a>
- </div>
- </li>
+</ul>
<script type="text/javascript">
diff --git a/docs/html/distribute/essentials/index.jd b/docs/html/distribute/essentials/index.jd
index ca5442a..d5c3397 100644
--- a/docs/html/distribute/essentials/index.jd
+++ b/docs/html/distribute/essentials/index.jd
@@ -12,23 +12,17 @@
process of monitoring feedback and making improvement after launch.
</p>
-<div class="dynamic-grid">
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/essentials"
data-cardSizes="6x6"
data-maxResults="6">
</div>
-<h3>Related resources</h3>
+<!-- <h2>Related resources</h2>
<div class="resource-widget resource-flow-layout col-16"
- data-query="type:blog+tag:quality"
- data-cardSizes="6x3"
- data-maxResults="3">
-</div>
-<div class="resource-widget resource-flow-layout col-16"
- data-query="type:youtube+tag:appquality"
- data-cardSizes="6x3"
- data-maxResults="3">
-</div>
-</div>
\ No newline at end of file
+ data-query="tag:quality"
+ data-cardSizes="6x2"
+ data-maxResults="3"
+ data-sortOrder="random">
+</div> -->
diff --git a/docs/html/distribute/googleplay/auto.jd b/docs/html/distribute/googleplay/auto.jd
index af24a54..1f5915b 100644
--- a/docs/html/distribute/googleplay/auto.jd
+++ b/docs/html/distribute/googleplay/auto.jd
@@ -1,4 +1,4 @@
-page.title=Distributing to Android Auto
+page.title=Distribute to Android Auto
page.image=/design/auto/images/auto-overview.png
meta.tags="auto", "publish", "quality"
page.tags="auto", "publish", "googleplay"
diff --git a/docs/html/distribute/googleplay/cardboard.jd b/docs/html/distribute/googleplay/cardboard.jd
index c187ffd..d5965d1 100644
--- a/docs/html/distribute/googleplay/cardboard.jd
+++ b/docs/html/distribute/googleplay/cardboard.jd
@@ -1,12 +1,11 @@
page.title=Build VR with Google Cardboard
page.metaDescription=Build apps and games with VR, for a viewer anyone can buy.
-page.image=images/cards/card-cardboard_2x.jpg
page.tags=vr, carboard, games
@jd:body
<p>
Virtual reality promises to transform the way players view games, taking them from a
- flat world into the realm of 3D. And it’s not just games, any application that provides
+ flat world into the realm of 3D. In fact, any application that provides
a way to visually explore has the possibility to offer users more immersive experiences
with VR — like a virtual tour of a famous landmark or a way to visualise atoms in a
chemical compound.
diff --git a/docs/html/distribute/googleplay/cast.jd b/docs/html/distribute/googleplay/cast.jd
index 68c0584..55bc1a9 100644
--- a/docs/html/distribute/googleplay/cast.jd
+++ b/docs/html/distribute/googleplay/cast.jd
@@ -1,13 +1,12 @@
-page.title=Stream Your Content with Google Cast
-page.metaDescription=Let users stream your video and audio content to TVs and speakers.
-page.image=images/cards/card-cast_2x.jpg
+page.title=Stream with Google Cast
+page.metaDescription=Let users stream your video and audio content to their TVs and speakers.
page.tags=cast, video, chromecast
@jd:body
<p>
- The average person spends 3 hours per day watching the TV. With Google Cast
+ With Google Cast
you make it easy for users to include your content as part of their viewing
- schedule. All they need is an Android TV, a TV with a Chromecast plugged in,
+ on TV and listening on audio systems. All they need is an Android TV, a TV with a Chromecast plugged in,
or a Cast for audio device connected to their audio system.
</p>
diff --git a/docs/html/distribute/googleplay/edu/about.jd b/docs/html/distribute/googleplay/edu/about.jd
index 469b899..36a67b26 100644
--- a/docs/html/distribute/googleplay/edu/about.jd
+++ b/docs/html/distribute/googleplay/edu/about.jd
@@ -33,18 +33,20 @@
<img src="{@docRoot}images/gpfe-developer.png">
</div>
- <h3>
- FOR DEVELOPERS
- </h3>
- <b>Get discovered</b>
- <p>
- With Google Play for Education, teachers and administrators can browse
+
+
+<div class="wrap">
+ <div class="cols" style="margin-top:2em;">
+ <div class="col-6of12">
+ <h2 id="maximize_your_ad_revenue">For developers</h2>
+ <img src="{@docRoot}images/gpfe-developer.png">
+ <h5>Get discovered</h5>
+ <p>With Google Play for Education, teachers and administrators can browse
content by curriculum, grade, and standard — discovering the right
content for their students. If your app offers an exciting new way to
learn sixth grade algebra, math educators will be able to find, purchase,
- and distribute your app to their classes in a few clicks.
- </p>
- <b>Reach more schools and students</b>
+ and distribute your app to their classes in a few clicks.</p>
+ <h5>Reach more schools and students</h5>
<p>
Millions of students, faculty, and staff are using Google Apps for
Education and other Google services. Many of these schools are excited to
@@ -52,55 +54,52 @@
looking to bring your apps into their classrooms, especially apps using
Google sign-on.
</p>
- <b>Monetize effectively</b>
+ <h5>Monetize effectively</h5>
<p>
With Google Play for Education, educators are able to make high-volume
purchases using standard institutional payment mechanisms and then
distribute apps to the students who need them — whether it’s a class of
20 or a district of 20,000.
</p>
- </div>
-
- <div style="width:48%; margin-left:2%; float:left;">
- <div class="centered-full-image">
- <img src="{@docRoot}images/gpfe-educator.png">
</div>
- <h3>
- FOR EDUCATORS
- </h3>
- <b>Android tablets in the classroom</b>
+
+ <div class="col-6of12">
+ <h2 id="maximize_your_ad_revenue">For educators</h2>
+ <img src="{@docRoot}images/gpfe-educator.png">
+ <h5> <b>Android tablets in the classroom</h5>
<p>
Google Play for Education brings the innovation of Android technology
into classrooms. School districts can set up and deploy large numbers of
devices in just minutes or hours, rather than days.
</p>
- <b>Curriculum-based discovery</b>
+ <h5>Curriculum-based discovery</h5>
<p>
Powerful browsing tools let educators quickly discover apps, videos, and
other content—with many recommended by teachers and categorized according
to familiar Core Curriculum standards.
</p>
- <b>Bulk purchase with institutional payment</b>
+ <h5>Bulk purchase with institutional payment</h5>
<p>
Convenient purchasing and delivery tools let educators buy apps in bulk,
using purchase orders and other payment methods that are easy for schools
to manage.
</p>
- <b>Over-the-air delivery to student devices</b>
+ <h5>Over-the-air delivery to student devices</h5>
<p>
After finding apps they want, educators can push them instantly to
student devices over the air. They can send the apps to individuals or
groups of any size, across classrooms, schools, or even districts.
</p>
+ <h5>Business-wide licensing</h5>
+ <p>Paid apps are licensed to your organization, enabling you to move paid apps between users as users needs change and staff turns over. </p>
+ </div>
</div>
</div>
-<p style="clear:both">
-</p>
-<div class="headerLine">
+
<h2 id="related-resources">Related Resources</h2>
-</div>
+
<div class="dynamic-grid">
diff --git a/docs/html/distribute/googleplay/families/faq.jd b/docs/html/distribute/googleplay/families/faq.jd
index 9f916a8..363dc91 100644
--- a/docs/html/distribute/googleplay/families/faq.jd
+++ b/docs/html/distribute/googleplay/families/faq.jd
@@ -1,4 +1,4 @@
-page.title=Frequently Asked Questions
+page.title=Google Play for Families FAQ
meta.tags="families", "guidelines", "quality"
page.tags="families", "addendum"
page.metaDescription=Questions and answers about Designed for Families
diff --git a/docs/html/distribute/googleplay/googleplay_toc.cs b/docs/html/distribute/googleplay/googleplay_toc.cs
index 8a321bb..e348ee2 100644
--- a/docs/html/distribute/googleplay/googleplay_toc.cs
+++ b/docs/html/distribute/googleplay/googleplay_toc.cs
@@ -7,7 +7,7 @@
</li>
<li class="nav-section">
<div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/start.html">
- <span class="en">Get Started with Publishing</span>
+ <span class="en">Get Started <br />with Publishing</span>
</a>
</div>
</li>
@@ -19,37 +19,37 @@
</li>
<li class="nav-section">
<div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/guide.html">
- <span class="en">Finding Success on <span style="white-space:nowrap">Google Play</span></span>
+ <span class="en">Find Success on <span style="white-space:nowrap">Google Play</span></span>
</a>
</div>
</li>
<li class="nav-section">
<div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/wear.html">
- <span class="en">Distributing to <span style="white-space:nowrap">Android Wear</span></span>
+ <span class="en">Distribute to <br /><span style="white-space:nowrap">Android Wear</span></span>
</a>
</div>
</li>
<li class="nav-section">
<div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/tv.html">
- <span class="en">Distributing to <span style="white-space:nowrap">Android TV</span></span>
+ <span class="en">Distribute to <br /><span style="white-space:nowrap">Android TV</span></span>
</a>
</div>
</li>
<li class="nav-section">
<div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/auto.html">
- <span class="en">Distributing to <span style="white-space:nowrap">Android Auto</span></span>
+ <span class="en">Distribute to <br /><span style="white-space:nowrap">Android Auto</span></span>
</a>
</div>
</li>
<li class="nav-section">
<div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/cast.html">
- <span class="en">Stream Your Content<span style="white-space:nowrap">with Cast</span></span>
+ <span class="en">Stream Your Content <span style="white-space:nowrap">with Cast</span></span>
</a>
</div>
</li>
<li class="nav-section">
<div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/cardboard.html">
- <span class="en"><span style="white-space:nowrap">Build VR with Cardboard</span></span>
+ <span class="en"><span style="white-space:nowrap">Build VR with Google Cardboard</span></span>
</a>
</div>
</li>
@@ -84,6 +84,12 @@
</a></li>
</ul>
</li>
+ <li class="nav-section">
+ <div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/work/about.html">
+ <span class="en"><span style="white-space:nowrap">Google Play for Work</span></span>
+ </a>
+ </div>
+ </li>
</ul>
<script type="text/javascript">
<!--
diff --git a/docs/html/distribute/googleplay/index.jd b/docs/html/distribute/googleplay/index.jd
index 72e2de8..1908e01 100644
--- a/docs/html/distribute/googleplay/index.jd
+++ b/docs/html/distribute/googleplay/index.jd
@@ -11,9 +11,7 @@
help you gain traction in the marketplace.</span>
</p>
-<div class="dynamic-grid">
-
- <h3>Overview</h3>
+ <h2>Overview</h2>
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/gp/gplanding"
@@ -22,25 +20,20 @@
data-maxResults="3">
</div>
- <h3>Distribute Your Apps</h3>
+ <h2>Distribute Your Apps</h2>
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/gp/gpfelanding"
data-cardSizes="6x6"
- data-maxResults="5">
+ data-maxResults="10">
</div>
- <h3>Related resources</h3>
+<!-- <h2>Related resources</h2>
<div class="resource-widget resource-flow-layout col-16"
- data-query="type:youtube+tag:growth"
- data-cardSizes="6x3"
- data-maxResults="3">
- </div>
- <div class="resource-widget resource-flow-layout col-16"
- data-query="type:blog+tag:googleplay"
- data-cardSizes="6x3"
- data-maxResults="3">
- </div>
+ data-query="tag:growth"
+ data-cardSizes="6x2"
+ data-maxResults="3"
+ data-sortOrder="random">
+ </div> -->
-</div>
diff --git a/docs/html/distribute/googleplay/tv.jd b/docs/html/distribute/googleplay/tv.jd
index 37cbe26..a35edbc 100644
--- a/docs/html/distribute/googleplay/tv.jd
+++ b/docs/html/distribute/googleplay/tv.jd
@@ -1,4 +1,4 @@
-page.title=Distributing to Android TV
+page.title=Distribute to Android TV
page.image=/design/tv/images/atv-home.jpg
meta.tags="tv", "publish", "quality"
page.tags="tv", "publish", "googleplay"
diff --git a/docs/html/distribute/googleplay/wear.jd b/docs/html/distribute/googleplay/wear.jd
index c0a0017..7661016 100644
--- a/docs/html/distribute/googleplay/wear.jd
+++ b/docs/html/distribute/googleplay/wear.jd
@@ -1,4 +1,4 @@
-page.title=Distributing to Android Wear
+page.title=Distribute to Android Wear
page.image=/design/media/wear/ContextualExample.008_2x.png
meta.tags="wear", "publish", "quality"
page.tags="wear", "publish", "googleplay"
diff --git a/docs/html/distribute/googleplay/work/about.jd b/docs/html/distribute/googleplay/work/about.jd
new file mode 100644
index 0000000..6ced561
--- /dev/null
+++ b/docs/html/distribute/googleplay/work/about.jd
@@ -0,0 +1,95 @@
+page.title=Google Play for Work
+page.metaDescription=Distribute your apps directly to enterprises and business users.
+page.tags="enterprise", "emm", "business", "administrator"
+page.image=images/distribute/android-work.jpg
+@jd:body
+
+<p>Google Play for Work is an extension of Google Play that lets Android for Work users browse and install apps. IT admins in a business using Android for Work choose which public and private apps are made available to users in their business. Businesses can use Google Play for Work to securely bulk deploy free apps to their employees or bulk purchase paid apps depending on their needs.</p>
+
+<p>As a Google Play developer, your free apps are automatically ready to be selected by Android for Work customers and made available to their workforces on Google Play for Work. However, to allow businesses access to bulk purchase your paid apps, you must opt-in and agree to the <a href="https://play.google.com/about/work/developer-distribution-agreement-addendum.html">Google Play for Work Addendum</a> to the Developer Distribution Agreement.</p>
+
+<p>Find out more about <a href="">distributing to Google Play for Work</a>.</p>
+
+<div class="wrap">
+ <div class="cols" style="margin-top:2em;">
+ <div class="col-6of12">
+ <h2 id="maximize_your_ad_revenue">For developers</h2>
+ <img src="{@docRoot}images/distribute/gpfw_developer.png">
+ <h5>Get discovered</h5>
+ <p>Get your business related apps listed in a business specific gateway so they stand out from consumer apps.</p>
+ <h5>Get volume</h5>
+ <p>Reach new audiences at scale because businesses will be able to deploy your free apps in bulk. Bulk purchasing also allows businesses to buy your paid app at scale.</p>
+ <h5>Generate revenue from extended support</h5>
+ <p>Businesses will often look for extended support for business critical apps, and you have the opportunity to offer that support for a fee.</p>
+ <h5>Continue to offer in app-purchases</h5>
+ <p>You can continue to offer free apps with in-app purchases. Business employees will be able to make purchases just as they would if they personally installed your app. </p>
+ </div>
+
+
+ <div class="col-6of12">
+ <h2 id="maximize_your_ad_revenue">For businesses</h2>
+ <img src="{@docRoot}images/distribute/gpfw_business.png">
+ <h5>Free to businesses</h5>
+ <p>Google Play for Work is available free of charge to Android for Work customers.</p>
+ <h5>Full IT approval for apps</h5>
+ <p>IT can now approve and manage every app deployed to its organization’s workers.</p>
+ <h5>Secure app distribution</h5>
+ <p>Provides a secure gateway for distributing apps within your company, whether they are available publicly or distributed privately to employees at your organization.</p>
+ <h5>Work app configuration</h5>
+ <p>Maintain app settings, such as server addresses and default user settings, from your admin console for enabled apps.</p>
+ <h5>Business-wide licensing</h5>
+ <p>Paid apps are licensed to your organization, enabling you to move paid apps between users as users needs change and staff turns over. </p>
+ </div>
+ </div>
+</div>
+
+<h2 id="best_practices">Best practices for success</h2>
+
+<p>Keep these best practices in mind as you build a great enterprise-ready app.</p>
+
+<h3 id="design">Build a great app for business</h3>
+
+<ul>
+ <li>Follow best practices for security and manage user data properly. Businesses
+ are more conscious of data security and employee productivity, especially when it
+ comes to features that share information with other services.</li>
+ <li>Support the Work App Configuration framework to let an administrator remotely configure app settings such as:
+ <ul>
+ <li>Server address and protocol settings</li>
+ <li>The ability to switch features on and off</li>
+ <li>Sign-in credentials for your app's backend servers</li>
+ <li>Default user settings</li>
+ </ul>
+ </li>
+ <li>Request the minimum permissions that your app needs.</li>
+ <li>Make sure communication to your backend and data in your backend is secure.</li>
+ <li>Implement authorization policies that will minimize the number of your employees that can access user data.</li>
+ <li><a href={@docRoot}training/enterprise/app-compatibility.html">Offer compatibility with managed profile</a> and test that with the <a href="{@docRoot}samples/BasicManagedProfile/index.html">BasicManagedProfile sample app</a>.</li>
+ <li>Support <a href="{@docRoot}training/enterprise/app-restrictions.html">app restrictions</a> so that IT admins can remotely configure your app through leading EMM solutions.</li>
+</ul>
+
+<!--<h3 id="distribute">Distribute</h3>
+
+<p>Free to install apps are automatically made available through Google Play for Work, but for paid apps:</p>
+
+<ul>
+<li>When you <a href="https://support.google.com/googleplay/android-developer/answer/113469">publish your app through the Developer Console</a>, opt in to distribute through Google Play for Work, so you agree to your app’s licenses being transferrable within a single business customer.</li>
+<li>Use the <a href="{@docRoot}}google/play/licensing/index.html">Licensing API to track which users have licenses assigned.</li>
+</ul> -->
+
+
+<h3 id="support">Provide support and maintenance</h3>
+
+<ul>
+<li>Consider offering enhanced support to cover extended hours or specific means of contact. Businesses are often willing to pay for this service.</li>
+<li>If you update the App Configuration / App Restrictions schema for your app, make sure it remains backward compatible. This is because it’s possible that different users will have different versions of your app (at least temporarily), and IT admin will want a consistent remote configuration experience between versions to ensure efficient management of apps in the field.</li>
+</ul>
+
+<h2 id=related_resources>Related resources</h2>
+
+<div class="resource-widget resource-flow-layout col-13"
+ data-query="collection:distribute/googleplay/gpfw"
+ data-sortOrder="-timestamp"
+ data-cardSizes="9x3"
+ data-maxResults="6"></div>
+
diff --git a/docs/html/distribute/index.jd b/docs/html/distribute/index.jd
index ce64445..d3f3836 100644
--- a/docs/html/distribute/index.jd
+++ b/docs/html/distribute/index.jd
@@ -22,7 +22,7 @@
data-query="type:youtube+tag:googleplay+tag:developerstory+tag:featured, type:blog+tag:googleplay+tag:distribute+tag:featured"
data-sortOrder="-timestamp"
data-cardSizes="6x6"
- data-maxResults="6"></div>
+ data-maxResults="3"></div>
</div></section>
<section class="dac-section dac-invert dac-darken-bg" style="background-image: url(/images/distribute/google-play-bg.jpg)"><div class="wrap">
@@ -64,7 +64,11 @@
</a></li>
<li class="dac-section-link"><a href="/distribute/monetize/index.html">
<span class="dac-sprite dac-auto-chevron"></span>
- Monetize
+ Earn
+ </a></li>
+ <li class="dac-section-link"><a href="/distribute/analyze/index.html">
+ <span class="dac-sprite dac-auto-chevron"></span>
+ Analyze
</a></li>
</ul>
</div></section>
@@ -77,10 +81,10 @@
<div class="resource-widget resource-flow-layout col-16"
data-query="collection:distribute/landing/more"
data-cardSizes="6x6"></div>
- <ul class="dac-section-links">
+<!-- <ul class="dac-section-links">
<li class="dac-section-link"><a href="https://developers.google.com/">
<span class="dac-sprite dac-auto-chevron"></span>
More Google services for Android
</a></li>
- </ul>
+ </ul> -->
</div></section>
diff --git a/docs/html/distribute/monetize/ads.jd b/docs/html/distribute/monetize/ads.jd
index 8f6c8b0..c15f2bb 100644
--- a/docs/html/distribute/monetize/ads.jd
+++ b/docs/html/distribute/monetize/ads.jd
@@ -25,89 +25,88 @@
</ul>
<p><a href="http://www.google.com/ads/admob/#subid=us-en-et-dac">Sign-up for AdMob</a>
-today and start showing ads by integrating the <a
-href="https://developers.google.com/mobile-ads-sdk/download">Google Mobile Ads SDK</a>
+today and start using the <a
+href="https://developer.android.com/google/play-services/ads.html">Google Mobile Ads SDK</a>
in your app with a few lines of code.</p>
-<h2 id="key_features">Key features</h2>
+<h2 id="maximize_your_ad_revenue">Maximize your ad revenue</h2>
-<div style="display:inline-block">
-<h3 id="maximize_your_ad_revenue">Maximize your ad revenue</h3>
-
-<div class="col-4">
- <h4 id="maximize_earnings">Maximize earnings</h4>
- <p>Earn more with our industry-leading ad service, which includes <a href=
- "https://support.google.com/admob/answer/3063564">free mediation</a> to
- automatically improve your earnings, and access to all of Google’s advertiser
- demand from AdMob, AdWords, and the DoubleClick Ad Exchange.</p>
+<div class="wrap">
+ <div class="cols" style="margin-top:2em;">
+ <div class="col-4of12">
+ <h5 id="maximize_earnings">Maximize earnings</h5>
+ <p>Earn more with our industry-leading ad service, which includes <a href=
+ "https://support.google.com/admob/answer/3063564">free mediation</a> to
+ automatically improve your earnings, and access to all of Google’s advertiser
+ demand from AdMob, AdWords, and the DoubleClick Ad Exchange.</p>
+ </div>
+ <div class="col-4of12">
+ <h5 id="get_paid_fast">Get paid fast</h5>
+ <p>Get paid in local currencies quickly and reliably, with no wire fees charged by
+ AdMob.</p>
+ </div>
+ <div class="col-4of12">
+ <h5 id="easy_and_free">Easy and free</h5>
+ <p>The SDK can be installed quickly, and there are no standard fees for using the
+ platform.</p>
+ </div>
+ </div>
</div>
-<div class="col-4">
- <h4 id="get_paid_fast">Get paid fast</h4>
- <p>Get paid in local currencies quickly and reliably, with no wire fees charged by
- AdMob.</p>
+<h2 id="grow_your_business_with_a_trusted_partner">Grow your business with a trusted partner</h2>
+<div class="wrap">
+ <div class="cols" style="margin-top:2em;">
+ <div class="col-6of12">
+ <h5 id="powered_by_googles_ad_technology">Powered by Google’s ad technology</h5>
+ <p>For over a decade, Google has helped millions of developers grow their digital
+ businesses.</p>
+ </div>
+ <div class="col-6of12">
+ <h5 id="auto_updates_on_google_play">Auto updates on Google Play</h5>
+ <p>AdMob’s integration with Google Play services pushes automatic performance
+ improvements to Android apps without additional SDK changes.</p>
+ </div>
+ </div>
</div>
-<div class="col-4">
- <h4 id="easy_and_free">Easy and free</h4>
- <p>The SDK can be installed quickly, and there are no standard fees for using the
- platform.</p>
-</div>
-</div>
-<div style="display:inline-block">
-<h3 id="grow_your_business_with_a_trusted_partner">Grow your business with a trusted partner</h3>
-
-<div class="col-6">
- <h4 id="powered_by_googles_ad_technology">Powered by Google’s ad technology</h4>
- <p>For over a decade, Google has helped millions of developers grow their digital
- businesses.</p>
-</div>
-
-<div class="col-6">
-<h4 id="auto_updates_on_google_play">Auto updates on Google Play</h4>
-<p>AdMob’s integration with Google Play services pushes automatic performance
- improvements to Android apps without additional SDK changes.</p>
-</div>
-</div>
-
-<div style="display:inline-block">
-<h3 id="drive_more_in-app_purchases_and_downloads">Drive more in-app purchases and downloads</h3>
-
-<div class="col-6">
-<h4 id="sell_more_in-app_purchases">Sell more in-app purchases</h4>
+<h2 id="drive_more_in-app_purchases_and_downloads">Drive more in-app purchases and downloads</h2>
+<div class="wrap">
+ <div class="cols" style="margin-top:2em;">
+ <div class="col-6of12">
+<h5 id="sell_more_in-app_purchases">Sell more in-app purchases</h5>
<p>Earn more revenue by intelligently promoting your in-app purchases to the users
most likely to buy them.</p>
-</div>
-
-<div class="col-6">
-<h4 id="promote_your_apps_for_free">Promote your apps for free</h4>
+ </div>
+ <div class="col-6of12">
+<h5 id="promote_your_apps_for_free">Promote your apps for free</h5>
<p>Cross-sell your other apps (or your friend’s apps) to your existing users,
using free AdMob <a href="https://support.google.com/admob/answer/3210452">house ads</a>.</p>
-</div>
+ </div>
+ </div>
</div>
-<div style="display:inline-block">
-<h3 id="drive_more_in-app_purchases_and_downloads">Drive more in-app purchases and downloads</h3>
-
-<div class="col-6">
-<h4 id="analytics_for_apps">Analytics for apps</h4>
+<h2 id="understand_users">Understand your users</h2>
+<div class="wrap">
+ <div class="cols" style="margin-top:2em;">
+ <div class="col-6of12">
+<h5 id="analytics_for_apps">Analytics for apps</h5>
<p>Analyze your app’s performance from within AdMob with Google Analytics.
Discover where people are downloading your app, and the features they use the
most in real time.</p>
-</div>
-
-<div class="col-6">
-<h4 id="flow_visualization_reports">Flow visualization reports</h4>
+ </div>
+ <div class="col-6of12">
+<h5 id="flow_visualization_reports">Flow visualization reports</h5>
<p>In Analytics, see how people are navigating through your app with graphical
<a href="https://support.google.com/analytics/answer/2519986">flow reports</a>.
View the path they take to making a purchase, and the point where they exit
the app, plus much more.</p>
-</div>
+ </div>
+ </div>
</div>
-<h2 id=tips>Tips</h2>
+<div style="<h2 id=tips>Tips</h2>
<ul>
<li> Place ads wisely, they shouldn't be too intrusive but still need to be clearly
diff --git a/docs/html/distribute/monetize/index.jd b/docs/html/distribute/monetize/index.jd
index 7350a24..dc5c704 100644
--- a/docs/html/distribute/monetize/index.jd
+++ b/docs/html/distribute/monetize/index.jd
@@ -1,4 +1,4 @@
-page.title=Monetize
+page.title=Earn
section.landing=true
nonavpage=true
@@ -17,20 +17,17 @@
help you track where your money is coming from.
</p>
-<div class="dynamic-grid">
-
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/monetize"
data-cardSizes="6x6"
data-maxResults="9">
</div>
-<h3>Related resources</h3>
+<!-- <h2>Related resources</h2>
<div class="resource-widget resource-flow-layout col-16"
data-query="tag:monetizing"
- data-sortOrder="-timestamp"
- data-cardSizes="6x3"
- data-maxResults="6">
- </div>
-</div>
+ data-cardSizes="6x2"
+ data-sortOrder="random"
+ data-maxResults="3">
+ </div> -->
\ No newline at end of file
diff --git a/docs/html/distribute/monetize/payments.jd b/docs/html/distribute/monetize/payments.jd
index 55c289f..187f872 100644
--- a/docs/html/distribute/monetize/payments.jd
+++ b/docs/html/distribute/monetize/payments.jd
@@ -1,5 +1,5 @@
page.title=Convenient, Frictionless Purchasing
-page.image=/distribute/images/payment-method.jpg
+page.image=
page.metaDescription=Users can purchase instantly with a choice of payment methods.
page.tags="google play", "payments", "gift card"
diff --git a/docs/html/distribute/tools/index.jd b/docs/html/distribute/tools/index.jd
index c8f0212..24c3398 100644
--- a/docs/html/distribute/tools/index.jd
+++ b/docs/html/distribute/tools/index.jd
@@ -9,49 +9,39 @@
users, and monetize your investment.
</p>
-<div class="dynamic-grid">
-
- <h3>Publishing and Launch</h3>
+ <h2>Publishing and Launch</h2>
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/tools/checklists"
data-cardSizes="9x6"
data-maxResults="2">
</div>
-<h3>Marketing Tools</h3>
+ <h2>Marketing Tools</h2>
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/tools/promote"
data-cardSizes="6x6"
data-maxResults="3">
</div>
- <h3>Developer Support</h3>
+ <h2>Developer Support</h2>
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/tools/support"
data-cardSizes="6x6"
data-maxResults="3">
</div>
- <h3>Developer News</h3>
+ <h2>Developer News</h2>
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/tools/news"
data-cardSizes="9x6"
data-maxResults="2">
</div>
- <h3>More</h3>
+ <h2>More</h2>
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/tools/more"
data-cardSizes="6x6"
data-maxResults="3">
</div>
-<!-- <h3>Related Resources</h3>
- <div class="resource-widget resource-stack-layout col-16"
- data-query="tag:developersupport"
- data-sortOrder="-timestamp"
- data-numStacks="3"
- data-maxResults="6">
- </div> -->
-</div>
diff --git a/docs/html/distribute/users/house-ads.jd b/docs/html/distribute/users/house-ads.jd
new file mode 100644
index 0000000..d662fb2
--- /dev/null
+++ b/docs/html/distribute/users/house-ads.jd
@@ -0,0 +1,61 @@
+page.title=Cross-Sell to Users with House Ads
+page.metaDescription=Tap into your existing user base to increase downloads and increase conversions.
+page.tags="google", "identity", "signin"
+page.image=distribute/images/advertising.jpg
+
+@jd:body
+
+<p>One of the fastest ways to accumulate downloads or increase conversions is to tap into your
+existing user base. These users know your products and are a receptive audience for your other
+apps and in-app products.</p>
+
+<h3>Promote your apps for free</h3>
+
+<p>AdMob's house ads let you cross-sell your other apps (or your friend’s apps) to your
+existing users, and it's a free service.</p>
+
+<h3>Sell more in-app purchases</h3>
+
+<p>Intelligently promote your in-app purchases to the users most likely to buy them, with
+AdMob’s free in-app purchase house ad format.</p>
+
+<p>Get started <a href="https://developers.google.com/identity/sign-in/">integrating
+Google sign-in into your apps and games</a>.</p>
+
+<div class="wrap">
+ <div class="cols" style="margin:1em auto;">
+ <div class="col-8of12">
+ <img src="{@docRoot}images/distribute/house-ads.png" style="padding-top:1em;">
+ </div>
+ </div>
+</div>
+
+<p><a href="http://www.google.com/ads/admob/#subid=us-en-et-dac">Sign-up for AdMob</a> today
+and start using the <a href="https://developer.android.com/google/play-services/ads.html">Google
+Mobile Ads SDK</a> included in Google Play services to show ads in your app with a few lines of
+code. Then create your <a href="https://support.google.com/admob/answer/3210442?hl=en">house
+ad campaigns</a>.</p>
+
+<h2>Tips</h2>
+
+<ul>
+ <li>AdMob automatically figures out which of your users are likely to spend to optimize which
+ users see the ads.</li>
+ <li>Place ads wisely, they shouldn't be too intrusive but still need to be clearly visible to
+ attract clickthroughs.</li>
+ <li>Remember that ads form part of your app and must match its age rating.</li>
+ <li>Use the impression goals feature of AdMob house ads to set limits on the number of ads
+ served. This is useful if you want to run ad campaigns in your app from other developers.</li>
+</ul>
+
+
+<h2 style="clear:both" id="related-resources">Related Resources</h2>
+
+<div class="resource-widget resource-flow-layout col-13"
+ data-query="collection:distribute/users/houseads"
+ data-sortorder="-timestamp"
+ data-cardsizes="6x3"
+ data-maxresults="6">
+</div>
+
+
diff --git a/docs/html/distribute/users/index.jd b/docs/html/distribute/users/index.jd
index a810f36..a3f8d01 100644
--- a/docs/html/distribute/users/index.jd
+++ b/docs/html/distribute/users/index.jd
@@ -10,21 +10,23 @@
developers. These best practices are critical to your app or game’s success.
</p>
-<div class="dynamic-grid">
-
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/users"
data-cardSizes="6x6"
data-maxResults="6">
</div>
+<div class="resource-widget resource-flow-layout landing col-16"
+ data-query="collection:distribute/users"
+ data-cardSizes="9x3"
+ data-maxResults="16">
+</div>
-<h3>Related resources</h3>
+<!-- <h2>Related resources</h2>
<div class="resource-widget resource-flow-layout col-16"
- data-query="type:youtube+tag:users,tag:global,type:blog+tag:users"
- data-sortOrder="-timestamp"
- data-cardSizes="6x3"
- data-maxResults="6">
- </div>
-
-</div>
+ data-query="tag:users"
+ data-sortOrder="random"
+ data-cardSizes="6x2"
+ data-maxResults="3">
+ </div> -->
+
diff --git a/docs/html/distribute/users/ota-installs.jd b/docs/html/distribute/users/ota-installs.jd
new file mode 100644
index 0000000..f703257
--- /dev/null
+++ b/docs/html/distribute/users/ota-installs.jd
@@ -0,0 +1,51 @@
+page.title=Offer Over-the-air Installs
+page.metaDescription=Let users send your app directly to their devices when they sign in with Google.
+page.tags="google", "identity", "installs"
+page.image=images/cards/google-sign-in_2x.png
+
+
+@jd:body
+
+<p>Google sign-in is a trusted registration system that's familiar to users and
+consistent across devices. With minimal effort, you can improve your sign-in conversion
+with a fast and secure authentication option for users. And by using Google sign-in,
+you can offer users the option to send your app directly to their Android devices when
+they sign-in, without the need for them to visit the Play store. Using this approach,
+some developers have seen app installation acceptance rates of 40%.</p>
+
+<p>Get started with <a href="https://developers.google.com/identity/sign-in/android/">
+Google sign-in for Android</a> and then enable <a
+href="https://developers.google.com/identity/sign-in/web/android-app-installs">over-the-air
+installs</a> for your web site.</p>
+
+<div class="wrap">
+ <div class="cols" style="margin-top:2em;">
+ <div class="col-8of12 col-push-1of12">
+ <img src="{@docRoot}images/distribute/ota-installs.gif">
+ </div>
+ </div>
+</div>
+
+
+<h2>Tips</h2>
+
+<ul>
+ <li>Adding Google sign-in to your app can increase conversions by reducing the burden
+ and friction of sign-in, while helping users keep their accounts secure.</li>
+ <li>Focus on the quality of your app to ensure that it passes the quality threshold for
+ over-the-air installation.</li>
+ <li>Measure impressions of the Android install prompt, installs, and success rate by
+ day, week, and month with Platform Insights.</li>
+</ul>
+
+
+<h2 style="clear:both" id="related-resources">Related Resources</h2>
+
+<div class="resource-widget resource-flow-layout col-13"
+ data-query="collection:distribute/users/otas"
+ data-sortorder="-timestamp"
+ data-cardsizes="9x3"
+ data-maxresults="4">
+</div>
+
+
diff --git a/docs/html/distribute/users/promote-with-ads.jd b/docs/html/distribute/users/promote-with-ads.jd
index d71b8c9..3456c66 100644
--- a/docs/html/distribute/users/promote-with-ads.jd
+++ b/docs/html/distribute/users/promote-with-ads.jd
@@ -11,56 +11,67 @@
<h2 id=drive_installs>Drive installs</h2>
-<p><a href="http://adwords.google.com">AdWords</a> promotes your app to interested users where they spend time on phones and
+<p><a href="http://adwords.google.com">AdWords</a> promotes your app to interested
+users where they spend time on phones and
tablets – with app install ads on Google Search, YouTube, Gmail, and within
apps and across the web on the Google Display Network. AdWords is a powerful
way to scale app promotion across Google networks and find customers that are
most likely to install your app. </p>
-<p><a href="https://support.google.com/adwords/answer/6032059">Get started with AdWords app install ads</a>.</p>
+<p><a href="https://support.google.com/adwords/answer/6032059">Get started with AdWords
+app install ads</a>.</p>
-<div style="display:inline-block">
- <div class="figure-left" style="width:40%;">
+
+
+<div class="wrap">
+ <div class="cols" style="margin-top:1em;">
+ <div class="col-4of12">
<h3>From Google Play</h3>
- <img src="/images/distribute/promote_ads_play.png">
- <p class="figure-caption">Search ads on Google Play are still undergoing testing and not yet available to
-buy. <a href="http://android-developers.blogspot.com/2015/02/a-new-way-to-promote-your-app-on-google.html">Find out more</a>.</p>
- </div>
- <div class="figure-right" style="width:40%;">
- <h3>From apps</h3>
+ <img src="/images/distribute/promote_ads_play.png">
+ <p class="figure-caption">Search ads on Google Play are still undergoing testing and
+ not yet available to buy. <a
+ href="http://android-developers.blogspot.com/2015/02/a-new-way-to-promote-your-app-on-google.html">Find
+ out more</a>.</p>
+ </div>
+ <div class="col-4of12">
+ <h3>From search</h3>
<img src="/images/distribute/promote_ads_search.png">
- <p class="figure-caption">Connect with users as they search for content and services provided by your
-app.</p>
- </div>
-</div>
-
-<div style="display:inline-block">
- <div class="figure-left" style="width:40%;">
+ <p class="figure-caption">Connect with users as they search for content and services
+ provided by your app.</p>
+ </div>
+ <div class="col-4of12">
<h3>From YouTube</h3>
<img src="/images/distribute/promote_ads_youtube.png">
<p class="figure-caption">Promote your app when users are watching related videos.</p>
+ </div>
</div>
- <div class="figure-right" style="width:40%;">
+</div>
+
+<div class="wrap">
+ <div class="cols" style="margin-top:1em;">
+ <div class="col-4of12">
<h3>From apps</h3>
<img src="/images/distribute/promote_ads_apps.png">
- <p class="figure-caption">Reach users while they’re engaged with apps and games across the AdMob network.</p>
- </div>
-</div>
-
-<div style="display:inline-block">
- <div class="figure-left" style="width:40%;">
+ <p class="figure-caption">Reach users while they’re engaged with apps and games across the
+ AdMob network.</p>
+ </div>
+ <div class="col-4of12">
<h3>From the web</h3>
<img src="/images/distribute/promote_ads_web.png">
- <p class="figure-caption">Reach users while they’re engaged with websites across the Google Display Network.</p>
- </div>
- <div class="figure-right" style="width:40%;">
+ <p class="figure-caption">Reach users while they’re engaged with websites across the Google
+ Display Network.</p>
+ </div>
+ <div class="col-4of12">
<h3>From Gmail</h3>
<img src="/images/distribute/promote_ads_gmail.png">
- <p class="figure-caption">Promote your app while users communicate and get things done in Gmail.</p>
+ <p class="figure-caption">Promote your app while users communicate and get things done in
+ Gmail.</p>
+ </div>
</div>
</div>
-<h3>Tips</h3>
+
+<h2>Tips</h3>
<ul>
<li> Estimate how much an app user is worth to your business, so that you can work
@@ -89,21 +100,25 @@
remind them of key features and encourage them to try your app again, or help
them complete an activity they didn't know your app could handle.</p>
-<div>
- <div class="figure-left" style="width:46%;">
+
+<div class="wrap">
+ <div class="cols" style="margin-top:1em;">
+ <div class="col-4of12">
<h3>From search</h3>
<img src="/images/distribute/promote_ads.png">
<p class="figure-caption">Add deep links to your app, then bring users straight
to relevant app content when they’re searching.</p>
- </div>
- <div class="figure-right" style="width:46%;">
+ </div>
+ <div class="col-4of12">
<h3>From apps</h3>
<img src="/images/distribute/promote_ads_inapp.png">
<p class="figure-caption">Use remarketing and deep links to bring users to just the right
place in your app to re-engage and convert, from other apps and games they love.</p>
+ </div>
</div>
</div>
+
<h3>Tips</h3>
<ul>
diff --git a/docs/html/distribute/users/users_toc.cs b/docs/html/distribute/users/users_toc.cs
index 2e796c8..3aa3fe1 100644
--- a/docs/html/distribute/users/users_toc.cs
+++ b/docs/html/distribute/users/users_toc.cs
@@ -29,6 +29,24 @@
</div>
</li>
<li class="nav-section">
+ <div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/users/ota-installs.html">
+ <span class="en">Offer Over-the-air Installs</span>
+ </a>
+ </div>
+ </li>
+ <li class="nav-section">
+ <div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/users/house-ads.html">
+ <span class="en">Cross-Sell to Users with House Ads</span>
+ </a>
+ </div>
+ </li>
+ <li class="nav-section">
+ <div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/users/youtube.html">
+ <span class="en">Drive installs from YouTube</span>
+ </a>
+ </div>
+ </li>
+ <li class="nav-section">
<div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/users/build-buzz.html">
<span class="en">Build Buzz</span>
</a>
diff --git a/docs/html/distribute/users/youtube.jd b/docs/html/distribute/users/youtube.jd
new file mode 100644
index 0000000..0be6584
--- /dev/null
+++ b/docs/html/distribute/users/youtube.jd
@@ -0,0 +1,34 @@
+page.title=Drive installs from YouTube
+page.metaDescription=Bring users from videos to your store listing with a merchandise card available on YouTube.
+page.image=images/cards/card-youtube_2x.png
+page.tags="users, youtube, cards, videos"
+@jd:body
+
+<p>Now you can bring users who discover your app videos on YouTube to your store listing with a merchandise card, one of several card types available to add to YouTube videos. Once published the presence of the merchandise card is indicated in the video with an information icon. When a viewer opens the card they follow the link to your app on the Play Store, where they can install your app.</p>
+
+<div class="wrap" style="margin:2em auto">
+ <div class="cols">
+ <div class="col-9of12">
+ <img src="{@docRoot}images/distribute/youtube-card-example.png">
+ </div>
+ </div>
+</div>
+
+<p>Get started <a href="https://support.google.com/youtube/answer/6140493">adding merchandise cards to your uploaded videos</a>.</p>
+
+<h2>Tips</h2>
+<ul>
+<li>Don’t indicate the location of a card within your video, cards might be positioned differently on different devices.</li>
+<li>When you add cards, featured video or playlist highlights in your video are hidden. </li>
+<li>In order to display cards, your account needs to be in good standing.</li>
+<li>Performance reporting for cards is available in YouTube Analytics.</li>
+<li>Cards work better when they're not too close to each other. Try spacing them out.</li>
+</ul>
+
+<h2 id="related-resources">Related resources</h2>
+
+<div class="resource-widget resource-flow-layout col-13"
+ data-query="collection:distribute/users/youtube"
+ data-sortOrder="-timestamp"
+ data-cardSizes="9x3"
+ data-maxResults="6"></div>
diff --git a/docs/html/google/gcm/c2dm.jd b/docs/html/google/gcm/c2dm.jd
deleted file mode 100644
index bc58e66..0000000
--- a/docs/html/google/gcm/c2dm.jd
+++ /dev/null
@@ -1,119 +0,0 @@
-page.title=Migration
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
-
-<h2>Quickview</h2>
-
-<ul>
-<li>Understand the differences between GCM and C2DM.</li>
-<li>Learn how to migrate an app from C2DM to GCM.</li>
-
-</ul>
-
-
-<h2>In this document</h2>
-
-<ol>
-<li><a href="#history">Historical Overview</a></li>
-<li><a href="#diffs">How is GCM Different from C2DM?</a>
- <ol>
- <li><a href="#interop">Relationship between C2DM and GCM</a></li>
- </ol>
-</li>
-<li><a href="#migrating">Migrating Your Apps</a>
- <ol>
- <li><a href="#client">Client changes</a></li>
- <li><a href="#server">Server changes</a></li>
- </ol>
-</li>
-</ol>
-
-</div>
-</div>
-
-<p>Android Cloud to Device Messaging (C2DM) was officially deprecated on June 26, 2012, and will be
- shut down completely as of July 30, 2015. <strong>C2DM developers are strongly encouraged to move
- to Google Cloud Messaging (GCM)</strong>. GCM is the next generation of C2DM.</p>
-
-<p>This document is addressed to C2DM developers who are moving to GCM. It describes the differences between GCM and C2DM, and explains how to migrate existing C2DM apps to GCM.</p>
-
-
-<h2 id="history">Historical Overview</h2>
-<p>C2DM was launched in 2010 to help Android apps send data from servers to their applications. Servers can tell apps to contact the server directly, to fetch updated application or user data. The C2DM service handles all aspects of queueing of messages and delivery to the target application running on the target device.</p>
-<p>GCM replaces C2DM. The focus of GCM is as follows:</p>
-<ul>
- <li> Ease of use. No sign-up forms.</li>
- <li>No quotas.</li>
- <li>GCM and C2DM stats are available through the <a href="http://play.google.com/apps/publish">Developer Console</a>.</li>
- <li>Battery efficiency.</li>
- <li>Rich set of new APIs.</li>
-</ul>
-<h2 id="diffs">How is GCM Different from C2DM?</h2>
-<p>GCM builds on the core foundation of C2DM. Here is what's different:</p>
-
-<dl>
-<dt><strong>Simple API Key</strong></dt>
-<dd>To use the GCM service, you need to obtain a Simple API Key from Google APIs console page. For more information, see <a href="gs.html">Getting Started</a>. Note that GCM <em>only</em> accepts Simple API Key—using ClientLogin or OAuth2 tokens will not work.
-</dd>
-<dt><strong>Sender ID</strong></dt>
-<dd>In C2DM, the Sender ID is an email address. In GCM, the Sender ID is a project number that you acquire from the API console, as described in <a href="gs.html#create-proj">Getting Started</a>. </dd>
-
-<dt><strong>JSON format</strong></dt>
-<dd>GCM HTTP requests support JSON format in addition to plain text. For more information, see the <a href="gcm.html#send-msg">Architectural Overview</a>.</dd>
-
-<dt><strong>Multicast messages</strong></dt>
-<dd>In GCM you can send the same message to multiple devices simultaneously. For example, a sports app wanting to deliver a score update to fans can now send the message to up to 1000 registration IDs in the same request (requires JSON). For more information, see the <a href="gcm.html#send-msg">Architectural Overview</a>.</dd>
-
-<dt><strong>Multiple senders</strong></dt>
-<dd>Multiple parties can send messages to the same app with one common registration ID. For more information, see <a href="adv.html#multi-senders">Advanced Topics</a>.</dd>
-
-<dt><strong>Time-to-live messages</strong></dt>
-<dd>Apps like video chat and calendar apps can send expiring invitation events with a time-to-live value between 0 and 4 weeks. GCM will store the messages until they expire. A message with a time-to-live value of 0 will not be stored on the GCM server, nor will it be throttled. For more information, see <a href="adv.html#ttl">Advanced Topics</a>.</dd>
-
-<dt><strong>Messages with payload</strong></dt>
-<dd>Apps can use "messages with payload" to deliver messages of up to 4 Kb. This would be useful in a chat application, for example. To use this feature, simply omit the <code>collapse_key</code> parameter and messages will not be collapsed. GCM will store up to 100 messages. If you exceed that number, all messages will be discarded but you will receive a special message. If an application receives this message, it needs to sync with the server. For more information, see <a href="adv.html#collapsible">Advanced Topics</a>.</dd>
-
-<dt><strong>Canonical registration ID</strong></dt>
-<dd>There may be situations where the server ends up with 2 registration IDs for the same device. If the GCM response contains a registration ID, simply replace the registration ID you have with the one provided. With this feature your application doesn't need to send the device ID to your server anymore. For more information, see <a href="adv.html#canonical">Advanced Topics</a>.</dd>
-</dl>
-
-<h3 id="interop">Relationship between C2DM and GCM</h3>
-
-<p>C2DM and GCM are not interoperable. For example, you cannot post notifications from GCM to C2DM registration IDs, nor can you use C2DM registration IDs as GCM registration IDs. From your server-side application, you must keep track of whether a registration ID is from C2DM or GCM and use the proper endpoint. </p>
-
-<p>As you transition from C2DM to GCM, your server needs to be aware of whether a given registration ID
-contains an old C2DM sender or a new GCM project number. This is the approach we recommend: have the new app version (the one that uses GCM) send a bit along with the registration ID. This bit tells your server that this registration ID is for GCM. If you don't get the extra bit, you mark the registration ID as C2DM. Once no more valid registration IDs are marked as C2DM, you can complete the migration.</p>
-
-<h2 id="migrating">Migrating Your Apps</h2>
-<p>This section describes how to move existing C2DM apps to GCM.</p>
-<h3 id="client">Client changes</h3>
-<p>Migration is simple! Just re-register the client app for your target GCM-enabled platform. For
- example, see <a href="{@docRoot}google/gcm/client.html#sample-register">Register for GCM</a></p>
-
-<p>After receiving a response from GCM, the registration ID obtained must be sent to the application server. When doing this, the application should indicate that it is sending a GCM registration ID so that the server can distinguish it from existing C2DM registrations.</p>
-
-<h3 id="server">Server changes</h3>
-<p>When the application server receives a GCM registration ID, it should store it and mark it as such.</p>
-<p>Sending messages to GCM devices requires a few changes:</p>
-<ul>
- <li> The request should be sent to a new endpoint: <code>https://android.googleapis.com/gcm/send</code>.</li>
- <li>The Authorization header of the request should contain the API key generated during sign up. This key replaces the deprecated ClientLogin Auth token.</li>
-</ul>
-<p>For example:
-</p>
-<pre>Content-Type:application/json
-Authorization:key=AIzaSyB-1uEai2WiUapxCs2Q0GZYzPu7Udno5aA
-
-{
- "registration_id" : "APA91bHun4MxP5egoKMwt2KZFBaFUH-1RYqx...",
- "data" : {
- "Team" : "Portugal",
- "Score" : "3",
- "Player" : "Varela",
- },
-}</pre>
-<p>For a detailed discussion of this topic and more examples, see the <a href="gcm.html#send-msg">Architectural Overview</a>.</p>
-<p>Eventually, once enough users of your application have migrated to the new service, you might want to take advantage of the new <a href="gcm.html#send-msg">JSON-formatted</a> requests that give access to the full set of features provided by GCM.</p>
-
diff --git a/docs/html/google/gcm/ccs.jd b/docs/html/google/gcm/ccs.jd
deleted file mode 100644
index c4d1b1d..0000000
--- a/docs/html/google/gcm/ccs.jd
+++ /dev/null
@@ -1,963 +0,0 @@
-page.title=GCM Cloud Connection Server (XMPP)
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
-
-
-<h2>In this document</h2>
-
-<ol class="toc">
- <li><a href="#connecting">Establishing a Connection</a>
- <ol class="toc">
- <li><a href="#auth">Authentication</a></li>
- </ol>
- </li>
- <li><a href="#format">Downstream Messages</a>
- <ol class="toc">
- <li><a href="#request">Request format</a></li>
- <li><a href="#response">Response format</a></li>
- <li><a href="#receipts">Receive delivery receipts</a></li>
- </ol>
- </li>
- <li><a href="#upstream">Upstream Messages</a>
- </li>
- <li><a href="#flow">Flow Control</a> </li>
- <li><a href="#implement">Implementing an XMPP-based App Server</a>
- <ol class="toc">
- <li><a href="#smack">Java sample using the Smack library</a></li>
- <li><a href="#python">Python sample</a></li>
- </ol>
- </li>
-</ol>
-
-<h2>See Also</h2>
-
-<ol class="toc">
-<li><a href="server-ref.html">Server Reference</a></li>
-<li><a href="{@docRoot}google/gcm/http.html">HTTP</a></li>
-<li><a href="{@docRoot}google/gcm/gs.html">Getting Started</a></li>
-<li><a href="{@docRoot}google/gcm/server.html">Implementing GCM Server</a></li>
-<li><a href="{@docRoot}google/gcm/client.html">Implementing GCM Client</a></li>
-</ol>
-
-</div>
-</div>
-
-<p>The Google Cloud Messaging (GCM) Cloud Connection Server (CCS) is an XMPP endpoint that provides a
-persistent, asynchronous, bidirectional connection to Google servers. The
-connection can be used to send and receive messages between your server and
-your users' GCM-connected devices.</p>
-
-<p class="note"><strong>Note:</strong> The content in this document
-applies to <a href="http://developer.chrome.com/apps/cloudMessaging">
-GCM with Chrome apps</a> as well as Android.
-
-<p>You can continue to use the HTTP request mechanism to send messages to GCM
-servers, side-by-side with CCS which uses XMPP. Some of the benefits of CCS include:</p>
-
-<ul>
- <li>The asynchronous nature of XMPP allows you to send more messages with fewer
-resources.</li>
- <li>Communication is bidirectional—not only can your server send messages
-to the device, but the device can send messages back to your server.</li>
- <li>The device can send messages back using the same connection used for receiving,
-thereby improving battery life.</li>
-</ul>
-
-<p>The upstream messaging (device-to-cloud) feature of CCS is part of the Google
-Play services platform. Upstream messaging is available through the
-<a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">
-{@code GoogleCloudMessaging}</a>
-APIs. For examples, see
-<a href="#implement">Implementing an XMPP-based App Server</a>.</p>
-
-<p class="note"><strong>Note:</strong> See the
-<a href="server-ref.html">Server Reference</a> for a list of all the message
-parameters and which connection server(s) supports them.</p>
-
-<h2 id="connecting">Establishing a Connection</h2>
-
-<p>CCS just uses XMPP as an authenticated transport layer, so you can use most
-XMPP libraries to manage the connection. For an example, see <a href="#smack">
-Java sample using the Smack library</a>.</p>
-
-<p>The CCS XMPP endpoint runs at {@code gcm.googleapis.com:5235}. When testing
-functionality (with non-production users), you should instead connect to
-{@code gcm-preprod.googleapis.com:5236} (note the different port). Regular
-testing on preprod (a smaller environment where the latest CCS builds run) is
-beneficial both for isolating real users from test code, as well as for early
-detection of unexpected behavior changes. Note that a connection receives upstream
-messages destined for its GCM sender ID, regardless of which environment (gcm or
-gcm-preprod) it is connected to. Therefore, test code connecting to
-{@code gcm-preprod.googleapis.com:5236} should use a different GCM sender ID to
-avoid upstream messages from production traffic being sent over test connections.</p>
-
-<p>The connection has two important requirements:</p>
-
-<ul>
- <li>You must initiate a Transport Layer Security (TLS) connection. Note that
- CCS doesn't currently support the <a href="http://xmpp.org/rfcs/rfc3920.html"
- class="external-link" target="_android">STARTTLS extension</a>.</li>
- <li>CCS requires a SASL PLAIN authentication mechanism using
- {@code <your_GCM_Sender_Id>@gcm.googleapis.com} (GCM sender ID)
- and the API key as the password, where the sender ID and API key are the same
- as described in <a href="gs.html">Getting Started</a>.</li>
-</ul>
-
-<p>If at any point the connection fails, you should immediately reconnect.
-There is no need to back off after a disconnect that happens after
-authentication.</p>
-
-<h3 id="auth">Authentication</h3>
-
-<p>The following snippets illustrate how to perform authentication in CCS.</p>
-<h4>Client</h4>
-<pre><stream:stream to="gcm.googleapis.com"
- version="1.0" xmlns="jabber:client"
- xmlns:stream="http://etherx.jabber.org/streams"/>
-</pre>
-<h4>Server</h4>
-<pre><str:features xmlns:str="http://etherx.jabber.org/streams">
- <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
- <mechanism>X-OAUTH2</mechanism>
- <mechanism>X-GOOGLE-TOKEN</mechanism>
- <mechanism>PLAIN</mechanism>
- </mechanisms>
-</str:features>
-</pre>
-
-<h4>Client</h4>
-<pre><auth mechanism="PLAIN"
-xmlns="urn:ietf:params:xml:ns:xmpp-sasl">MTI2MjAwMzQ3OTMzQHByb2plY3RzLmdjbS5hb
-mFTeUIzcmNaTmtmbnFLZEZiOW1oekNCaVlwT1JEQTJKV1d0dw==</auth>
-</pre>
-
-<h4>Server</h4>
-<pre><success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/></pre>
-
-<h2 id="format">Downstream Messages</h2>
-<p>Once the XMPP connection is established, CCS and your server use normal XMPP
-<code><message></code> stanzas to send JSON-encoded messages back and
-forth. The body of the <code><message></code> must be:</p>
-<pre>
-<gcm xmlns:google:mobile:data>
- <em>JSON payload</em>
-</gcm>
-</pre>
-
-<p>The JSON payload for regular GCM messages is similar to
-<a href="http.html#request">what the GCM http endpoint uses</a>, with these
-exceptions:</p>
-<ul>
- <li>There is no support for multiple recipients.</li>
- <li>{@code to} is used instead of {@code registration_ids}.</li>
- <li>CCS adds the field {@code message_id}, which is required. This ID uniquely
-identifies the message in an XMPP connection. The ACK or NACK from CCS uses the
-{@code message_id} to identify a message sent from 3rd-party app servers to CCS.
-Therefore, it's important that this {@code message_id} not only be unique (per
-sender ID), but always present.</li>
-</ul>
-
-<p>In addition to regular GCM messages, control messages are sent, indicated by
-the {@code message_type} field in the JSON object. The value can be either
-'ack' or 'nack', or 'control' (see formats below). Any GCM message with an
-unknown {@code message_type} can be ignored by your server.</p>
-
-<p>For each device message your app server receives from CCS, it needs to send
-an ACK message.
-It never needs to send a NACK message. If you don't send an ACK for a message,
-CCS resends it the next time a new XMPP connection is established, unless the
-message expires first.
-</p>
-<p>CCS also sends an ACK or NACK for each server-to-device message. If you do not
-receive either, it means that the TCP connection was closed in the middle of the
-operation and your server needs to resend the messages. See
-<a href="#flow">Flow Control</a> for details.
-</p>
-
-<p class="note"><strong>Note:</strong> See the
-<a href="server-ref.html">Server Reference</a> for a list of all the message
-parameters and which connection server(s) supports them.</p>
-
-<h3 id="request">Request format</h3>
-
-<p>Here is an XMPP stanza containing the JSON message from a 3rd-party app server to CCS:
-
-</p>
-<pre><message id="">
- <gcm xmlns="google:mobile:data">
- {
- "to":"REGISTRATION_ID", // "to" replaces "registration_ids"
- "message_id":"m-1366082849205" // new required field
- "data":
- {
- "hello":"world",
- }
- "time_to_live":"600",
- "delay_while_idle": true/false,
- "delivery_receipt_requested": true/false
- }
- </gcm>
-</message>
-</pre>
-
-<h3 id="response">Response format</h3>
-
-<p>A CCS response can have 3 possible forms. The first one is a regular 'ack'
-message. But when the response contains an error, there are 2
-different forms the message can take, described below.</p>
-
-<h4 id="ack">ACK message</h4>
-
-<p>Here is an XMPP stanza containing the ACK/NACK message from CCS to 3rd-party app server:
-</p>
-<pre><message id="">
- <gcm xmlns="google:mobile:data">
- {
- "from":"REGID",
- "message_id":"m-1366082849205"
- "message_type":"ack"
- }
- </gcm>
-</message>
-</pre>
-
-<h4 id="nack">NACK message</h4>
-
-<p>A NACK error is a regular XMPP message in which the {@code message_type} status
-message is "nack". A NACK message contains:</p>
-<ul>
-<li>Nack error code.</li>
-<li>Nack error description.</li>
-</ul>
-
-<p>Below are some examples.</p>
-
-<p>Bad registration:</p>
-
-<pre><message>
- <gcm xmlns="google:mobile:data">
- {
- "message_type":"nack",
- "message_id":"msgId1",
- "from":"SomeInvalidRegistrationId",
- "error":"BAD_REGISTRATION",
- "error_description":"Invalid token on 'to' field: SomeInvalidRegistrationId"
- }
- </gcm>
-</message></pre>
-
-<p>Invalid JSON:</p>
-
-<pre><message>
- <gcm xmlns="google:mobile:data">
- {
- "message_type":"nack",
- "message_id":"msgId1",
- "from":"APA91bHFOtaQGSwupt5l1og",
- "error":"INVALID_JSON",
- "error_description":"InvalidJson: JSON_TYPE_ERROR : Field \"time_to_live\" must be a JSON java.lang.Number: abc"
- }
- </gcm>
-</message>
-</pre>
-
-<p>Device Message Rate Exceeded:</p>
-
-<pre><message id="...">
- <gcm xmlns="google:mobile:data">
- {
- "message_type":"nack",
- "message_id":"msgId1",
- "from":"REGID",
- "error":"DEVICE_MESSAGE_RATE_EXCEEDED",
- "error_description":"Downstream message rate exceeded for this registration id"
- }
- </gcm>
-</message>
-</pre>
-
-<p>See the <a href="server-ref.html#table11">Server Reference</a> for a complete list of the
-NACK error codes. Unless otherwise
-indicated, a NACKed message should not be retried. Unexpected NACK error codes
-should be treated the same as {@code INTERNAL_SERVER_ERROR}.</p>
-
-<h4 id="stanza">Stanza error</h4>
-
-<p>You can also get a stanza error in certain cases.
-A stanza error contains:</p>
-<ul>
-<li>Stanza error code.</li>
-<li>Stanza error description (free text).</li>
-</ul>
-<p>For example:</p>
-
-<pre><message id="3" type="error" to="123456789@gcm.googleapis.com/ABC">
- <gcm xmlns="google:mobile:data">
- {"random": "text"}
- </gcm>
- <error code="400" type="modify">
- <bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
- <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">
- InvalidJson: JSON_PARSING_ERROR : Missing Required Field: message_id\n
- </text>
- </error>
-</message>
-</pre>
-
-<h4 id="control">Control messages</h4>
-
-<p>Periodically, CCS needs to close down a connection to perform load balancing. Before it
-closes the connection, CCS sends a {@code CONNECTION_DRAINING} message to indicate that the connection is being drained
-and will be closed soon. "Draining" refers to shutting off the flow of messages coming into a
-connection, but allowing whatever is already in the pipeline to continue. When you receive
-a {@code CONNECTION_DRAINING} message, you should immediately begin sending messages to another CCS
-connection, opening a new connection if necessary. You should, however, keep the original
-connection open and continue receiving messages that may come over the connection (and
-ACKing them)—CCS handles initiating a connection close when it is ready.</p>
-
-<p>The {@code CONNECTION_DRAINING} message looks like this:</p>
-<pre><message>
- <data:gcm xmlns:data="google:mobile:data">
- {
- "message_type":"control"
- "control_type":"CONNECTION_DRAINING"
- }
- </data:gcm>
-</message></pre>
-
-<p>{@code CONNECTION_DRAINING} is currently the only {@code control_type} supported.</p>
-
-<!--Delivery receipts section-->
-
-<h3 id="receipts">Receive delivery receipts</h3>
-
-<p>You can get delivery receipts (sent from CCS to
-your 3rd party app server) when
-a device confirms that it received a message sent by CCS.</p>
-
-<p>To enable this feature, the message your 3rd-party app server sends to CCS must include
-a field called <code>"delivery_receipt_requested"</code>. When this field is set to
-<code>true</code>, CCS sends a delivery receipt when a device confirms that it received a particular message.</p>
-
-<p>Here is an XMPP stanza containing a JSON
-message with <code>"delivery_receipt_requested"</code> set to <code>true</code>:</p>
-
-<pre><message id="">
- <gcm xmlns="google:mobile:data">
- {
- "to":"REGISTRATION_ID",
- "message_id":"m-1366082849205"
- "data":
- {
- "hello":"world",
- }
- "time_to_live":"600",
- "delay_while_idle": true,
- <strong>"delivery_receipt_requested": true</strong>
- }
- </gcm>
-</message>
-</pre>
-
-
-
-<p>Here is an example of the delivery receipt that CCS sends to tell your 3rd-party
-app server that a device received a message that CCS sent it:</p>
-
-</p>
-<pre><message id="">
- <gcm xmlns="google:mobile:data">
- {
- "category":"com.example.yourapp", // to know which app sent it
- "data":
- {
- “message_status":"MESSAGE_SENT_TO_DEVICE",
- “original_message_id”:”m-1366082849205”
- “device_registration_id”: “REGISTRATION_ID”
- },
- "message_id":"dr2:m-1366082849205",
- "message_type":"receipt",
- "from":"gcm.googleapis.com"
- }
- </gcm>
-</message></pre>
-
-<p>Note the following:</p>
-
-<ul>
- <li>The {@code "message_type"} is set to {@code "receipt"}.
- <li>The {@code "message_status"} is set to {@code "MESSAGE_SENT_TO_DEVICE"},
- indicating that the device received the message. Notice that in this case,
-{@code "message_status"} is not a field but rather part of the data payload.</li>
- <li>The receipt message ID consists of the original message ID, but with a
-<code>dr2:</code> prefix. Your 3rd-party app server must send an ACK back with this ID,
-which in this example is {@code dr2:m-1366082849205}.</li>
- <li>The original message ID, the device registration ID, and the status are inside the
-{@code "data"} field.</li>
-<li>If the connection between CCS and the device is poor, GCM may send multiple, duplicate delivery
- receipts. You can safely ignore such duplicates.</li>
-</ul>
-
-<h2 id="upstream">Upstream Messages</h2>
-
-<p>Using CCS and the
-<a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">
-{@code GoogleCloudMessaging}</a>
-API, you can send messages from a user's device to the cloud.</p>
-
-<p>Here is how you send an upstream message using the
-<a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">
-{@code GoogleCloudMessaging}</a>
-API. For a complete example, see <a href="client.html">Implementing GCM Client</a>:</p>
-
-<pre>GoogleCloudMessaging gcm = GoogleCloudMessaging.get(context);
-String GCM_SENDER_ID = "Your-Sender-ID";
-AtomicInteger msgId = new AtomicInteger();
-String id = Integer.toString(msgId.incrementAndGet());
-Bundle data = new Bundle();
-// Bundle data consists of a key-value pair
-data.putString("hello", "world");
-// "time to live" parameter
-// This is optional. It specifies a value in seconds up to 24 hours.
-int ttl = [0 seconds, 24 hours]
-
-gcm.send(GCM_SENDER_ID + "@gcm.googleapis.com", id, ttl, data);
-</pre>
-
-<p>This call generates the necessary XMPP stanza for sending the upstream message.
-The message goes from the app on the device to CCS to the 3rd-party app server.
-The stanza has the following format:</p>
-
-<pre><message id="">
- <gcm xmlns="google:mobile:data">
- {
- "category":"com.example.yourapp", // to know which app sent it
- "data":
- {
- "hello":"world",
- },
- "message_id":"m-123",
- "from":"REGID"
- }
- </gcm>
-</message></pre>
-
-<p>Here is the format of the ACK expected by CCS from 3rd-party app servers in
-response to the above message:</p>
-
-<pre><message id="">
- <gcm xmlns="google:mobile:data">
- {
- "to":"REGID",
- "message_id":"m-123"
- "message_type":"ack"
- }
- </gcm>
-</message></pre>
-
-
-<h2 id="flow">Flow Control</h2>
-
-<p>Every message sent to CCS receives either an ACK or a NACK response. Messages
-that haven't received one of these responses are considered pending. If the pending
-message count reaches 100, the 3rd-party app server should stop sending new messages
-and wait for CCS to acknowledge some of the existing pending messages as illustrated in
-figure 1:</p>
-
-<img src="{@docRoot}images/gcm/CCS-ack.png">
-
-<p class="img-caption">
- <strong>Figure 1.</strong> Message/ack flow.
-</p>
-
-<p>Conversely, to avoid overloading the 3rd-party app server, CCS stops sending
-if there are too many unacknowledged messages. Therefore, the 3rd-party app server
-should "ACK" upstream messages, received from the client application via CCS, as soon as possible
-to maintain a constant flow of incoming messages. The aforementioned pending message limit doesn't
-apply to these ACKs. Even if the pending message count reaches 100, the 3rd-party app server
-should continue sending ACKs for messages received from CCS to avoid blocking delivery of new
-upstream messages.</p>
-
-<p>ACKs are only valid within the context of one connection. If the connection is
-closed before a message can be ACKed, the 3rd-party app server should wait for CCS
-to resend the upstream message before ACKing it again. Similarly, all pending messages for which an
-ACK/NACK was not received from CCS before the connection was closed should be sent again.
-</p>
-
-<h2 id="implement">Implementing an XMPP-based App Server</h2>
-
-<p>This section gives examples of implementing an app server that works with CCS.
-Note that a full GCM implementation requires a client-side implementation, in
-addition to the server. For more information, see <a href="client.html">
-Implementing GCM Client</a>.</a>
-
-<h3 id="smack">Java sample using the Smack library</h3>
-
-<p>Here is a sample app server written in Java, using the
-<a href="http://www.igniterealtime.org/projects/smack/">Smack</a> library.</p>
-
-<pre>import org.jivesoftware.smack.ConnectionConfiguration;
-import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
-import org.jivesoftware.smack.ConnectionListener;
-import org.jivesoftware.smack.PacketInterceptor;
-import org.jivesoftware.smack.PacketListener;
-import org.jivesoftware.smack.SmackException;
-import org.jivesoftware.smack.SmackException.NotConnectedException;
-import org.jivesoftware.smack.XMPPConnection;
-import org.jivesoftware.smack.XMPPException;
-import org.jivesoftware.smack.filter.PacketTypeFilter;
-import org.jivesoftware.smack.packet.DefaultPacketExtension;
-import org.jivesoftware.smack.packet.Message;
-import org.jivesoftware.smack.packet.Packet;
-import org.jivesoftware.smack.packet.PacketExtension;
-import org.jivesoftware.smack.provider.PacketExtensionProvider;
-import org.jivesoftware.smack.provider.ProviderManager;
-import org.jivesoftware.smack.tcp.XMPPTCPConnection;
-import org.jivesoftware.smack.util.StringUtils;
-import org.json.simple.JSONValue;
-import org.json.simple.parser.ParseException;
-import org.xmlpull.v1.XmlPullParser;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.net.ssl.SSLSocketFactory;
-
-/**
- * Sample Smack implementation of a client for GCM Cloud Connection Server. This
- * code can be run as a standalone CCS client.
- *
- * <p>For illustration purposes only.
- */
-public class SmackCcsClient {
-
- private static final Logger logger = Logger.getLogger("SmackCcsClient");
-
- private static final String GCM_SERVER = "gcm.googleapis.com";
- private static final int GCM_PORT = 5235;
-
- private static final String GCM_ELEMENT_NAME = "gcm";
- private static final String GCM_NAMESPACE = "google:mobile:data";
-
- static {
-
- ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE,
- new PacketExtensionProvider() {
- @Override
- public PacketExtension parseExtension(XmlPullParser parser) throws
- Exception {
- String json = parser.nextText();
- return new GcmPacketExtension(json);
- }
- });
- }
-
- private XMPPConnection connection;
-
- /**
- * Indicates whether the connection is in draining state, which means that it
- * will not accept any new downstream messages.
- */
- protected volatile boolean connectionDraining = false;
-
- /**
- * Sends a downstream message to GCM.
- *
- * @return true if the message has been successfully sent.
- */
- public boolean sendDownstreamMessage(String jsonRequest) throws
- NotConnectedException {
- if (!connectionDraining) {
- send(jsonRequest);
- return true;
- }
- logger.info("Dropping downstream message since the connection is draining");
- return false;
- }
-
- /**
- * Returns a random message id to uniquely identify a message.
- *
- * <p>Note: This is generated by a pseudo random number generator for
- * illustration purpose, and is not guaranteed to be unique.
- */
- public String nextMessageId() {
- return "m-" + UUID.randomUUID().toString();
- }
-
- /**
- * Sends a packet with contents provided.
- */
- protected void send(String jsonRequest) throws NotConnectedException {
- Packet request = new GcmPacketExtension(jsonRequest).toPacket();
- connection.sendPacket(request);
- }
-
- /**
- * Handles an upstream data message from a device application.
- *
- * <p>This sample echo server sends an echo message back to the device.
- * Subclasses should override this method to properly process upstream messages.
- */
- protected void handleUpstreamMessage(Map<String, Object> jsonObject) {
- // PackageName of the application that sent this message.
- String category = (String) jsonObject.get("category");
- String from = (String) jsonObject.get("from");
- @SuppressWarnings("unchecked")
- Map<String, String> payload = (Map<String, String>) jsonObject.get("data");
- payload.put("ECHO", "Application: " + category);
-
- // Send an ECHO response back
- String echo = createJsonMessage(from, nextMessageId(), payload,
- "echo:CollapseKey", null, false);
-
- try {
- sendDownstreamMessage(echo);
- } catch (NotConnectedException e) {
- logger.log(Level.WARNING, "Not connected anymore, echo message is
- not sent", e);
- }
- }
-
- /**
- * Handles an ACK.
- *
- * <p>Logs a {@code INFO} message, but subclasses could override it to
- * properly handle ACKs.
- */
- protected void handleAckReceipt(Map<String, Object> jsonObject) {
- String messageId = (String) jsonObject.get("message_id");
- String from = (String) jsonObject.get("from");
- logger.log(Level.INFO, "handleAckReceipt() from: " + from + ",
- messageId: " + messageId);
- }
-
- /**
- * Handles a NACK.
- *
- * <p>Logs a {@code INFO} message, but subclasses could override it to
- * properly handle NACKs.
- */
- protected void handleNackReceipt(Map<String, Object> jsonObject) {
- String messageId = (String) jsonObject.get("message_id");
- String from = (String) jsonObject.get("from");
- logger.log(Level.INFO, "handleNackReceipt() from: " + from + ",
- messageId: " + messageId);
- }
-
- protected void handleControlMessage(Map<String, Object> jsonObject) {
- logger.log(Level.INFO, "handleControlMessage(): " + jsonObject);
- String controlType = (String) jsonObject.get("control_type");
- if ("CONNECTION_DRAINING".equals(controlType)) {
- connectionDraining = true;
- } else {
- logger.log(Level.INFO, "Unrecognized control type: %s. This could
- happen if new features are " + "added to the CCS protocol.",
- controlType);
- }
- }
-
- /**
- * Creates a JSON encoded GCM message.
- *
- * @param to RegistrationId of the target device (Required).
- * @param messageId Unique messageId for which CCS sends an
- * "ack/nack" (Required).
- * @param payload Message content intended for the application. (Optional).
- * @param collapseKey GCM collapse_key parameter (Optional).
- * @param timeToLive GCM time_to_live parameter (Optional).
- * @param delayWhileIdle GCM delay_while_idle parameter (Optional).
- * @return JSON encoded GCM message.
- */
- public static String createJsonMessage(String to, String messageId,
- Map<String, String> payload, String collapseKey, Long timeToLive,
- Boolean delayWhileIdle) {
- Map<String, Object> message = new HashMap<String, Object>();
- message.put("to", to);
- if (collapseKey != null) {
- message.put("collapse_key", collapseKey);
- }
- if (timeToLive != null) {
- message.put("time_to_live", timeToLive);
- }
- if (delayWhileIdle != null && delayWhileIdle) {
- message.put("delay_while_idle", true);
- }
- message.put("message_id", messageId);
- message.put("data", payload);
- return JSONValue.toJSONString(message);
- }
-
- /**
- * Creates a JSON encoded ACK message for an upstream message received
- * from an application.
- *
- * @param to RegistrationId of the device who sent the upstream message.
- * @param messageId messageId of the upstream message to be acknowledged to CCS.
- * @return JSON encoded ack.
- */
- protected static String createJsonAck(String to, String messageId) {
- Map<String, Object> message = new HashMap<String, Object>();
- message.put("message_type", "ack");
- message.put("to", to);
- message.put("message_id", messageId);
- return JSONValue.toJSONString(message);
- }
-
- /**
- * Connects to GCM Cloud Connection Server using the supplied credentials.
- *
- * @param senderId Your GCM project number
- * @param apiKey API Key of your project
- */
- public void connect(long senderId, String apiKey)
- throws XMPPException, IOException, SmackException {
- ConnectionConfiguration config =
- new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
- config.setSecurityMode(SecurityMode.enabled);
- config.setReconnectionAllowed(true);
- config.setRosterLoadedAtLogin(false);
- config.setSendPresence(false);
- config.setSocketFactory(SSLSocketFactory.getDefault());
-
- connection = new XMPPTCPConnection(config);
- connection.connect();
-
- connection.addConnectionListener(new LoggingConnectionListener());
-
- // Handle incoming packets
- connection.addPacketListener(new PacketListener() {
-
- @Override
- public void processPacket(Packet packet) {
- logger.log(Level.INFO, "Received: " + packet.toXML());
- Message incomingMessage = (Message) packet;
- GcmPacketExtension gcmPacket =
- (GcmPacketExtension) incomingMessage.
- getExtension(GCM_NAMESPACE);
- String json = gcmPacket.getJson();
- try {
- @SuppressWarnings("unchecked")
- Map<String, Object> jsonObject =
- (Map<String, Object>) JSONValue.
- parseWithException(json);
-
- // present for "ack"/"nack", null otherwise
- Object messageType = jsonObject.get("message_type");
-
- if (messageType == null) {
- // Normal upstream data message
- handleUpstreamMessage(jsonObject);
-
- // Send ACK to CCS
- String messageId = (String) jsonObject.get("message_id");
- String from = (String) jsonObject.get("from");
- String ack = createJsonAck(from, messageId);
- send(ack);
- } else if ("ack".equals(messageType.toString())) {
- // Process Ack
- handleAckReceipt(jsonObject);
- } else if ("nack".equals(messageType.toString())) {
- // Process Nack
- handleNackReceipt(jsonObject);
- } else if ("control".equals(messageType.toString())) {
- // Process control message
- handleControlMessage(jsonObject);
- } else {
- logger.log(Level.WARNING,
- "Unrecognized message type (%s)",
- messageType.toString());
- }
- } catch (ParseException e) {
- logger.log(Level.SEVERE, "Error parsing JSON " + json, e);
- } catch (Exception e) {
- logger.log(Level.SEVERE, "Failed to process packet", e);
- }
- }
- }, new PacketTypeFilter(Message.class));
-
- // Log all outgoing packets
- connection.addPacketInterceptor(new PacketInterceptor() {
- @Override
- public void interceptPacket(Packet packet) {
- logger.log(Level.INFO, "Sent: {0}", packet.toXML());
- }
- }, new PacketTypeFilter(Message.class));
-
- connection.login(senderId + "@gcm.googleapis.com", apiKey);
- }
-
- public static void main(String[] args) throws Exception {
- final long senderId = 1234567890L; // your GCM sender id
- final String password = "Your API key";
-
- SmackCcsClient ccsClient = new SmackCcsClient();
-
- ccsClient.connect(senderId, password);
-
- // Send a sample hello downstream message to a device.
- String toRegId = "RegistrationIdOfTheTargetDevice";
- String messageId = ccsClient.nextMessageId();
- Map<String, String> payload = new HashMap<String, String>();
- payload.put("Hello", "World");
- payload.put("CCS", "Dummy Message");
- payload.put("EmbeddedMessageId", messageId);
- String collapseKey = "sample";
- Long timeToLive = 10000L;
- String message = createJsonMessage(toRegId, messageId, payload,
- collapseKey, timeToLive, true);
-
- ccsClient.sendDownstreamMessage(message);
- }
-
- /**
- * XMPP Packet Extension for GCM Cloud Connection Server.
- */
- private static final class GcmPacketExtension extends DefaultPacketExtension {
-
- private final String json;
-
- public GcmPacketExtension(String json) {
- super(GCM_ELEMENT_NAME, GCM_NAMESPACE);
- this.json = json;
- }
-
- public String getJson() {
- return json;
- }
-
- @Override
- public String toXML() {
- return String.format("<%s xmlns=\"%s\">%s</%s>",
- GCM_ELEMENT_NAME, GCM_NAMESPACE,
- StringUtils.escapeForXML(json), GCM_ELEMENT_NAME);
- }
-
- public Packet toPacket() {
- Message message = new Message();
- message.addExtension(this);
- return message;
- }
- }
-
- private static final class LoggingConnectionListener
- implements ConnectionListener {
-
- @Override
- public void connected(XMPPConnection xmppConnection) {
- logger.info("Connected.");
- }
-
- @Override
- public void authenticated(XMPPConnection xmppConnection) {
- logger.info("Authenticated.");
- }
-
- @Override
- public void reconnectionSuccessful() {
- logger.info("Reconnecting..");
- }
-
- @Override
- public void reconnectionFailed(Exception e) {
- logger.log(Level.INFO, "Reconnection failed.. ", e);
- }
-
- @Override
- public void reconnectingIn(int seconds) {
- logger.log(Level.INFO, "Reconnecting in %d secs", seconds);
- }
-
- @Override
- public void connectionClosedOnError(Exception e) {
- logger.info("Connection closed on error.");
- }
-
- @Override
- public void connectionClosed() {
- logger.info("Connection closed.");
- }
- }
-}</pre>
-
-<h3 id="python">Python sample</h3>
-
-<p>Here is an example of a CCS app server written in Python. This sample echo
-server sends an initial message, and for every upstream message received, it sends
-a dummy response back to the application that sent the upstream message. This
-example illustrates how to connect, send, and receive GCM messages using XMPP. It
-shouldn't be used as-is on a production deployment.</p>
-
-<pre>
-#!/usr/bin/python
-import sys, json, xmpp, random, string
-
-SERVER = 'gcm.googleapis.com'
-PORT = 5235
-USERNAME = "Your GCM Sender Id"
-PASSWORD = "API Key"
-REGISTRATION_ID = "Registration Id of the target device"
-
-unacked_messages_quota = 100
-send_queue = []
-
-# Return a random alphanumerical id
-def random_id():
- rid = ''
- for x in range(8): rid += random.choice(string.ascii_letters + string.digits)
- return rid
-
-def message_callback(session, message):
- global unacked_messages_quota
- gcm = message.getTags('gcm')
- if gcm:
- gcm_json = gcm[0].getData()
- msg = json.loads(gcm_json)
- if not msg.has_key('message_type'):
- # Acknowledge the incoming message immediately.
- send({'to': msg['from'],
- 'message_type': 'ack',
- 'message_id': msg['message_id']})
- # Queue a response back to the server.
- if msg.has_key('from'):
- # Send a dummy echo response back to the app that sent the upstream message.
- send_queue.append({'to': msg['from'],
- 'message_id': random_id(),
- 'data': {'pong': 1}})
- elif msg['message_type'] == 'ack' or msg['message_type'] == 'nack':
- unacked_messages_quota += 1
-
-def send(json_dict):
- template = ("<message><gcm xmlns='google:mobile:data'>{1}</gcm></message>")
- client.send(xmpp.protocol.Message(
- node=template.format(client.Bind.bound[0], json.dumps(json_dict))))
-
-def flush_queued_messages():
- global unacked_messages_quota
- while len(send_queue) and unacked_messages_quota > 0:
- send(send_queue.pop(0))
- unacked_messages_quota -= 1
-
-client = xmpp.Client('gcm.googleapis.com', debug=['socket'])
-client.connect(server=(SERVER,PORT), secure=1, use_srv=False)
-auth = client.auth(USERNAME, PASSWORD)
-if not auth:
- print 'Authentication failed!'
- sys.exit(1)
-
-client.RegisterHandler('message', message_callback)
-
-send_queue.append({'to': REGISTRATION_ID,
- 'message_id': 'reg_id',
- 'data': {'message_destination': 'RegId',
- 'message_id': random_id()}})
-
-while True:
- client.Process(1)
- flush_queued_messages()</pre>
diff --git a/docs/html/google/gcm/client.jd b/docs/html/google/gcm/client.jd
deleted file mode 100644
index 9cb3f84..0000000
--- a/docs/html/google/gcm/client.jd
+++ /dev/null
@@ -1,689 +0,0 @@
-page.title=Implementing GCM Client on Android
-page.tags=cloud,push,messaging
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
-
-
-<h2>In this document</h2>
-
-<ol class="toc">
-<li><a href="#play-services">Set Up Google Play Services</a></li>
-<li><a href="#manifest">Edit Your Application's Manifest</a></li>
-<li><a href="#app">Write Your Application</a>
- <ol class="toc">
- <li><a href="#sample-play">Check for Google Play Services APK</a></li>
- <li><a href="#sample-register">Register for GCM</a></li>
- <li><a href="#sample-receive">Receive a downstream message</a></li>
- <li><a href="#sample-send">Send an upstream message</a></li>
- </ol>
- <li><a href="#run">Running the Sample</a></li>
- <li><a href="#stats">Viewing Statistics</a></li>
-</li>
-
-</ol>
-
-<h2>See Also</h2>
-
-<ol class="toc">
-<li><a href="gs.html">Getting Started</a></li>
-<li><a href="server.html">Implementing GCM Server</a></li>
-</ol>
-
-</div>
-</div>
-
-<p>A Google Cloud Messaging (GCM) Android client is a GCM-enabled app that runs on an
-Android device. To write your client code, we recommend that you use the
-<a href="{@docRoot}reference/com/google/android/gms/gcm/package-summary.html">
-{@code GoogleCloudMessaging}</a> API.</p>
-
-<p>Here are the requirements for running a GCM Android client:</p>
-
-<ul>
- <li>At a bare minimum, GCM requires devices running Android 2.2 or higher that also have the
-Google Play Store application installed, or an emulator running Android 2.2
-with Google APIs. Note that you are not limited to deploying your
-Android applications through Google Play Store.</li>
- <li>However, if you want to continue to use new GCM features that are distributed
-through Google Play Services, the device must be running Android 2.3 or higher, or
-you can use an emulator running Android 2.3 with Google APIs.</li>
-<li>On Android devices, GCM uses an existing connection for Google services. For
-pre-3.0 devices, this requires users to set up their Google accounts on their mobile
-devices. A Google account is not a requirement on devices running Android 4.0.4 or higher.</li>
-
-</ul>
-
-<p>A full GCM implementation requires both a client implementation and a server
-implementation. For more
-information about implementing the server side, see <a href="server.html">
-Implementing GCM Server</a>.</p>
-
-<p>The following sections walk you through the steps involved in writing a GCM
-client-side application. Your client app can be arbitrarily complex, but at bare
-minimum, a GCM client app must include code to register (and thereby get a
-registration ID), and a broadcast receiver to receive messages sent by GCM.
-</p>
-
-<h2 id="play-services">Step 1: Set Up Google Play Services</h2>
-
-<p>To write your client application, use the
-<a href="{@docRoot}reference/com/google/android/gms/gcm/package-summary.html">
-{@code GoogleCloudMessaging}</a> API.
-To use this API, you must set up your project to use the Google Play services SDK,
-as described in <a href="/google/play-services/setup.html">Setup Google Play
-Services SDK</a>.</p>
-
-<p class="note"><strong>Caution:</strong> When you add the Play Services library to
-your project, be sure to add it <em>with resources</em>, as described in
-<a href="{@docRoot}google/play-services/setup.html#Setup">
-Setup Google Play Services SDK</a>. The key point is that you must
-<em>reference</em> the library—simply adding a {@code .jar} file to
-your Eclipse project will not work. You must follow the directions
-for referencing a library, or your app won't be able to access
-the library's resources, and it won't run properly.
-If you're using Android Studio, this is the string to add to the
-{@code dependency} section of your application's {@code build.gradle} file:</p>
-
-<pre>dependencies {
- compile "com.google.android.gms:play-services:3.1.+"
-}
-</pre>
-
-
-<h2 id="manifest">Step 2: Edit Your Application's Manifest</h2>
-
-<p>Add the following to your application's manifest:</p>
-<ul>
- <li>The <code>com.google.android.c2dm.permission.RECEIVE</code> permission so
-the Android application can register and receive messages.</li>
- <li>The <code>android.permission.INTERNET</code> permission so the Android
-application can send the registration ID to the 3rd party server.</li>
- <li>The <code>android.permission.WAKE_LOCK</code> permission so the application
-can keep the processor from sleeping when a message is received. Optional—use
-only if the app wants to keep the device from sleeping.</li>
- <li>An <code>applicationPackage + ".permission.C2D_MESSAGE"</code>
-permission to prevent other Android applications from registering and receiving
-the Android application's messages. The permission name must exactly match this
-pattern—otherwise the Android application will not receive the messages.</li>
- <li>A receiver for <code>com.google.android.c2dm.intent.RECEIVE</code>, with
-the category set
-as <code>applicationPackage</code>. The receiver should require the
-<code>com.google.android.c2dm.permission.SEND</code> permission, so that only the GCM
-Framework can send a message to it. If your app uses an {@link android.app.IntentService}
-(not required, but a common pattern), this receiver should be an instance of
-{@link android.support.v4.content.WakefulBroadcastReceiver}.
-A {@link android.support.v4.content.WakefulBroadcastReceiver} takes care of
-creating and managing a
-<a href="{@docRoot}reference/android/os/PowerManager.html#PARTIAL_WAKE_LOCK">
-partial wake lock</a> for your app.</li>
-
-<li>A {@link android.app.Service} (typically an {@link android.app.IntentService})
-to which the {@link android.support.v4.content.WakefulBroadcastReceiver} passes off
-the work of handling the GCM message, while ensuring that the device does not
-go back to sleep in the process. Including an {@link android.app.IntentService} is
-optional—you could choose to process your messages in a regular
-{@link android.content.BroadcastReceiver} instead, but realistically, most apps will
-use a {@link android.app.IntentService}.
-</li>
- <li>If the GCM feature is critical to the Android application's function, be sure to
-set <code>android:minSdkVersion="8"</code> or higher in the manifest. This
-ensures that the Android application cannot be installed in an environment in which it
-could not run properly. </li>
-</ul>
-
-<p>Here are excerpts from a sample manifest that supports GCM:</p>
-
-<pre class="prettyprint pretty-xml">
-<manifest package="com.example.gcm" ...>
-
- <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17"/>
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.GET_ACCOUNTS" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
- <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
-
- <permission android:name="com.example.gcm.permission.C2D_MESSAGE"
- android:protectionLevel="signature" />
- <uses-permission android:name="com.example.gcm.permission.C2D_MESSAGE" />
-
- <application ...>
- <receiver
- android:name=".GcmBroadcastReceiver"
- android:permission="com.google.android.c2dm.permission.SEND" >
- <intent-filter>
- <action android:name="com.google.android.c2dm.intent.RECEIVE" />
- <category android:name="com.example.gcm" />
- </intent-filter>
- </receiver>
- <service android:name=".GcmIntentService" />
- </application>
-
-</manifest>
-</pre>
-
-<h2 id="app"> Step 3: Write Your Application</h2>
-
-<p>Finally, write your application. This section features a sample client
-application that illustrates how to use the
-<a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">
-{@code GoogleCloudMessaging}</a> API. The sample consists of a main activity
-({@code DemoActivity}), a {@link android.support.v4.content.WakefulBroadcastReceiver}
-({@code GcmBroadcastReceiver}), and an {@link android.app.IntentService}
-({@code GcmIntentService}). You can find the complete source code for this sample at the
-<a href="http://code.google.com/p/gcm">open source site</a>.</p>
-
-<p>Note the following:</p>
-
-<ul>
- <li>Among other things, the sample illustrates registration and upstream
-(device-to-cloud) messaging. Upstream messaging only applies to apps that are running against a
-<a href="ccs.html">CCS</a> (XMPP) server; HTTP-based servers don't support upstream messaging.</li>
- <li>The <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">
- {@code GoogleCloudMessaging}</a>
-registration APIs replace the old registration process, which was based on the
-now-obsolete client helper library. While the old registration process still works,
-we encourage you to use the newer
-<a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">
-{@code GoogleCloudMessaging}</a>
-registration APIs, regardless of your underlying server.</li>
-</ul>
-
-<h3 id="sample-play">Check for Google Play Services APK</h3>
-
-<p>As described in <a href="{@docRoot}google/play-services/setup.html">
-Setup Google Play Services SDK</a>, apps that rely on the Play Services SDK
-should always check the device for a compatible Google Play services APK before
-accessing Google Play services features. In the sample app this check is done in
-two places: in the main activity's {@code onCreate()} method, and in its
-{@code onResume()} method. The check in {@code onCreate()} ensures that the app
-can't be used without a successful check. The check in {@code onResume()} ensures
-that if the user returns to the running app through some other means, such as
-through the back button, the check is still performed. If the
-device doesn't have a compatible Google Play services APK, your app can call
-{@code GooglePlayServicesUtil.getErrorDialog()} to allow users to download the
-APK from the Google Play Store or enable it in the device's system settings.
-For example:</p>
-
-<pre>private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
-...
-@Override
-public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.main);
- mDisplay = (TextView) findViewById(R.id.display);
-
- context = getApplicationContext();
-
- // Check device for Play Services APK.
- if (checkPlayServices()) {
- // If this check succeeds, proceed with normal processing.
- // Otherwise, prompt user to get valid Play Services APK.
- ...
- }
-}
-
-// You need to do the Play Services APK check here too.
-@Override
-protected void onResume() {
- super.onResume();
- checkPlayServices();
-}
-
-/**
- * Check the device to make sure it has the Google Play Services APK. If
- * it doesn't, display a dialog that allows users to download the APK from
- * the Google Play Store or enable it in the device's system settings.
- */
-private boolean checkPlayServices() {
- int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
- if (resultCode != ConnectionResult.SUCCESS) {
- if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
- GooglePlayServicesUtil.getErrorDialog(resultCode, this,
- PLAY_SERVICES_RESOLUTION_REQUEST).show();
- } else {
- Log.i(TAG, "This device is not supported.");
- finish();
- }
- return false;
- }
- return true;
-}</pre>
-
-<h3 id="sample-register">Register for GCM</h3>
-<p>An Android application needs to register with GCM servers before it can receive
-messages. When an app registers, it receives a registration ID, which it can then
-store for future use (note that registration IDs must be kept secret). In the
-following snippet the {@code onCreate()} method in the sample app's
-main activity checks to see if the app is already registered with GCM and with
-the server:</p>
-
-<pre>/**
- * Main UI for the demo app.
- */
-public class DemoActivity extends Activity {
-
- public static final String EXTRA_MESSAGE = "message";
- public static final String PROPERTY_REG_ID = "registration_id";
- private static final String PROPERTY_APP_VERSION = "appVersion";
- private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
-
- /**
- * Substitute you own sender ID here. This is the project number you got
- * from the API Console, as described in "Getting Started."
- */
- String SENDER_ID = "Your-Sender-ID";
-
- /**
- * Tag used on log messages.
- */
- static final String TAG = "GCMDemo";
-
- TextView mDisplay;
- GoogleCloudMessaging gcm;
- AtomicInteger msgId = new AtomicInteger();
- SharedPreferences prefs;
- Context context;
-
- String regid;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.main);
- mDisplay = (TextView) findViewById(R.id.display);
-
- context = getApplicationContext();
-
- // Check device for Play Services APK. If check succeeds, proceed with
- // GCM registration.
- if (checkPlayServices()) {
- gcm = GoogleCloudMessaging.getInstance(this);
- regid = getRegistrationId(context);
-
- if (regid.isEmpty()) {
- registerInBackground();
- }
- } else {
- Log.i(TAG, "No valid Google Play Services APK found.");
- }
- }
-...
-}</pre>
-
-<p>The app calls {@code getRegistrationId()} to see whether there is an existing
-registration ID stored in shared preferences:</p>
-
-<pre>/**
- * Gets the current registration ID for application on GCM service.
- * <p>
- * If result is empty, the app needs to register.
- *
- * @return registration ID, or empty string if there is no existing
- * registration ID.
- */
-private String getRegistrationId(Context context) {
- final SharedPreferences prefs = getGCMPreferences(context);
- String registrationId = prefs.getString(PROPERTY_REG_ID, "");
- if (registrationId.isEmpty()) {
- Log.i(TAG, "Registration not found.");
- return "";
- }
- // Check if app was updated; if so, it must clear the registration ID
- // since the existing registration ID is not guaranteed to work with
- // the new app version.
- int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
- int currentVersion = getAppVersion(context);
- if (registeredVersion != currentVersion) {
- Log.i(TAG, "App version changed.");
- return "";
- }
- return registrationId;
-}
-...
-/**
- * @return Application's {@code SharedPreferences}.
- */
-private SharedPreferences getGCMPreferences(Context context) {
- // This sample app persists the registration ID in shared preferences, but
- // how you store the registration ID in your app is up to you.
- return getSharedPreferences(DemoActivity.class.getSimpleName(),
- Context.MODE_PRIVATE);
-}</pre>
-
-<p>If the registration ID doesn't exist or the app was updated,
-{@code getRegistrationId()} returns an empty string
-to indicate that the app needs to get a new registration ID. {@code getRegistrationId()} calls
-the following method to check the app version:</p>
-
-<pre>/**
- * @return Application's version code from the {@code PackageManager}.
- */
-private static int getAppVersion(Context context) {
- try {
- PackageInfo packageInfo = context.getPackageManager()
- .getPackageInfo(context.getPackageName(), 0);
- return packageInfo.versionCode;
- } catch (NameNotFoundException e) {
- // should never happen
- throw new RuntimeException("Could not get package name: " + e);
- }
-}</pre>
-
-
-<p>If there isn't a valid existing registration ID, {@code DemoActivity} calls the
-following {@code registerInBackground()} method to register. Note that because the GCM
-methods {@code register()} and {@code unregister()} are blocking, this has to
-take place on a background thread. This sample uses {@link android.os.AsyncTask}
-to accomplish this:</p>
-
-<pre>
-/**
- * Registers the application with GCM servers asynchronously.
- * <p>
- * Stores the registration ID and app versionCode in the application's
- * shared preferences.
- */
-private void registerInBackground() {
- new AsyncTask<Void, Void, String>() {
- @Override
- protected String doInBackground(Void... params) {
- String msg = "";
- try {
- if (gcm == null) {
- gcm = GoogleCloudMessaging.getInstance(context);
- }
- regid = gcm.register(SENDER_ID);
- msg = "Device registered, registration ID=" + regid;
-
- // You should send the registration ID to your server over HTTP,
- // so it can use GCM/HTTP or CCS to send messages to your app.
- // The request to your server should be authenticated if your app
- // is using accounts.
- sendRegistrationIdToBackend();
-
- // For this demo: we don't need to send it because the device
- // will send upstream messages to a server that echo back the
- // message using the 'from' address in the message.
-
- // Persist the registration ID - no need to register again.
- storeRegistrationId(context, regid);
- } catch (IOException ex) {
- msg = "Error :" + ex.getMessage();
- // If there is an error, don't just keep trying to register.
- // Require the user to click a button again, or perform
- // exponential back-off.
- }
- return msg;
- }
-
- @Override
- protected void onPostExecute(String msg) {
- mDisplay.append(msg + "\n");
- }
- }.execute(null, null, null);
- ...
-}</pre>
-
-<p>Once you've received your registration ID, send it to your server:</p>
-<pre>
-/**
- * Sends the registration ID to your server over HTTP, so it can use GCM/HTTP
- * or CCS to send messages to your app. Not needed for this demo since the
- * device sends upstream messages to a server that echoes back the message
- * using the 'from' address in the message.
- */
-private void sendRegistrationIdToBackend() {
- // Your implementation here.
-}</pre>
-
-<p>After registering, the app calls {@code storeRegistrationId()} to store the
-registration ID in shared preferences for future use. This is just one way of
-persisting a registration ID. You might choose to use a different approach in
-your app:</p>
-
-<pre>/**
- * Stores the registration ID and app versionCode in the application's
- * {@code SharedPreferences}.
- *
- * @param context application's context.
- * @param regId registration ID
- */
-private void storeRegistrationId(Context context, String regId) {
- final SharedPreferences prefs = getGCMPreferences(context);
- int appVersion = getAppVersion(context);
- Log.i(TAG, "Saving regId on app version " + appVersion);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(PROPERTY_REG_ID, regId);
- editor.putInt(PROPERTY_APP_VERSION, appVersion);
- editor.commit();
-}</pre>
-
-<h4 id="reg-errors">Handle registration errors</h4>
-
-<p>As stated above, an Android app must register with GCM servers and get a registration ID
-before it can receive messages. A given registration ID is not guaranteed to last indefinitely,
-so the first thing your app should always do is check to make sure it has a valid
-registration ID (as shown in the code snippets above).</p>
-
-<p>In addition to confirming that it has a valid registration ID, your app should be prepared to handle
-the registration error {@code TOO_MANY_REGISTRATIONS}. This error indicates that the device
-has too many apps registered with GCM. The error only occurs in cases where there are
-extreme numbers of apps, so it should not affect the average user. The remedy is to prompt
-the user to delete some of the other client apps from the device to make
-room for the new one.</p>
-
-<h3 id="sample-receive">Receive a downstream message</h3>
-
-<p>As described above in <a href="#manifest">Step 2</a>, the app includes a
-{@link android.support.v4.content.WakefulBroadcastReceiver} for the <code>com.google.android.c2dm.intent.RECEIVE</code>
-intent. A broadcast receiver is the mechanism GCM uses to deliver messages. </p>
-<p>A {@link android.support.v4.content.WakefulBroadcastReceiver} is a special type of
-broadcast receiver that takes care of
-creating and managing a
-<a href="{@docRoot}reference/android/os/PowerManager.html#PARTIAL_WAKE_LOCK">
-partial wake lock</a> for your app.
-It passes off the work of processing the GCM message to a
-{@link android.app.Service} (typically an
-{@link android.app.IntentService}), while ensuring that the device does not
-go back to sleep in the transition. If you don't hold a wake lock while transitioning
-the work to a service, you are effectively allowing the device to go back to sleep before
-the work completes. The net result is that the app might not finish processing
-the GCM message until some arbitrary point in the future, which is not what you want.</p>
-
-<p class="note"><strong>Note:</strong> Using {@link android.support.v4.content.WakefulBroadcastReceiver}
-is not a requirement. If you have a relatively simple app that doesn't require
-a service, you can intercept the GCM message in a regular {@link android.content.BroadcastReceiver}
-and do your processing there. Once you get the intent that GCM passes into
-your broadcast receiver's {@code onReceive()} method, what you do with it
-is up to you.</p>
-
-<p>This snippet starts {@code GcmIntentService} with the method
-{@link android.support.v4.content.WakefulBroadcastReceiver#startWakefulService startWakefulService()}.
-This method is comparable to {@link android.content.Context#startService startService()}, except that
-the {@link android.support.v4.content.WakefulBroadcastReceiver} is holding a
-wake lock when the service starts. The intent that is passed with
-{@link android.support.v4.content.WakefulBroadcastReceiver#startWakefulService startWakefulService()}
-holds an extra identifying the wake lock:</p>
-
-
-<pre>public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- // Explicitly specify that GcmIntentService will handle the intent.
- ComponentName comp = new ComponentName(context.getPackageName(),
- GcmIntentService.class.getName());
- // Start the service, keeping the device awake while it is launching.
- startWakefulService(context, (intent.setComponent(comp)));
- setResultCode(Activity.RESULT_OK);
- }
-}</pre>
-
-<p>The intent service shown below does the actual work of handling the GCM
-message. When the service is finished, it calls
-{@link android.support.v4.content.WakefulBroadcastReceiver#completeWakefulIntent GcmBroadcastReceiver.completeWakefulIntent()}
-to release the wake lock. The
-{@link android.support.v4.content.WakefulBroadcastReceiver#completeWakefulIntent completeWakefulIntent()}
-method has as its parameter the same intent that was
-passed in from the {@link android.support.v4.content.WakefulBroadcastReceiver}.
-</p>
-
-<p>This snippet processes the GCM message based on message type, and posts the
-result in a notification. But what you do with GCM messages in your app is up to
-you—the possibilities are endless. For example, the message might be a ping,
-telling the app to sync to a server to retrieve new content, or it might be a
-chat message that you display in the UI.</p>
-
-<pre>
-public class GcmIntentService extends IntentService {
- public static final int NOTIFICATION_ID = 1;
- private NotificationManager mNotificationManager;
- NotificationCompat.Builder builder;
-
- public GcmIntentService() {
- super("GcmIntentService");
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- Bundle extras = intent.getExtras();
- GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
- // The getMessageType() intent parameter must be the intent you received
- // in your BroadcastReceiver.
- String messageType = gcm.getMessageType(intent);
-
- if (!extras.isEmpty()) { // has effect of unparcelling Bundle
- /*
- * Filter messages based on message type. Since it is likely that GCM
- * will be extended in the future with new message types, just ignore
- * any message types you're not interested in, or that you don't
- * recognize.
- */
- if (GoogleCloudMessaging.
- MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
- sendNotification("Send error: " + extras.toString());
- } else if (GoogleCloudMessaging.
- MESSAGE_TYPE_DELETED.equals(messageType)) {
- sendNotification("Deleted messages on server: " +
- extras.toString());
- // If it's a regular GCM message, do some work.
- } else if (GoogleCloudMessaging.
- MESSAGE_TYPE_MESSAGE.equals(messageType)) {
- // This loop represents the service doing some work.
- for (int i=0; i<5; i++) {
- Log.i(TAG, "Working... " + (i+1)
- + "/5 @ " + SystemClock.elapsedRealtime());
- try {
- Thread.sleep(5000);
- } catch (InterruptedException e) {
- }
- }
- Log.i(TAG, "Completed work @ " + SystemClock.elapsedRealtime());
- // Post notification of received message.
- sendNotification("Received: " + extras.toString());
- Log.i(TAG, "Received: " + extras.toString());
- }
- }
- // Release the wake lock provided by the WakefulBroadcastReceiver.
- GcmBroadcastReceiver.completeWakefulIntent(intent);
- }
-
- // Put the message into a notification and post it.
- // This is just one simple example of what you might choose to do with
- // a GCM message.
- private void sendNotification(String msg) {
- mNotificationManager = (NotificationManager)
- this.getSystemService(Context.NOTIFICATION_SERVICE);
-
- PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
- new Intent(this, DemoActivity.class), 0);
-
- NotificationCompat.Builder mBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_stat_gcm)
- .setContentTitle("GCM Notification")
- .setStyle(new NotificationCompat.BigTextStyle()
- .bigText(msg))
- .setContentText(msg);
-
- mBuilder.setContentIntent(contentIntent);
- mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
- }
-}</pre>
-
-
-<h3 id="sample-send">Send an upstream message</h3>
-<p>When the user clicks the app's <strong>Send</strong> button, the app sends an
-upstream message using the
-<a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">
-{@code GoogleCloudMessaging}</a> API. In order to receive the upstream message,
-your server should be connected to CCS. You can use one of the demo servers in
-<a href="ccs.html#implement">Implementing an XMPP-based App Server</a> to run the sample and connect
-to CCS.</p>
-
-<pre>public void onClick(final View view) {
- if (view == findViewById(R.id.send)) {
- new AsyncTask<Void, Void, String>() {
- @Override
- protected String doInBackground(Void... params) {
- String msg = "";
- try {
- Bundle data = new Bundle();
- data.putString("my_message", "Hello World");
- data.putString("my_action",
- "com.google.android.gcm.demo.app.ECHO_NOW");
- String id = Integer.toString(msgId.incrementAndGet());
- gcm.send(SENDER_ID + "@gcm.googleapis.com", id, data);
- msg = "Sent message";
- } catch (IOException ex) {
- msg = "Error :" + ex.getMessage();
- }
- return msg;
- }
-
- @Override
- protected void onPostExecute(String msg) {
- mDisplay.append(msg + "\n");
- }
- }.execute(null, null, null);
- } else if (view == findViewById(R.id.clear)) {
- mDisplay.setText("");
- }
-}</pre>
-
-<h2 id="run">Running the Sample</h2>
-
-<p>To run the sample:</p>
-
-<ol>
- <li>Follow the instructions in <a href="gs.html">Getting Started</a> to get your sender ID and
- API key.</li>
- <li>Implement your client app, as described in this document. You can find the complete source
- code for the client app at the <a href="http://code.google.com/p/gcm">open source site</a>.</li>
- <li>Run one of the demo servers (Java or Python) provided in
-<a href="ccs.html#implement">Implementing an XMPP-based App Server</a>. Whichever demo server you
- choose, don't forget to edit its code before running it to supply
-your sender ID and API key.
-</li>
-
-</ol>
-
-<h2 id="stats">Viewing Statistics</h2>
-
-<p>To view statistics and any error messages for your GCM applications:</p>
-<ol>
- <li> Go to the <a href="http://play.google.com/apps/publish">Developer Console</a>.</li>
- <li>Login with your developer account.
- <p>You will see a page that has a list of all of your apps.</p></li>
- <li> Click on the "statistics" link next to the app for which you
-want to view GCM stats.
- <p>Now you are on the statistics page.</p> </li>
- <li>Go to the drop-down menu and select the GCM metric you want to view.
- </li>
-</ol>
-<p class="note"><strong>Note:</strong> Stats on the Google API Console are not
-enabled for GCM. You must use the <a href="http://play.google.com/apps/publish">Developer Console</a>.</p>
-
diff --git a/docs/html/google/gcm/demo.jd b/docs/html/google/gcm/demo.jd
deleted file mode 100644
index 012eb9a..0000000
--- a/docs/html/google/gcm/demo.jd
+++ /dev/null
@@ -1,277 +0,0 @@
-page.title=GCM Demo Application
-@jd:body
-
-<div id="deprecatedSticker">
- <a href="#"
- onclick="$('#naMessage').show();$('#deprecatedSticker').hide();return false">
- <strong>This doc is deprecated</strong></a>
-</div>
-
-
-<div id="naMessage" style="display:block">
-<div><p><strong>The information in this document has been superseded by <a href="server.html">GCM Server</a> and <a href="client.html">GCM Client</a></strong>. Please use the <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">{@code GoogleCloudMessaging}</a> API instead of the GCM client helper library. The GCM server helper library is still valid.</p>
-
- <input style="margin-top:1em;padding:5px" type="button"
- value="That's nice, but I still want to read this document"
-onclick="$('#naMessage').hide();$('#deprecatedSticker').show()" />
-</div>
-</div>
-
-<div id="qv-wrapper">
-<div id="qv">
-
-<h2>Quickview</h2>
-
-<ul>
-<li>Build and run the GCM demo app.</li>
-<li>Understand how to set up both the client and server sides of a GCM app.</li>
-<li>Become familiar with the GCM helper libraries.</li>
-</ul>
-
-
-<h2>In this document</h2>
-
-<ol>
- <li><a href="#requirements">Requirements</a> </li>
- <li><a href="#gcm-setup">Setting Up GCM</a></li>
- <li><a href="#server-setup">Setting Up the Server</a>
- <ol>
- <li><a href="#webserver-setup">Using a standard web server</a></li>
- <li><a href="#appengine-setup">Using App Engine for Java</a></li>
- </ol>
- </li>
- <li><a href="#device-setup">Setting Up the Device</a></li>
-</ol>
-
-</div>
-</div>
-
-<p class="note"><strong>Note:</strong> This tutorial describes how to develop GCM-enabled apps using the helper libraries. This is just one approach. For a more comprehensive discussion of the available APIs and development paths, see <a href="gs.html">Getting Started</a>.
-
-<p>The Google Cloud Messaging (GCM) Demo demonstrates how to use the Google Cloud Messaging framework in your Android application. This tutorial walks you through setting up and running the demo.</p>
-
-
-<p>This demo consists of the following pieces: </p>
-<ul>
- <li>A web server containing a page where you can send messages.</li>
- <li>An Android application that receives and displays such messages.</li>
-</ul>
-<p>See the <a href="{@docRoot}reference/com/google/android/gcm/package-summary.html">reference</a> for the client and server helper libraries used in this demo.</p>
-
-<p>The sections below describe how to download the demo code and helper libraries from the SDK Manager. The demo code and helper libraries are also available at the <a href="http://code.google.com/p/gcm">open source site</a>.
-
-<h2 id="requirements">Requirements</h2>
-<p>For the web server:</p>
-<ul>
- <li> <a href="http://ant.apache.org/">Ant 1.8</a> (it might work with earlier versions, but it's not guaranteed).</li>
- <li>One of the following:
- <ul>
- <li>A running web server compatible with Servlets API version 2.5, such as <a href="http://tomcat.apache.org/">Tomcat 6</a> or <a href="http://jetty.codehaus.org/">Jetty</a>, or</li>
- <li><a href="http://code.google.com/appengine/">Java App Engine SDK</a> version 1.6 or later.</li>
- </ul>
- </li>
- <li>A Google account registered to use GCM.</li>
- <li>The API key for that account.</li>
-</ul>
-<p>For the Android application:</p>
-<ul>
- <li>Emulator (or device) running Android 2.2 with Google APIs.</li>
- <li>The Google API project number of the account registered to use GCM.</li>
-</ul>
-<h2 id="gcm-setup">Setting Up GCM</h2>
-<p>Before proceeding with the server and client setup, it's necessary to register a Google account with the Google API Console, enable Google Cloud Messaging in GCM, and obtain an API key from the <a href="https://code.google.com/apis/console">Google API Console</a>.</p>
-<p>For instructions on how to set up GCM, see <a href="gs.html">Getting Started</a>.</p>
-
-
-<h2 id="server-setup">Setting Up the Server</h2>
-<p>This section describes the different options for setting up a server.</p>
-<h3 id="webserver-setup">Using a standard web server</h3>
-<p>To set up the server using a standard, servlet-compliant web server:</p>
-<ol>
- <li> From the SDK Manager, install <strong>Extras > Google Cloud Messaging for Android Library</strong>.
-
-
- <p>This creates a <code>gcm</code> directory under <code><em>YOUR_SDK_ROOT</em>/extras/google/</code> containing these subdirectories: <code>gcm-client</code>, <code>gcm-server</code>, <code>samples/gcm-demo-client</code>, <code>samples/gcm-demo-server</code>, and <code>samples/gcm-demo-appengine</code>.</p>
-
-<p class="note"><strong>Note:</strong> If you don't see <strong>Extras > Google Cloud Messaging for Android Library</strong> in the SDK Manager, make sure you are running version 20 or higher. Be sure to restart the SDK Manager after updating it.</p>
- </li>
-
- <li>In a text editor, edit the <code>samples/gcm-demo-server/WebContent/WEB-INF/classes/api.key</code> and replace the existing text with the API key obtained above.</li>
- <li>In a shell window, go to the <code>samples/gcm-demo-server</code> directory.</li>
- <li>Generate the server's WAR file by running <code>ant war</code>:</li>
-
- <pre class="prettyprint">$ ant war
-
-Buildfile:build.xml
-
-init:
- [mkdir] Created dir: build/classes
- [mkdir] Created dir: dist
-
-compile:
- [javac] Compiling 6 source files to build/classes
-
-war:
- [war] Building war: <strong>dist/gcm-demo.war</strong>
-
-BUILD SUCCESSFUL
-Total time: 0 seconds
-</pre>
-
- <li>Deploy the <code>dist/gcm-demo.war</code> to your running server. For instance, if you're using Jetty, copy <code>gcm-demo.war</code> to the <code>webapps</code> directory of the Jetty installation.</li>
- <li>Open the server's main page in a browser. The URL depends on the server you're using and your machine's IP address, but it will be something like <code>http://192.168.1.10:8080/gcm-demo/home</code>, where <code>gcm-demo</code> is the application context and <code>/home</code> is the path of the main servlet.
-
- </li>
-</ol>
-<p class="note"><strong>Note:</strong> You can get the IP by running <code>ifconfig</code> on Linux or MacOS, or <code>ipconfig</code> on Windows. </p>
-
-<p> You server is now ready.</p>
-<h3 id="appengine-setup">Using App Engine for Java</h3>
-
-<p>To set up the server using a standard App Engine for Java:</p>
-<ol>
- <li> From the SDK Manager, install <strong>Extras > Google Cloud Messaging for Android Library</strong>.
- <p>This creates a <code>gcm</code> directory under <code><em>YOUR_SDK_ROOT</em>/extras/google/</code> containing these subdirectories: <code>gcm-client</code>, <code>gcm-server</code>, <code>samples/gcm-demo-client</code>, <code>samples/gcm-demo-server</code>, and <code>samples/gcm-demo-appengine</code>.</p>
- </li>
- <li>In a text editor, edit <code>samples/gcm-demo-appengine/src/com/google/android/gcm/demo/server/ApiKeyInitializer.java</code> and replace the existing text with the API key obtained above.
- <p class="note"><strong>Note:</strong> The API key value set in that class will be used just once to create a persistent entity on App Engine. If you deploy the application, you can use App Engine's <code>Datastore Viewer</code> to change it later.</p>
-
- </li>
- <li>In a shell window, go to the <code>samples/gcm-demo-appengine</code> directory.</li>
- <li>Start the development App Engine server by <code>ant runserver</code>, using the <code>-Dsdk.dir</code> to indicate the location of the App Engine SDK and <code>-Dserver.host</code> to set your server's hostname or IP address:</li>
-
-<pre class="prettyprint">
-$ ant -Dsdk.dir=/opt/google/appengine-java-sdk runserver -Dserver.host=192.168.1.10
-Buildfile: gcm-demo-appengine/build.xml
-
-init:
- [mkdir] Created dir: gcm-demo-appengine/dist
-
-copyjars:
-
-compile:
-
-datanucleusenhance:
- [enhance] DataNucleus Enhancer (version 1.1.4) : Enhancement of classes
- [enhance] DataNucleus Enhancer completed with success for 0 classes. Timings : input=28 ms, enhance=0 ms, total=28 ms. Consult the log for full details
- [enhance] DataNucleus Enhancer completed and no classes were enhanced. Consult the log for full details
-
-runserver:
- [java] Jun 15, 2012 8:46:06 PM com.google.apphosting.utils.jetty.JettyLogger info
- [java] INFO: Logging to JettyLogger(null) via com.google.apphosting.utils.jetty.JettyLogger
- [java] Jun 15, 2012 8:46:06 PM com.google.apphosting.utils.config.AppEngineWebXmlReader readAppEngineWebXml
- [java] INFO: Successfully processed gcm-demo-appengine/WebContent/WEB-INF/appengine-web.xml
- [java] Jun 15, 2012 8:46:06 PM com.google.apphosting.utils.config.AbstractConfigXmlReader readConfigXml
- [java] INFO: Successfully processed gcm-demo-appengine/WebContent/WEB-INF/web.xml
- [java] Jun 15, 2012 8:46:09 PM com.google.android.gcm.demo.server.ApiKeyInitializer contextInitialized
- [java] SEVERE: Created fake key. Please go to App Engine admin console, change its value to your API Key (the entity type is 'Settings' and its field to be changed is 'ApiKey'), then restart the server!
- [java] Jun 15, 2012 8:46:09 PM com.google.appengine.tools.development.DevAppServerImpl start
- [java] INFO: The server is running at http://192.168.1.10:8080/
- [java] Jun 15, 2012 8:46:09 PM com.google.appengine.tools.development.DevAppServerImpl start
- [java] INFO: The admin console is running at http://192.168.1.10:8080/_ah/admin
-</pre>
-
- <li>Open the server's main page in a browser. The URL depends on the server you're using and your machine's IP address, but it will be something like <code>http://192.168.1.10:8080/home</code>, where <code>/home</code> is the path of the main servlet.</li>
-
- <p class="note"><strong>Note:</strong> You can get the IP by running <code>ifconfig</code> on Linux or MacOS, or <code>ipconfig</code> on Windows.</p>
-
-</ol>
-<p> You server is now ready.</p>
-<h2 id="device-setup">Setting Up the Device</h2>
-<p>To set up the device:</p>
-<ol>
- <li> From the SDK Manager, install <strong>Extras > Google Cloud Messaging for Android Library</strong>.
- <p>This creates a <code>gcm</code> directory under <code><em>YOUR_SDK_ROOT</em>/extras/google</code> containing these subdirectories: <code>gcm-client</code>, <code>gcm-server</code>, <code>samples/gcm-demo-client</code>, <code>samples/gcm-demo-server</code>, and <code>samples/gcm-demo-appengine</code>.</p>
- </li>
- <li>Using a text editor, open <code>samples/gcm-demo-client/src/com/google/android/gcm/demo/app/CommonUtilities.java</code> and set the proper values for the <code>SENDER_ID</code> and <code>SERVER_URL</code> constants. For example:</li>
-
-<pre class="prettyprint pretty-java">
-static final String SERVER_URL = "http://192.168.1.10:8080/gcm-demo";
-static final String SENDER_ID = "4815162342";</pre>
-<p>Note that the <code>SERVER_URL</code> is the URL for the server and the application's context (or just server, if you are using App Engine), and it does not include the forward slash (<code>/</code>). Also note that <code>SENDER_ID</code> is the Google API project number you obtained in the server setup steps above.</p>
-
- <li>In a shell window, go to the <code>gcm-demo-client</code> directory.</li>
- <li>Use the SDK's <code>android</code> tool to generate the <code>ant</code> build files:</li>
-
-<pre class="prettyprint">
-$ android update project --name GCMDemo -p . --target android-16
-Updated project.properties
-Updated local.properties
-Updated file ./build.xml
-Updated file ./proguard-project.txt
-</pre>
-<p>If this command fails becase <code>android-16</code> is not recognized, try a different target (as long as it is at least <code>android-15</code>).</p>
-
-<li>Use <code>ant</code> to build the application's APK file:</li>
-
- <pre class="prettyprint">
-$ ant clean debug
-Buildfile: build.xml
-
-...
-
-
--do-debug:
-[zipalign] Running zip align on final apk...
- [echo] Debug Package: bin/GCMDemo-debug.apk
-[propertyfile] Creating new property file: <strong>bin/build.prop</strong>
-[propertyfile] Updating property file: bin/build.prop
-[propertyfile] Updating property file: bin/build.prop
-[propertyfile] Updating property file: bin/build.prop
-
--post-build:
-
-debug:
-
-BUILD SUCCESSFUL
-Total time: 3 seconds
- </pre>
-
-<li>Start the Android emulator:</li>
-<pre class="prettyprint">$emulator -avd my_avd
-</pre>
-
-<p> This example assumes there is an AVD (Android Virtual Device) named <code>my_avd</code> previously configured with Android 2.2 and Google APIs level 8. For more information on how to run an Android emulator, see <a href="{@docRoot}tools/devices/index.html">Managing Virtual Devices</a> in the Android Developers Guide.</p>
-
-<li>Make sure there is a Google account added to the emulator. It doesn't have to be any account (like the <code>senderId</code>) in particular. </li>
-
-<p> If the emulator is running Android 4.0.4 or later, this step is optional as GCM does not require an account from this version on.</p>
-
-<li>Install the application in the emulator:</li>
-
-<pre class="prettyprint">
-$ ant installd
-Buildfile: gcm-demo-client/build.xml
-
--set-mode-check:
-
--set-debug-files:
-
-install:
- [echo] Installing gcm-demo-client/bin/GCMDemo-debug.apk onto default emulator or device...
- [exec] 1719 KB/s (47158 bytes in 0.026s)
- [exec] pkg: /data/local/tmp/GCMDemo-debug.apk
- [exec] Success
-
-installd:
-
-BUILD SUCCESSFUL
-Total time: 3 seconds
-</pre>
- <li>In the emulator, launch the GCM Demo app. The initial screen should look like this:</li>
- <p><img src="{@docRoot}images/gcm/gcm-avd-home-auto-reg.png" class="screenshot" /></p>
-<p class="note"><strong>Note:</strong> What happened? When the device received a registration callback intent from GCM, it contacted the server to register itself, using the register servlet and passing the registration ID received from GCM; the server then saved the registration ID to use it to send messages to the phone.</p>
-<li> Now go back to your browser and refresh the page. It will show that there is one device registered:</li>
-
-<p><img src="{@docRoot}images/gcm/gcm-device-reg.png" class="screenshot" /></p>
-
-<li>Click on <strong>Send Message</strong>. The browser should show:</li>
-<p><img src="{@docRoot}images/gcm/gcm-sent-server.png" class="screenshot" /></p>
-
-<p>And in your emulator:</p>
-
-<p><img src="{@docRoot}images/gcm/gcm-avd-first-msg.png" class="screenshot" /></p>
-
-<p class="note"><strong>Note:</strong> What happened? When you clicked the button, the web server sent a message to GCM addressed to your device (more specifically, to the registration ID returned by GCM during the registration step). The device then received the message and displayed in the main activity; it also issued a system notification so the user would be notified even if the demo application was not running.</p>
-</ol>
-
diff --git a/docs/html/google/gcm/gcm.jd b/docs/html/google/gcm/gcm.jd
deleted file mode 100644
index d4bb45e..0000000
--- a/docs/html/google/gcm/gcm.jd
+++ /dev/null
@@ -1,307 +0,0 @@
-page.title=Overview
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
-
-<h2>In this document</h2>
-
-<ol class="toc">
- <li><a href="#key">Key Concepts</a></li>
- <li><a href="#arch">Architectural Overview</a></li>
- <li><a href="#lifecycle">Lifecycle Flow</a></li>
- <li><a href="#reg">Register to enable GCM</a></li>
-</ol>
-
-</div>
-</div>
-
-<p>Google Cloud Messaging (GCM) is a free service that enables developers
-to send downstream messages (from servers to GCM-enabled client apps), and
-upstream messages (from the GCM-enabled client apps to servers).
-This could be a lightweight message telling the client app
-that there is new data to be fetched from the server (for instance, a "new email"
-notification informing the app that it is out of sync with the back end),
-or it could be a message containing up to 4kb of payload
-data (so apps like instant messaging can consume the message directly). The GCM
-service handles all aspects of queueing of messages and delivery to and from
-the target client app.</p>
-
-
-<h2 id="key">Key Concepts</h2>
-
-<p>This table summarizes the key terms and concepts involved in GCM. It is
-divided into these categories:</p>
-<ul>
- <li><strong>Components</strong> — The entities that play a primary role in
-GCM.</li>
- <li><strong>Credentials</strong> — The IDs and tokens that are used in
-GCM to ensure that all parties have been authenticated, and
-that the message is going to the correct place.</li>
-</ul>
-
-<p class="table-caption" id="table1">
- <strong>Table 1.</strong> GCM components and credentials.</p>
-
-<table>
- <tr>
- <th colspan="2">Components</th>
- </tr>
-<tr>
- <td><strong>GCM Connection Servers</strong></td>
- <td>The Google-provided servers involved in sending messages between the
-3rd-party app server and the client app.</td>
- </tr>
- <tr>
- <td><strong>Client App</strong></td>
- <td>A GCM-enabled client app that communicates with a 3rd-party app server.</td>
- </tr>
- <tr>
- <td><strong>3rd-party App Server</strong></td>
- <td>An app server that you write as part of implementing
-GCM. The 3rd-party app server sends data to a client app via
-the GCM connection server.</td>
- </tr>
- <tr>
- <th colspan="2">Credentials</th>
- </tr>
- <tr>
- <td id="senderid"><strong>Sender ID</strong></td>
- <td>A project number you acquire from the API console, as described in
-<a href="gs.html#create-proj">Getting Started</a>. The sender
-ID is used in the <a href="#register">registration process</a> to identify a
-3rd-party app server that is permitted to send messages to the client app.</td>
- </tr>
- <tr>
- <td id="apikey"><strong>Sender Auth Token</strong></td>
- <td>An API key that is saved on the 3rd-party app
-server that gives the app server authorized access to Google services.
-The API key is included in the header of POST requests.
-</td>
- </tr>
- <tr>
- <td><strong>Application ID</strong></td>
- <td>The client app that is registering to receive messages. How this is implemented
-is platform-dependent. For example, an Android app
-is identified by the package name from the <a href="client.html#manifest">manifest</a>.
-This ensures that the messages are targeted to the correct Android app.</td>
- </tr>
- <tr>
- <td><strong>Registration ID</strong></td>
- <td>An ID issued by the GCM servers to the client app that allows
-it to receive messages. Note that registration IDs must be kept secret.
-
-</td>
- </tr>
-
-</table>
-
-<h2 id="arch">Architectural Overview</h2>
-
-<p>A GCM implementation includes a Google-provided
-connection server, a 3rd-party app server that interacts with the connection
-server, and a GCM-enabled client app. For example, this diagram shows GCM
-communicating with a client app on an Android device:</p>
-
-<img src="{@docRoot}images/gcm/GCM-arch.png">
-
-<p class="img-caption">
- <strong>Figure 1.</strong> GCM Architecture.
-</p>
-
-<p>This is how these components interact:</p>
-<ul>
- <li>Google-provided <strong>GCM Connection Servers</strong> take messages from
-a 3rd-party app server and send these messages to a
-GCM-enabled client app (the "client app").
-Currently Google provides connection servers for <a href="http.html">HTTP</a>
-and <a href="ccs.html">XMPP</a>.</li>
- <li>The <strong>3rd-Party App Server</strong> is a component that you
-implement to work with your chosen GCM connection server(s). App servers send
-messages to a GCM connection server; the connection server enqueues and stores the
-message, and then sends it to the client app.
-For more information, see <a href="server.html">Implementing GCM Server</a>.</li>
- <li>The <strong>Client App</strong> is a GCM-enabled client app.
-To receive GCM messages, this app must register with GCM and get a
-registration ID. If you are using the <a href="ccs.html">XMPP</a> (CCS) connection
-server, the client app can send "upstream" messages back to the 3rd-party app server.
-For more information on how to implement the client app, see
-the documentation for your platform.</li>
-</ul>
-
-<h2 id="lifecycle">Lifecycle Flow</h2>
-
-<ul>
- <li><strong>Register to enable GCM</strong>. A client app registers to receive messages.
-For more discussion, see <a href="#register">Register to enable GCM</a>.</li>
- <li><strong>Send and receive downstream messages</strong>.
- <ul>
- <li>Send a message. A 3rd-party app server sends messages to the client app:
- <ol>
- <li>The 3rd-party app server <a href="server.html#send-msg">sends a message</a>
-to GCM connection servers.</li>
- <li>The GCM connection server enqueues and stores the message if the device is offline.</li>
- <li>When the device is online, the GCM connection server sends the message to the device. </li>
- <li>On the device, the client app receives the message according to the platform-specific implementation.
-See your platform-specific documentation for details.</li>
- </ol>
- </li>
- <li>Receive a message. A client app
-receives a message from a GCM server. See your platform-specific documentation for details
-on how a client app in that environment processes the messages it receives.</li>
- </ul>
-</li>
-
- <li><strong>Send and receive upstream messages</strong>. This feature is only available if
-you're using the <a href="ccs.html">XMPP Cloud Connection Server</a> (CCS).
-<ul>
- <li>Send a message. A client app sends messages to the 3rd-party app server:
- <ol>
- <li>On the device, the client app sends messages to XMPP (CCS).See your platform-specific
- documentation for details on how a client app can send a message to XMPP (CCS).</li>
- <li>XMPP (CCS) enqueues and stores the message if the server is disconnected.</li>
- <li>When the 3rd-party app server is re-connected, XMPP (CCS) sends the message to the 3rd-party app server.</li>
- </ol>
- </li>
- <li>Receive a message. A 3rd-party app server receives a message from XMPP (CCS) and then does the following:
- <ol>
- <li>Parses the message header to verify client app sender information.
- <li>Sends "ack" to GCM XMPP connection server to acknowledge receiving the message.
- <li>Optionally parses the message payload, as defined by the client app.
- </ol>
-</li>
-</ul>
-
-</li>
-
-
-</ul>
-
-<h2 id="reg">Register to enable GCM</h2>
-
-<p>Regardless of the platform you're developing on, the first step
-a client app must do is register with GCM. This section covers some of the general
-best practices for registration and unregistration. See your platform-specific docs for
-details on writing a GCM-enabled client app on that platform.</p>
-
-<h3 id="reg-state">Keeping the Registration State in Sync</h3>
-<p>Whenever the app registers as described in
-<a href="{@docRoot}google/gcm/client.html">Implementing GCM Client</a>,
-it should save the registration ID for future use, pass it to the
-3rd-party server to complete the registration, and keep track of
-whether the server completed the registration. If the server fails
-to complete the registration, the client app should retry passing the
-registration ID to 3rd-party app server to complete the registration.
-If this continues to fail, the client app should unregister from GCM.</p>
-
-<p>There are also two other scenarios that require special care:</p>
-<ul>
- <li>Client app update</li>
- <li>Backup and restore
- </li>
-</ul>
-<p><bold>Client app update:</bold> When a client app is updated, it should invalidate its existing registration
-ID, as it is not guaranteed to work with the new version. The recommended way to achieve
-this validation is by storing the current app version when a registration
-ID is stored. Then when the app starts, compare the stored value with
-the current app version. If they do not match, invalidate the stored data
-and start the registration process again.</p>
-
-<p><bold>Backup and restore: </bold> You should not save the registration ID when an app is
-backed up. This is because the registration ID could become invalid by the time
-the app is restored, which would put the app in an invalid state
-(that is, the app thinks it is registered, but the server and GCM do not
-store that registration ID anymore—thus the app will not get more
-messages). The best practice is to initiate the registration process as if the app has been
-installed for the first time.</p>
-
-<h4 id="canonical">Canonical IDs</h4>
-<p>If a bug in the app triggers multiple
-registrations for the same device, it can be hard to reconcile state and you might
-end up with duplicate messages.</p>
-<p>GCM provides a facility called "canonical registration IDs" to easily
-recover from these situations. A canonical registration ID is defined to be the ID
-of the last registration requested by your app. This is the ID that the
-server should use when sending messages to the device.</p>
-<p>If later on you try to send a message using a different registration ID, GCM
-will process the request as usual, but it will include the canonical registration
-ID in the <code>registration_id</code> field of the response. Make sure to replace
-the registration ID stored in your server with this canonical ID, as eventually
-the ID you're using will stop working.</p>
-
-<h3 id="retry">Automatic Retry Using Exponential Back-Off</h3>
-
-<p>When registration or unregistration fails, the app should retry the failed operation.</p>
-<p>In the simplest case, if your app attempts to register and GCM is not a
-fundamental part of the app, the app could simply ignore the error
-and try to register again the next time it starts. Otherwise, it should retry the
-previous operation using exponential back-off. In exponential back-off, each time
-there is a failure, it should wait twice the previous amount of time before trying
-again.
-</p>
-
-<h3 id="unreg">Unregistration</h3>
-
-<p>This section explains when you should unregister in GCM and what happens
-when you do.</p>
-
-<h4 id="unreg-why">Why you should rarely unregister</h4>
-
-<p>You should only need to unregister in rare cases, such as
-if you want an app to stop receiving messages, or if you suspect that the registration ID has
-been compromised. In general, once an app has a registration ID, you shouldn't need
-to change it.</p>
-
-<p>In particular, you should never unregister your app as a mechanism for
-logout or for switching between users, for the following reasons:</p>
-
-<ul>
- <li>A registration ID isn't associated with a particular
- logged in user. If you unregister and then re-register, GCM may return the same
- ID or a different ID—there's no guarantee either way.</li>
-
- <li>Unregistration may take up to 5 minutes to propagate.</li>
- <li>After unregistration, re-registration may again take up to 5 minutes to
-propagate. During this time messages may be rejected due to the state of being
-unregistered, and after all this, messages may still go to the wrong user.</li>
-</ul>
-
-
-<p>To make sure that messages go to the intended user:</p>
-
-<ul>
- <li>Your app server can maintain a mapping between the current user
-and the registration ID.</li>
- <li>The app can then check to ensure that messages it
-receives match the logged in user.</li>
-</ul>
-
-
-<h4 id="unreg-how">How unregistration works</h4>
-
-<p>A client app can be automatically unregistered after it is uninstalled.
-However, this process does not happen right away. What happens in
-this scenario is as follows:</p>
-<ol>
- <li>The end user uninstalls the client app.</li>
- <li>The 3rd-party app server sends a message to GCM server.</li>
- <li>The GCM server sends the message to the GCM client on the device.</li>
- <li>The GCM client on the device receives the message and detects that the client app has been
- uninstalled; the detection details depend on the platform on which the client app is running.
-</li>
- <li>The GCM client on the device informs the GCM server that the client app was uninstalled.</li>
- <li>The GCM server marks the registration ID for deletion.</li>
- <li>The 3rd-party app server sends a message to GCM.</li>
- <li>The GCM returns a <code>NotRegistered</code> error message to the 3rd-party app server.</li>
- <li>The 3rd-party app server deletes the registration ID.
- </li>
-</ol>
-
-<p>Note that it might take a while for the registration ID be completely removed
-from GCM. Thus it is possible that messages sent during step 7 above gets a valid
-message ID as response, even though the message will not be delivered to the client app.
-Eventually, the registration ID will be removed and the server will get a
-<code>NotRegistered</code> error, without any further action being required from
-the 3rd-party server (this scenario happens frequently while an app is
-being developed and tested).</p>
-
diff --git a/docs/html/google/gcm/gs.jd b/docs/html/google/gcm/gs.jd
deleted file mode 100644
index 2331292..0000000
--- a/docs/html/google/gcm/gs.jd
+++ /dev/null
@@ -1,94 +0,0 @@
-page.title=Getting Started on Android
-page.tags=cloud,push,messaging
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
-
-
-<h2>In this document</h2>
-
-<ol class="toc">
-<li><a href="#create-proj">Creating a Google API Project</a></li>
-<li><a href="#gcm-service">Enabling the GCM Service</a></li>
-<li><a href="#access-key">Obtaining an API Key</a></li>
-<li><a href="#next">Next Steps</a></li>
-</ol>
-
-<h2>See Also</h2>
-
-<ol class="toc">
-<li><a href="https://cloud.google.com/console">Google Cloud Console</a></li>
-<li><a href="https://developers.google.com/console/help/new/">Google Cloud Console Help</a></li>
-</ol>
-
-</div>
-</div>
-
-<p>This document tells you how to get started setting up a Google Cloud Messaging
-(GCM) implementation.
-Before you begin, make sure to <a href="/google/play-services/setup.html">set up
-the Google Play Services SDK</a>. You need this SDK to use the
-<a href="{@docRoot}reference/com/google/android/gms/gcm/package-summary.html">
-GCM APIs</a>.</p>
-
-<h2 id="create-proj">Creating a Google API project</h2>
-<p>To create a Google API project:</p>
-<ol>
- <li>Open the <a href="https://cloud.google.com/console">Google Developers Console</a>.
- </li>
- <li>If you haven't created an API project yet, click <strong>Create Project</strong>.</li>
-
- <li>Supply a project name and click <strong>Create</strong>.
-
-<p>Once the project has been created, a page appears that displays your project ID and
-project number. For example, <strong>Project Number: 670330094152</strong>.</p></li>
-
- <li>Copy down your project number. You will use it later on as the
- <a href="{@docRoot}google/gcm/gcm.html#senderid">GCM sender ID</a>.</li>
-
-</ol>
-<h2 id="gcm-service">Enabling the GCM Service</h2>
-<p>To enable the GCM service:</p>
-<ol>
- <li>In the sidebar on the left, select <strong>APIs & auth</strong>. </li>
- <li>In the displayed list of APIs, turn the <strong>Google Cloud Messaging for Android
- </strong> toggle to ON.</li>
-
-</ol>
-<h2 id="access-key">Obtaining an API Key</h2>
-<p>To obtain an API key:</p>
-<ol>
- <li>In the sidebar on the left, select <strong>APIs & auth > Credentials</strong>.</li>
-
- <li>Under <strong>Public API access</strong>, click <strong>Create new key</strong>.</li>
-
-<li>In the <strong>Create a new key</strong> dialog, click <strong>Server key</strong>.</li>
-
-<li>In the resulting configuration dialog, supply your server's IP address. For testing
-purposes, you can use {@code 0.0.0.0/0}.</p></li>
-<li>Click <strong>Create</strong>.</li>
-
-<li>In the refreshed page, copy the
-<a href="{@docRoot}google/gcm/gcm.html#apikey">API key</a>.
-You will need the API key later on to perform authentication in your app server.</li>
-
-<p class="note"><strong>Note:</strong> If you need to rotate the key, click
-<strong>Regenerate key</strong>. A new key will be created. If you think the key has been
-compromised and you want to delete it immediately, click <strong>Delete</strong>.</p>
-</ol>
-
-<h2 id="next">Next Steps</h2>
-
-<p>Once you've finished the tasks listed above, you're ready to start
-implementing GCM. Here is an overview of the basic steps:</p>
-
-<ol>
- <li>Implement an app server (the "3rd-party app server") to interact
-with your chosen GCM connection server. The app server sends data to a
-GCM-enabled Android client app via the GCM connection server. For more
-information about implementing the server side, see <a href="server.html">
-Implementing GCM Server</a>.</li>
-<li>Write your client app. This is the GCM-enabled Android app that runs
-on a device. See <a href="client.html">Implementing GCM Client</a> for more information.</li>
-</ol>
diff --git a/docs/html/google/gcm/helper.jd b/docs/html/google/gcm/helper.jd
deleted file mode 100644
index 19dcdc5..0000000
--- a/docs/html/google/gcm/helper.jd
+++ /dev/null
@@ -1,199 +0,0 @@
-page.title=Using the GCM Helper Libraries
-page.tags=cloud,push,messaging
-@jd:body
-
-<div id="deprecatedSticker">
- <a href="#"
- onclick="$('#naMessage').show();$('#deprecatedSticker').hide();return false">
- <strong>This doc is deprecated</strong></a>
-</div>
-
-
-<div id="naMessage" style="display:block">
-<div><p><strong>The information in this document has been superseded by <a href="server.html">GCM Server</a> and <a href="client.html">GCM Client</a></strong>. Please use the <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">{@code GoogleCloudMessaging}</a> API instead of the GCM client helper library. The GCM server helper library is still valid.</p>
-
- <input style="margin-top:1em;padding:5px" type="button"
- value="That's nice, but I still want to read this document"
-onclick="$('#naMessage').hide();$('#deprecatedSticker').show()" />
-</div>
-</div>
-
-
-<div id="qv-wrapper">
-<div id="qv">
-
-<h2>Quickview</h2>
-
-<ul>
-<li>Walk through the steps of creating a GCM app.</li>
-</ul>
-
-
-<h2>In this document</h2>
-
-<ol class="toc">
-<li><a href="#libs">Installing the Helper Libraries</a></li>
-<li><a href="#android-app">Writing the Android Application</a>
-<li><a href="#server-app">Writing the Server-side Application</a> </li>
-</ol>
-
-<h2>See Also</h2>
-
-<ol class="toc">
-<li><a href="{@docRoot}google/play-services/gcm/gs.html">Getting Started with GCM Extensions</a></li>
-<li><a href="https://services.google.com/fb/forms/gcm/" class="external-link" target="_android">CCS and User Notifications Signup Form</a></li>
-</ol>
-
-</div>
-</div>
-
-<p>This document describes how to write an Android application and the server-side logic, using the client and server <a href="{@docRoot}reference/com/google/android/gcm/package-summary.html">helper libraries</a> provided by GCM.</p>
-
-<p>The helper libraries are one option for creating an Android application that uses GCM. You can alternatively use the approach described in the <a href="{@docRoot}google/gcm/gcm.html#writing_apps">GCM Architectural Overview</a>. If you need to perform upstream messaging, you must use the <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">GoogleCloudMessaging</a> APIs, and <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">GoogleCloudMessaging</a> also provides a streamlined registration process.</p>
-
-<p>For information on how to get started creating an Android GCM application and an example of how to use the <a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">GoogleCloudMessaging</a> APIs, see <a href="{@docRoot}google/gcm/gs.html">Getting Started</a>.</p>
-
-<h2 id="libs">Installing the Helper Libraries</h2>
-<p>To perform the steps described in the following sections, you must first install the
-<a href="{@docRoot}reference/com/google/android/gcm/package-summary.html">helper libraries</a>. Note that while using the helper libraries is recommended, it is not required. See the <a href="gcm.html#writing_apps">GCM Architectural Overview</a> for a description of how to write apps without using the helper libraries.
-
-<p>To install the helper libraries, choose
-<strong>Extras > Google Cloud Messaging for Android Library</strong>
-from the SDK Manager. This creates a <code>gcm</code> directory under
-<code><em>YOUR_SDK_ROOT</em>/extras/google/</code> containing these
-subdirectories: <code>gcm-client</code>, <code>gcm-server</code>,
-<code>samples/gcm-demo-client</code>, <code>samples/gcm-demo-server</code>,
-and <code>samples/gcm-demo-appengine</code>.</p>
-
-<p class="note"><strong>Note:</strong> If you don't see <strong>Extras > Google Cloud Messaging for Android Library</strong> in the SDK Manager, make sure you are running version 20 or higher. Be sure to restart the SDK Manager after updating it.</p>
-
-<h2 id="android-app">Writing the Android Application</h2>
-<p>This section describes the steps involved in writing an Android application that uses GCM.</p>
-<h4>Step 1: Copy the gcm.jar file into your application classpath</h4>
-<p> To write your Android application, first copy the <code>gcm.jar</code> file from the SDK's <code>gcm-client/dist</code> directory to your application classpath.</p>
-<h4>Step 2: Make the following changes in the application's Android manifest</h4>
-<ol>
- <li>GCM requires Android 2.2 or later, so if your application cannot work without GCM, add the following line, where <em>xx</em> is the latest target SDK version:</li>
-
-<pre class="prettyprint pretty-xml"><uses-sdk android:minSdkVersion="8" android:targetSdkVersion="xx"/></pre>
-
- <li>Declare and use a custom permission so only this application can receive GCM messages:<br>
- </li>
-
-<pre class="prettyprint pretty-xml"><permission android:name="my_app_package.permission.C2D_MESSAGE" android:protectionLevel="signature" />
-<uses-permission android:name="my_app_package.permission.C2D_MESSAGE" /> </pre>
-<p> This permission must be called <code>my_app_package.permission.C2D_MESSAGE</code> (where <code>my_app_package</code> is the package name of your app as defined by the manifest tag), otherwise it will not work.</p>
-<p class="note"><strong>Note:</strong> This permission is not required if you are targeting your application to 4.1 or above (i.e., minSdkVersion 16)</p>
-
- <li>Add the following permissions:</li>
-
-<pre class="prettyprint pretty-xml"><!-- App receives GCM messages. -->
-<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
-<!-- GCM connects to Google Services. -->
-<uses-permission android:name="android.permission.INTERNET" />
-<!-- GCM requires a Google account. -->
-<uses-permission android:name="android.permission.GET_ACCOUNTS" />
-<!-- Keeps the processor from sleeping when a message is received. -->
-<uses-permission android:name="android.permission.WAKE_LOCK" /></pre>
-
- <li>Add the following broadcast receiver:</li>
-
-<pre class="prettyprint pretty-xml"><receiver android:name="com.google.android.gcm.GCMBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" >
- <intent-filter>
- <action android:name="com.google.android.c2dm.intent.RECEIVE" />
- <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
- <category android:name="my_app_package" />
- </intent-filter>
-</receiver></pre>
-<p> This broadcast receiver is responsible for handling the 2 intents that can be sent by GCM (<code>com.google.android.c2dm.intent.RECEIVE</code> and <code>com.google.android.c2dm.intent.REGISTRATION</code>) and should be defined in the manifest (rather than programmatically) so that these intents can be received even if the application is not running. By setting the <code>com.google.android.c2dm.permission.SEND</code> permission, you are ensuring that only intents sent by the GCM system framework are sent to the receiver (a regular application cannot issue intents with that permission).</p>
-<p> Notice that <code>android:name</code> in the category tag must be replaced by your application's package name (and the category tag is not required for applications targeted to minSdkVersion 16 and higher).<br>
- </p>
-
- <li>Add the following intent service:</li>
-
-
- <pre class="prettyprint pretty-xml"><service android:name=".GCMIntentService" /></pre>
-
-</ol>
-<p>This intent service will be called by the <code>GCMBroadcastReceiver</code> (which is provided by the GCM library), as shown in the next step. It must be a subclass of <code>com.google.android.gcm.GCMBaseIntentService</code>, must contain a public constructor, and should be named <code>my_app_package.GCMIntentService</code> (unless you use a subclass of <code>GCMBroadcastReceiver</code> that overrides the method used to name the service).</p>
-
-<p>The intent service must also define its sender ID(s). It does this as follows:</p>
-<ul>
- <li>If the value is static, the service's default constructor should call <code>super(senderIds)</code>.</li>
- <li>If the value is dynamic, the service should override the <code>getSenderIds()</code> method.</li>
-</ul>
-
-
-<h4>Step 3: Write the my_app_package.GCMIntentService class</h4>
-
-<p>Next write the <code>my_app_package.GCMIntentService</code> class, overriding the following callback methods (which are called by <code>GCMBroadcastReceiver</code>):<br>
-</p>
-<ul>
- <li><code>onRegistered(Context context, String regId)</code>: Called after a registration intent is received, passes the registration ID assigned by GCM to that device/application pair as parameter. Typically, you should send the <code>regid</code> to your server so it can use it to send messages to this device.</li>
- <li><code>onUnregistered(Context context, String regId)</code>: Called after the device has been unregistered from GCM. Typically, you should send the <code>regid</code> to the server so it unregisters the device.</li>
- <li><code>onMessage(Context context, Intent intent)</code>: Called when your server sends a message to GCM, and GCM delivers it to the device. If the message has a payload, its contents are available as extras in the intent.</li>
- <li><code>onError(Context context, String errorId)</code>: Called when the device tries to register or unregister, but GCM returned an error. Typically, there is nothing to be done other than evaluating the error (returned by errorId) and trying to fix the problem.</li>
- <li> <code>onRecoverableError(Context context, String errorId)</code>: Called when the device tries to register or unregister, but the GCM servers are unavailable. The GCM library will retry the operation using exponential backup, unless this method is overridden and returns false. This method is optional and should be overridden only if you want to display the message to the user or cancel the retry attempts.
- </li>
-</ul>
-
-<p class="note"><strong>Note:</strong> The methods above run in the intent service's thread and hence are free to make network calls without the risk of blocking the UI thread.</p>
-
-<h4> Step 4: Write your application's main activity</h4>
-Add the following import statement in your application's main activity:
-<pre class="prettyprint pretty-java">import com.google.android.gcm.GCMRegistrar;</pre>
-<p> In the <code>onCreate()</code> method, add the following code:</p>
-<pre class="prettyprint pretty-java">GCMRegistrar.checkDevice(this);
-GCMRegistrar.checkManifest(this);
-final String regId = GCMRegistrar.getRegistrationId(this);
-if (regId.equals("")) {
- GCMRegistrar.register(this, SENDER_ID);
-} else {
- Log.v(TAG, "Already registered");
-}</pre>
-<p>The <code>checkDevice()</code> method verifies that the device supports GCM and throws an exception if it does not (for instance, if it is an emulator that does not contain the Google APIs). Similarly, the <code>checkManifest()</code> method verifies that the application manifest contains meets all the requirements described in <a href="#android-app">Writing the Android Application</a> (this method is only necessary when you are developing the application; once the application is ready to be published, you can remove it).</p>
-
-<p>Once the sanity checks are done, the device calls <code>GCMRegsistrar.register()</code> to register the device, passing the <code>SENDER_ID</code> you got when you signed up for GCM. But since the <code>GCMRegistrar</code> singleton keeps track of the registration ID upon the arrival of registration intents, you can call <code>GCMRegistrar.getRegistrationId()</code> first to check if the device is already registered.</p>
-<p class="note"><strong>Note:</strong> It is possible that the device was successfully registered to GCM but failed to send the registration ID to your server, in which case you should retry. See <a href="adv.html#reg-state">Advanced Topics</a> for more details on how to handle this scenario.</p>
-
-<h2 id="server-app">Writing the Server-side Application</h2>
-
-<p>To write the server-side application:</p>
-<ol>
- <li> Copy the <code>gcm-server.jar</code> file from the SDK's <code>gcm-server/dist</code> directory to your server classpath.</li>
- <li>Create a servlet (or other server-side mechanism) that can be used by the Android application to send the registration ID received by GCM . The application might also need to send other information—such as the user's email address or username—so that the server can associate the registration ID with the user owning the device.</li>
- <li>Similarly, create a servlet used to unregister registration IDs.<br>
- </li>
-<li>When the server needs to send a message to the registration ID, it can use the <code>com.google.android.gcm.server.Sender</code> helper class from the GCM library. For example:</li>
-</ol>
-
-<pre class="prettyprint pretty-java">import com.google.android.gcm.server.*;
-
-Sender sender = new Sender(myApiKey);
-Message message = new Message.Builder().build();
-MulticastResult result = sender.send(message, devices, 5);</pre>
-
-<p> The snippet above does the following:
-<ul>
-<li>Creates a <code>Sender</code> object using your project's API key.</li>
-<li>Creates a message using a given registration ID (the message builder also has methods to set all message parameters such as the collapse key and payload data).</li>
-<li>Sends the message with a maximum of 5 retry attempts (in case the GCM servers are unavailable), and stores the response on result. </li>
-</ul>
-<p>It's now necessary to parse the result and take the proper action in the following cases:</p>
-<ul>
- <li>If the message was created but the result returned a canonical registration ID, it's necessary to replace the current registration ID with the canonical one.</li>
- <li>If the returned error is <code>NotRegistered</code>, it's necessary to remove that registration ID, because the application was uninstalled from the device.</li>
-</ul>
-<p> Here's a code snippet that handles these 2 conditions:</p>
-<pre class="prettyprint pretty-java">
-if (result.getMessageId() != null) {
- String canonicalRegId = result.getCanonicalRegistrationId();
- if (canonicalRegId != null) {
- // same device has more than on registration ID: update database
- }
-} else {
- String error = result.getErrorCodeName();
- if (error.equals(Constants.ERROR_NOT_REGISTERED)) {
- // application has been removed from device - unregister database
- }
-}</pre>
diff --git a/docs/html/google/gcm/http.jd b/docs/html/google/gcm/http.jd
deleted file mode 100644
index e36440a..0000000
--- a/docs/html/google/gcm/http.jd
+++ /dev/null
@@ -1,445 +0,0 @@
-page.title=GCM HTTP Connection Server
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
-
-
-<h2>In this document</h2>
-
-<ol class="toc">
- <li><a href="#auth">Authentication</a> </li>
- <li><a href="#request">Request Format</a> </li>
- <li><a href="#response">Response Format</a>
- <ol class="toc">
- <li><a href="#success">Interpreting a success response</a>
- <li><a href="#error_codes">Interpreting an error response</a>
- <li><a href="#example-responses">Example responses</a>
- </ol>
- </li>
- <li><a href="#app-server">Implementing an HTTP-Based App Server</a>
-</ol>
-
-<h2>See Also</h2>
-
-<ol class="toc">
-<li><a href="server-ref.html">Server Reference</a></li>
-<li><a href="gs.html">Getting Started</a></li>
-<li><a href="client.html">Implementing GCM Client</a></li>
-<li><a href="ccs.html">Cloud Connection Server</a></li>
-
-
-</ol>
-
-</div>
-</div>
-
-<p>This document describes the Google Cloud Messaging (GCM) HTTP
-connection server. Connection servers
-are the Google-provided servers that take messages from the 3rd-party
-application server and sending them to the device.</p>
-
-<p class="note"><strong>Note:</strong> The content in this document
-applies to <a href="http://developer.chrome.com/apps/cloudMessaging">
-GCM with Chrome apps</a> as well as Android.</p>
-
-<p>See the
-<a href="server-ref.html">Server Reference</a> for a list of all the message
-parameters and which connection server(s) supports them.</p>
-
-
-<h2 id="auth">Authentication</h2>
-
-<p>To send a message, the application server issues a POST request. For example:</p>
-<pre>https://android.googleapis.com/gcm/send</pre>
-<p>A message request is made of 2 parts: HTTP header and HTTP body.</p>
-
-<p>The HTTP header must contain the following headers:</p>
-<ul>
- <li><code>Authorization</code>: key=YOUR_API_KEY</li>
- <li><code>Content-Type</code>: <code>application/json</code> for JSON;
-<code>application/x-www-form-urlencoded;charset=UTF-8</code> for plain text.
-If <code>Content-Type</code> is omitted, the format
-is assumed to be plain text.
- </li>
-</ul>
-
-<p>For example:
-</p>
-
-<pre>Content-Type:application/json
-Authorization:key=AIzaSyB-1uEai2WiUapxCs2Q0GZYzPu7Udno5aA
-
-{
- "registration_ids" : ["APA91bHun4MxP5egoKMwt2KZFBaFUH-1RYqx..."],
- "data" : {
- ...
- },
-}</pre>
-
-
-<p>The HTTP body content depends on whether you're using JSON or plain text.
-See
-<a href="server-ref.html#params">the Server Reference</a> for a list of all the
-parameters your JSON or plain text message can contain.</p>
-
-<h3 id="checkAPIkey">Checking the validity of an API key</h3>
-
-<p>If you receive authentication errors when sending messages, check the validity
-of your API key. For example, on Android, run the following command:</p>
-
-<pre># api_key=YOUR_API_KEY
-
-# curl --header "Authorization: key=$api_key" \
- --header Content-Type:"application/json" \
- https://android.googleapis.com/gcm/send \
- -d "{\"registration_ids\":[\"ABC\"]}"</pre>
-
-<p>
-If you receive a 401 HTTP status code, your API key is not valid. Otherwise you
-should see something like this:</p>
-
-<pre>
-{"multicast_id":6782339717028231855,"success":0,"failure":1,
-"canonical_ids":0,"results":[{"error":"InvalidRegistration"}]}
-</pre>
-
-<p>
-If you want to confirm the validity of a registration ID, you can do so by
-replacing "ABC" with the registration ID.
-</p>
-
-
- <h2 id="request">Request Format</h2>
-
-<p>This section shows you how to format a request for both JSON and plain text. See
-the <a href="server-ref.html#table1">Server Reference</a> for a complete
-list of the fields you can include in a request.</p>
-
- <p>Here is the smallest possible request (a message without any parameters and
-just one recipient) using JSON:</p>
-
- <pre class="prettyprint pretty-json">{ "registration_ids": [ "42" ] }</pre>
-
- <p>And here the same example using plain text:</p>
- <pre class="prettyprint">registration_id=42</pre>
-
- <p> Here is a message with a payload and 6 recipients:</p>
-
- <pre class="prettyprint pretty-HTML">{ "data": {
- "score": "5x1",
- "time": "15:10"
- },
- "registration_ids": ["4", "8", "15", "16", "23", "42"]
-}</pre>
- <p>Here is a message with all optional fields set and 6 recipients:</p>
- <pre class="prettyprint pretty-json">{ "collapse_key": "score_update",
- "time_to_live": 108,
- "delay_while_idle": true,
- "data": {
- "score": "4x8",
- "time": "15:16.2342"
- },
- "registration_ids":["4", "8", "15", "16", "23", "42"]
-}</pre>
- <p>And here is the same message using plain-text format (but just 1 recipient): </p>
-
- <pre class="prettyprint">collapse_key=score_update&time_to_live=108&delay_while_idle=1&data.score=4x8&data.time=15:16.2342&registration_id=42
- </pre>
-
-<p>Here is a message that includes a notification key and payload:</p>
-
-<pre>
-{
- "data": {
- "message": "ciao"
- },
- "notification_key":"aUniqueKey"
-}
-</pre>
-
-<p>For more information about notifications and how to use them, see
-<a href="{@docRoot}google/gcm/notifications.html">User Notifications</a>.</p>
-
-
-<p class="note"><strong>Note:</strong> If your organization has a firewall
-that restricts the traffic to or
-from the Internet, you need to configure it to allow connectivity with GCM in order for
-your GCM client apps to receive messages.
-The ports to open are: 5228, 5229, and 5230. GCM typically only uses 5228, but
-it sometimes uses 5229 and 5230. GCM doesn't provide specific IPs, so you should allow
-your firewall to accept outgoing connections to all IP addresses
-contained in the IP blocks listed in Google's ASN of 15169.</p>
-
-
-
-<h2 id="response">Response format</h2>
-
-<p>There are two possible outcomes when trying to send a message:</p>
-<ul>
- <li>The message is processed successfully. The HTTP response has a 200 status, and
-the body contains more information about the status of the message (including possible errors).</li>
- <li>The GCM server rejects the request. The HTTP response contains a
-non-200 status code (such as 400, 401 or 5xx).</li>
-</ul>
-
-<p>When a JSON request is successful (HTTP status code 200), the JSON object returned
-contains the <a href="{@docRoot}google/gcm/server-ref.html#table4">
-Downstream HTTP message response body</a>.</p>
-
-<p>If the value of <code>failure</code> and <code>canonical_ids</code> is 0, it's
-not necessary to parse the remainder of the response. Otherwise, we recommend
-that you iterate through the results field and do the following for each object
-in that list:</p>
-<ul>
- <li>If <code>message_id</code> is set, check for <code>registration_id</code>:
- <ul>
- <li>If <code>registration_id</code> is set, replace the original ID with
-the new value (canonical ID) in your server database. Note that the original ID
-is not part of the result, so you need to obtain it from the list of
-code>registration_ids</code> passed in the request (using the same index).</li>
- </ul>
- </li>
- <li>Otherwise, get the value of <code>error</code>:
- <ul>
- <li>If it is <code>Unavailable</code>, you could retry to send it in another
-request.</li>
- <li>If it is <code>NotRegistered</code>, you should remove the registration
-ID from your server database because the application was uninstalled from the
-device, or the client app isn't configured to receive
-messages.</li>
- <li>Otherwise, there is something wrong in the registration ID passed in
-the request; it is probably a non-recoverable error that will also require removing
-the registration from the server database. See
-<a href="{@docRoot}google/gcm/server-ref.html#error-codes">Downstream message error response
- codes</a> for all possible error values.</li>
- </ul>
- </li>
-</ul>
-
-<p>When a plain-text request is successful (HTTP status code 200), the response
-body contains 1 or 2 lines in the form of key/value pairs.
-The first line is always available and its content is either <code>id=<em>ID of
-sent message</em></code> or <code>Error=<em>GCM error code</em></code>. The second
-line, if available,
-has the format of <code>registration_id=<em>canonical ID</em></code>. The second
-line is optional, and it can only be sent if the first line is not an error. We
-recommend handling the plain-text response in a similar way as handling the
-JSON response:</p>
-<ul>
- <li>If first line starts with <code>id</code>, check second line:
- <ul>
- <li>If second line starts with <code>registration_id</code>, gets its value
-and replace the registration IDs in your server database.</li>
- </ul>
- </li>
- <li>Otherwise, get the value of <code>Error</code>:
- <ul>
- <li>If it is <code>NotRegistered</code>, remove the registration ID from
-your server database.</li>
- <li>Otherwise, there is probably a non-recoverable error (<strong>Note:
-</strong>Plain-text requests will never return <code>Unavailable</code> as the
-error code, they would have returned a 500 HTTP status instead).</li>
- </ul>
- </li>
-</ul>
-
-
-<h3 id="example-responses">Example responses</h3>
-<p>This section shows a few examples of responses indicating messages that were
-processed successfully. See <a href="#request">Request Format</a> for
-the requests these responses are based on.</p>
-<p> Here is a simple case of a JSON message successfully sent to one recipient
-without canonical IDs in the response:</p>
-<pre class="prettyprint pretty-json">{ "multicast_id": 108,
- "success": 1,
- "failure": 0,
- "canonical_ids": 0,
- "results": [
- { "message_id": "1:08" }
- ]
-}</pre>
-
-<p>Or if the request was in plain-text format:</p>
-<pre class="prettyprint">id=1:08
-</pre>
-
-<p>Here are JSON results for 6 recipients (IDs 4, 8, 15, 16, 23, and 42 respectively)
-with 3 messages successfully processed, 1 canonical registration ID returned,
-and 3 errors:</p>
-<pre class="prettyprint pretty-json">{ "multicast_id": 216,
- "success": 3,
- "failure": 3,
- "canonical_ids": 1,
- "results": [
- { "message_id": "1:0408" },
- { "error": "Unavailable" },
- { "error": "InvalidRegistration" },
- { "message_id": "1:1516" },
- { "message_id": "1:2342", "registration_id": "32" },
- { "error": "NotRegistered"}
- ]
-}
-</pre>
-<p> In this example:</p>
-<ul>
- <li>First message: success, not required.</li>
- <li>Second message: should be resent (to registration ID 8).</li>
- <li>Third message: had an unrecoverable error (maybe the value got corrupted
-in the database).</li>
- <li>Fourth message: success, nothing required.</li>
- <li>Fifth message: success, but the registration ID should be updated in the
-server database (from 23 to 32).</li>
- <li>Sixth message: registration ID (42) should be removed from the server database
-because the application was uninstalled from the device.</li>
-</ul>
-<p>Or if just the 4th message above was sent using plain-text format:</p>
-<pre class="prettyprint">Error=InvalidRegistration
-</pre>
-<p>If the 5th message above was also sent using plain-text format:</p>
-<pre class="prettyprint">id=1:2342
-registration_id=32
-</pre>
-
-
-<h2 id="app-server">Implementing an HTTP-Based App Server</h2>
-
-<p>This section gives examples of implementing an app server that works with the
-GCM HTTP connection server. Note that a full GCM implementation requires a
-client-side implementation, in addition to the server. This example is based on Android.</a>
-
-
-<p>Requirements</p>
-<p>For the web server:</p>
-<ul>
- <li> <a href="http://ant.apache.org/">Ant 1.8</a> (it might work with earlier versions, but it's not guaranteed).</li>
- <li>One of the following:
- <ul>
- <li>A running web server compatible with Servlets API version 2.5, such as
-<a href="http://tomcat.apache.org/">Tomcat 6</a> or <a href="http://jetty.codehaus.org/">Jetty</a>, or</li>
- <li><a href="http://code.google.com/appengine/">Java App Engine SDK</a>
-version 1.6 or later.</li>
- </ul>
- </li>
- <li>A Google account registered to use GCM.</li>
- <li>The API key for that account.</li>
-</ul>
-<p>For the Android application:</p>
-<ul>
- <li>Emulator (or device) running Android 2.2 (ideally, 2.3 or above) with Google APIs.</li>
- <li>The Google API project number of the account registered to use GCM.</li>
-</ul>
-
-<h3 id="gcm-setup">Setting Up GCM</h3>
-<p>Before proceeding with the server and client setup, it's necessary to register
-a Google account with the Google API Console, enable Google Cloud Messaging in GCM,
-and obtain an API key from the <a href="https://code.google.com/apis/console">
-Google API Console</a>.</p>
-<p>For instructions on how to set up GCM, see <a href="gs.html">Getting Started</a>.</p>
-
-
-<h3 id="server-setup">Setting Up an HTTP Server</h3>
-<p>This section describes the different options for setting up an HTTP server.</p>
-
-<h4 id="webserver-setup">Using a standard web server</h4>
-<p>To set up the server using a standard, servlet-compliant web server:</p>
-<ol>
- <li>From the <a href="http://code.google.com/p/gcm">open source site</a>,
-download the following directories: <code>gcm-server</code>,
-<code>samples/gcm-demo-server</code>, and <code>samples/gcm-demo-appengine</code>.</p>
-
-
- <li>In a text editor, edit the <code>samples/gcm-demo-server/WebContent/WEB-INF/classes/api.key</code> and replace the existing text with the API key obtained above.</li>
- <li>In a shell window, go to the <code>samples/gcm-demo-server</code> directory.</li>
- <li>Generate the server's WAR file by running <code>ant war</code>:</li>
-
- <pre class="prettyprint">$ ant war
-
-Buildfile:build.xml
-
-init:
- [mkdir] Created dir: build/classes
- [mkdir] Created dir: dist
-
-compile:
- [javac] Compiling 6 source files to build/classes
-
-war:
- [war] Building war: <strong>dist/gcm-demo.war</strong>
-
-BUILD SUCCESSFUL
-Total time: 0 seconds
-</pre>
-
- <li>Deploy the <code>dist/gcm-demo.war</code> to your running server. For instance, if you're using Jetty, copy <code>gcm-demo.war</code> to the <code>webapps</code> directory of the Jetty installation.</li>
- <li>Open the server's main page in a browser. The URL depends on the server you're using and your machine's IP address, but it will be something like <code>http://192.168.1.10:8080/gcm-demo/home</code>, where <code>gcm-demo</code> is the application context and <code>/home</code> is the path of the main servlet.
-
- </li>
-</ol>
-<p class="note"><strong>Note:</strong> You can get the IP by running <code>ifconfig</code> on Linux or MacOS, or <code>ipconfig</code> on Windows. </p>
-
-<p> You server is now ready.</p>
-
-<h4 id="appengine-setup">Using App Engine for Java</h4>
-
-<p>To set up the server using a standard App Engine for Java:</p>
-<ol>
- <li>Get the files from the <a href="http://code.google.com/p/gcm">open source
-site</a>, as described above.</p>
- </li>
- <li>In a text editor, edit
-<code>samples/gcm-demo-appengine/src/com/google/android/gcm/demo/server/ApiKeyInitializer.java</code>
-and replace the existing text with the API key obtained above.
-
- <p class="note"><strong>Note:</strong> The API key value set in that class will
-be used just once to create a persistent entity on App Engine. If you deploy
-the application, you can use App Engine's <code>Datastore Viewer</code> to change
-it later.</p>
-
- </li>
- <li>In a shell window, go to the <code>samples/gcm-demo-appengine</code> directory.</li>
- <li>Start the development App Engine server by <code>ant runserver</code>,
-using the <code>-Dsdk.dir</code> to indicate the location of the App Engine SDK
-and <code>-Dserver.host</code> to set your server's hostname or IP address:</li>
-
-<pre class="prettyprint">
-$ ant -Dsdk.dir=/opt/google/appengine-java-sdk runserver -Dserver.host=192.168.1.10
-Buildfile: gcm-demo-appengine/build.xml
-
-init:
- [mkdir] Created dir: gcm-demo-appengine/dist
-
-copyjars:
-
-compile:
-
-datanucleusenhance:
- [enhance] DataNucleus Enhancer (version 1.1.4) : Enhancement of classes
- [enhance] DataNucleus Enhancer completed with success for 0 classes. Timings : input=28 ms, enhance=0 ms, total=28 ms. Consult the log for full details
- [enhance] DataNucleus Enhancer completed and no classes were enhanced. Consult the log for full details
-
-runserver:
- [java] Jun 15, 2012 8:46:06 PM com.google.apphosting.utils.jetty.JettyLogger info
- [java] INFO: Logging to JettyLogger(null) via com.google.apphosting.utils.jetty.JettyLogger
- [java] Jun 15, 2012 8:46:06 PM com.google.apphosting.utils.config.AppEngineWebXmlReader readAppEngineWebXml
- [java] INFO: Successfully processed gcm-demo-appengine/WebContent/WEB-INF/appengine-web.xml
- [java] Jun 15, 2012 8:46:06 PM com.google.apphosting.utils.config.AbstractConfigXmlReader readConfigXml
- [java] INFO: Successfully processed gcm-demo-appengine/WebContent/WEB-INF/web.xml
- [java] Jun 15, 2012 8:46:09 PM com.google.android.gcm.demo.server.ApiKeyInitializer contextInitialized
- [java] SEVERE: Created fake key. Please go to App Engine admin console, change its value to your API Key (the entity type is 'Settings' and its field to be changed is 'ApiKey'), then restart the server!
- [java] Jun 15, 2012 8:46:09 PM com.google.appengine.tools.development.DevAppServerImpl start
- [java] INFO: The server is running at http://192.168.1.10:8080/
- [java] Jun 15, 2012 8:46:09 PM com.google.appengine.tools.development.DevAppServerImpl start
- [java] INFO: The admin console is running at http://192.168.1.10:8080/_ah/admin
-</pre>
-
- <li>Open the server's main page in a browser. The URL depends on the server
-you're using and your machine's IP address, but it will be something like
-<code>http://192.168.1.10:8080/home</code>, where <code>/home</code>
-is the path of the main servlet.</li>
-
- <p class="note"><strong>Note:</strong> You can get the IP by running <code>ifconfig</code>
-on Linux or MacOS, or <code>ipconfig</code> on Windows.</p>
-
-</ol>
-<p> You server is now ready.</p>
diff --git a/docs/html/google/gcm/index.jd b/docs/html/google/gcm/index.jd
deleted file mode 100644
index af5d741..0000000
--- a/docs/html/google/gcm/index.jd
+++ /dev/null
@@ -1,70 +0,0 @@
-page.title=Google Cloud Messaging for Android
-page.tags=gcm
-header.hide=1
-@jd:body
-
-
-<div class="landing-banner">
-
-<div class="col-5" style="min-height:100px">
- <img src="{@docRoot}images/gcm/gcm-logo.png" />
-</div>
-<div class="col-7">
-
- <h1 itemprop="name" style="margin-bottom:0;">Google Cloud Messaging for Android</h1>
- <p itemprop="description">
- Google Cloud Messaging (GCM) for Android is a service that allows you to send data
-from your server to your users' Android-powered device, and also to receive messages from
-devices on the same connection. The GCM service handles all aspects of queueing of messages
-and delivery to the target Android application running on the target device, and it is
-completely free.
-</p>
-
-</div>
-</div>
-
-<div class="landing-docs">
- <div class="col-6 normal-links">
- <h3 style="clear:left">Key Developer Features</h3>
- <h4>Send data from your server to users' Android-powered devices</h4>
- <p>This could be a lightweight
-message telling your app there is new data to be fetched from the
-server (for instance, a movie uploaded by a friend), or it could be a message containing
-up to 4kb of payload data (so apps like instant messaging can consume the message directly).
-<a href="{@docRoot}google/gcm/gcm.html">GCM Architectural Overview.</a></p>
-
- <h4>Send "send-to-sync" messages</h4>
- <p>A send-to-sync (collapsible) message is often a "tickle" that tells a mobile
- application to sync data from the server. For example, suppose you have an email
- application. When a user receives new email on the server, the server pings the mobile
- application with a "New mail" message. This tells the application to sync to the server
- to pick up the new email.
- <a href="{@docRoot}google/gcm/adv.html#s2s">Learn more »</a></p>
-
- <h4>Send messages with payload</h4>
- <p>Unlike a send-to-sync message, every "message with payload" (non-collapsible message)
- is delivered. The payload the message contains can be up to 4kb.
- <a href="{@docRoot}google/gcm/adv.html#payload">Learn more »</a></p>
- </div>
-
-
- <div class="col-6 normal-links">
- <h3 style="clear:left">New Features</h3>
-
-
-
- <h4>Return Receipts</h4>
- <p>You can use upstream messaging to get receipt notifications, confirming that a given
- message was sent to a device. Your 3rd-party app server receives the receipt notification
- from CCS once the message has been sent to the device.
- <a href="{@docRoot}google/gcm/ccs.html#receipts">Learn more »</a></p>
-
-
- <h4>Get Started</h4>
- <p>Get started with a tutorial that walks you through creating a GCM app.
- <a href="{@docRoot}google/gcm/gs.html">Learn more »</a></p>
- </div>
-
-</div>
-
-
diff --git a/docs/html/google/gcm/notifications.jd b/docs/html/google/gcm/notifications.jd
deleted file mode 100644
index 333d4b6..0000000
--- a/docs/html/google/gcm/notifications.jd
+++ /dev/null
@@ -1,308 +0,0 @@
-page.title=User Notifications
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
-
-<h2>Quickview</h2>
-
-<ul>
-<li>Learn how to send a single message to multiple devices owned by a single user.</li>
-</ul>
-
-
-<h2>In this document</h2>
-
-<ol class="toc">
- <li><a href="#gen-server">Generate a Notification Key on the Server</a></li>
- <li><a href="#gen-client">Generate a Notification Key on the Client</a></li>
- <li><a href="#add">Add Registration IDs</a></li>
- <li><a href="#remove">Remove Registration IDs</a></li>
- <li><a href="#upstream">Send Upstream Messages</a></li>
- <li><a href="#response">Response Formats</a>
- <ol class="toc">
- <li><a href="#response-create">Create/add/remove operations</a>
- <li><a href="#response-send">Send operations</a>
- </ol>
- </li>
-</ol>
-
-<h2>See Also</h2>
-
-<ol class="toc">
-<li><a href="{@docRoot}google/gcm/gs.html">Getting Started</a></li>
-</ol>
-
-</div>
-</div>
-
-<p>With user notifications, 3rd-party app servers can send a single message to
-multiple instance of an app running on devices owned by a single user. This feature
-is called <em>user notifications</em>. User notifications make it possible for every
-app instance that a user owns to reflect the latest messaging state. For example:</p>
-
- <ul>
- <li>If a message has been handled on one device, the GCM message on the other
-devices are dismissed. For example, if a user has handled a calendar notification
-on one device, the notification will go away on the user's other devices.</li>
-
- <li>If a message has not been delivered yet to a device and but it has been handled,
-the GCM server removes it from the unsent queue for the other devices.</li>
-
- <li>Likewise, a device can send messages to the {@code notification_key}, which
-is the token that GCM uses to fan out notifications to all devices whose
-registration IDs are associated with the key.</li>
-</ul>
-
-<p>The way this works is that during registration, the 3rd-party server requests
-a {@code notification_key}. The {@code notification_key} maps a particular user
-to all of the user's associated registration IDs (a regID represents a particular
-Android application running on a particular device). Then instead of sending one
-message to one regID at a time, the 3rd-party server can send a message to to the
-{@code notification_key}, which then sends the message to all of the user's regIDs.</p>
-
-<p class="note"><strong>Note:</strong> A notification dismissal message is like any
-other upstream message, meaning that it will be delivered to the other devices that
-belong to the specified {@code notification_key}. You should design your app to
-handle cases where the app receives a dismissal message, but has not yet displayed
-the notification that is being dismissed. You can solve this by caching the dismissal
-and then reconciling it with the corresponding notification.
-</p>
-
-<p>You can use this feature with either the <a href="ccs.html">XMPP</a> (CCS) or
-<a href="http.html">HTTP</a> connection server.</p>
-
-<p>You can generate notification keys in two different ways: on the server, and on
-the client, if the user has a Google account. All of the associated registration IDs
-can be mapped to a single user.</p>
-
-<p>The examples below show you how to perform generate/add/remove operations,
-and how to send upstream messages. For generate/add/remove operations, the
-message body is JSON.</p>
-
-<h2 id="gen-server">Generate a Notification Key on the Server</h2>
-
-<p>To generate a notification key on the server, you create a new
-<code>notification_key</code> and map it to a
-<code>notification_key_name</code>.</p>
-
-<p>This example shows how to create a new <code>notification_key</code> for a
-<code>notification_key_name</code> called <code>appUser-Chris</code>.
-The {@code notification_key_name} is a name or identifier (it can be a username for
-a 3rd-party app) that is unique to a given user. It is used by third parties to
-group together registration IDs for a single user. Note that <code>notification_key_name</code>
-and <code>notification_key</code> are unique to a group of registration IDs. It is also
-important that <code>notification_key_name</code> be uniquely named per app in case
-you have multiple apps for the same project ID. This ensures that notifications
-only go to the intended target app.</p>
-
-
-<p>A create operation returns a token (<code>notification_key</code>). Third parties
-must save this token (as well as its mapping to the <code>notification_key_name</code>)
-to use in subsequent operations:</p>
-
-<pre>request:
-{
- "operation": "create",
- "notification_key_name": "appUser-Chris",
- "registration_ids": ["4", "8", "15", "16", "23", "42"]
-}</pre>
-
-<h3 id="request-server">Request format</h3>
-
-<p>To send a message in cases where your notification key is generated on the server,
-the application server issues a POST request to
-<code>https://android.googleapis.com/gcm/notification</code>.</p>
-
-<p>Here is the HTTP request header you should use for all server side create/add/remove operations:</p>
-
-<pre>content-type: "application/json"
-Header : "project_id": <projectID>
-Header: "Authorization", "key=API_KEY"
-</pre>
-
-
-<h2 id="gen-client">Generate a Notification Key on the Client</h2>
-
-<p>Generating a notification key on the client is useful for cases where a server is unavailable.
-To generate a notification key on the client, the device must have at least one
-Google account. Note that the process for generating a notification key on the client is significantly
-different from the server process described above.</p>
-
-<p>To generate a notification key on the client:</p>
-
-<ol>
- <li>Open your project in the <a href="https://cloud.google.com/console">Google Developers Console</a>.</li>
- <li>Click <strong>APIS & AUTH > Credentials</strong>.</li>
- <li>Under OAuth, click <strong>Create new Client ID</strong>.</li>
- <li>In the <strong>Create Client ID</strong> dialog, select <strong>Web Application</strong> as
-the application type, and click <strong>Create Client ID</strong>.</li>
- <li>Copy the value from <strong>Client ID for web application > Client ID</strong>.
-This client ID represents a Google account "scope" that you will use to generate an {@code id_token}.</li>
-</ol>
-
-<p>Once you've followed the above steps and gotten a client ID from Google Developers Console,
- you're ready to add this feature to your app. First check the device for the presence of a Google
-account. For example:</p>
-
-<pre>// This snippet takes the simple approach of using the first returned Google account,
-// but you can pick any Google account on the device.
-public String getAccount() {
- Account[] accounts = AccountManager.get(getActivity()).
- getAccountsByType("com.google");
- if (accounts.length == 0) {
- return null;
- }
- return accounts[0].name;
-}</pre>
-
-<p>Next, get an authentication token ({@code id_token}) by using the <code><a href=
-"http://developer.android.com/reference/com/google/android/gms/auth/GoogleAuthUtil.html">GoogleAuthUtil</a></code>
-class. For example:</p>
-
-<pre>String accountName = getAccount();
-
-// Initialize the scope using the client ID you got from the Console.
-final String scope = "audience:server:client_id:"
- + "1262xxx48712-9qs6n32447mcj9dirtnkyrejt82saa52.apps.googleusercontent.com";
-String id_token = null;
-try {
- id_token = GoogleAuthUtil.getToken(context, accountName, scope);
-} catch (Exception e) {
- log("exception while getting id_token: " + e);
-}
-...</pre>
-
-<p>Now use <code>id_token</code> to authenticate your request.
-This add operation returns a {@code notification_key}.
-Third parties must save this {@code notification_key} (as well as its mapping to the
-<code>notification_key_name</code>)
-to use in subsequent operations. Note that a client request only takes a single regID.
-The only operations supported on the client side are add/remove.</p>
-
-<pre>request:
-{
- "operation": "add",
- "notification_key_name": "appUser-Chris",
- "registration_ids": ["4"]
- "id_token": "id_token"
-}</pre>
-
-<h3 id="request-client">Request format</h3>
-
-<p>To send a message in cases where your notification key is generated on the client,
-the application server issues a POST request to
-<code>https://android.googleapis.com/gcm/googlenotification</code>.</p>
-
-<p>Here is the HTTP request header you should use for all add/remove operations. The
-client side doesn't support the create operation;
-the add operation has the effect of creating the notification key if it doesn't already
-exist:</p>
-
-<pre>content-type: "application/json"
-Header : "project_id": <projectID>
-</pre>
-
-<p>Note that the authentication token is passed in the JSON body as shown above, not the header.
-This is different from the server case.</p>
-
-
-<h2 id="add">Add Registration IDs</h2>
-
-<p>This example shows how to add registration IDs for a given notification key.
-The maximum number of members allowed for a {@code notification_key} is 20.</p>
-
-<p>Note that the <code>notification_key_name</code> is not strictly required for
-adding/removing regIDs. But including it protects you against accidentally using
-the incorrect <code>notification_key</code>.</p>
-
-<pre>request:
-{
- "operation": "add",
- "notification_key_name": "appUser-Chris",
- "notification_key": "aUniqueKey"
- "registration_ids": ["4", "8", "15", "16", "23", "42"]
-}</pre>
-
-<h2 id="remove">Remove Registration IDs</h2>
-
-<p>This example shows how to remove registration IDs for a given notification key:</p>
-<pre>request:
-{
- "operation": "remove",
- "notification_key_name": "appUser-Chris",
- "notification_key": "aUniqueKey"
- "registration_ids": ["4", "8", "15", "16", "23", "42"]
-}</pre>
-
-<h2 id="upstream">Send Upstream Messages</h2>
-
-<p>To send an upstream (device-to-cloud) message, you must use the
-<a href="{@docRoot}reference/com/google/android/gms/gcm/GoogleCloudMessaging.html">
-{@code GoogleCloudMessaging}</a> API. Specifying a {@code notification_key} as the target
-for an upstream message allows a user on one device to send a message to other
-devices in the notification group—for example, to dismiss a notification.
-Here is an example that shows targeting a {@code notification_key}:</p>
-
-<pre>GoogleCloudMessaging gcm = GoogleCloudMessaging.get(context);
-String to = NOTIFICATION_KEY;
-AtomicInteger msgId = new AtomicInteger();
-String id = Integer.toString(msgId.incrementAndGet());
-Bundle data = new Bundle();
-data.putString("hello", "world");
-
-gcm.send(to, id, data);
-</pre>
-
-<p>This call generates the necessary XMPP stanza for sending the message. The
-Bundle data consists of a key-value pair.</p>
-
-<p>For a complete example, see <a href="client.html">Implementing GCM Client</a>.
-
-<h2 id="response">Response Formats</h2>
-
-<p>This section shows examples of the responses that can be returned for
-notification key operations.</p>
-
-<h3 id="response-create">Create/add/remove operations</h3>
-
-<p>When you make a request to create a {@code notification_key} or to add/remove its
-regIDs, a successful response always returns the <code>notification_key</code>.
-Use the returned {@code notification_key} for sending messages:</p>
-
-<pre>HTTP status: 200
-{
- "notification_key": "aUniqueKey", // to be used for sending
-}</pre>
-
-
-<h3 id="response-send">Send operations</h3>
-
-<p>For a send operation that has a {@code notification_key} as its target, the
-possible responses are success, partial success, and failure.</p>
-
-<p>Here is an example of "success"—the {@code notification_key} has 2 regIDs
-associated with it, and the message was successfully sent to both of them:</p>
-
-<pre>{
- "success": 2,
- "failure": 0
-}</pre>
-
-<p>Here is an example of "partial success"—the {@code notification_key} has
-3 regIDs associated with it. The message was successfully send to 1 of the regIDs,
-but not to the other 2. The response message lists the regIDs that failed to
-receive the message:</p>
-
-<pre>{
- "success":1,
- "failure":2,
- "failed_registration_ids":[
- "regId1",
- "regId2"
- ]
-}</pre>
-
-<p>In the case of failure, the response has HTTP code 503 and no JSON. When a message
-fails to be delivered to one or more of the regIDs associated with a {@code notification_key},
-the 3rd-party server should retry.</p>
diff --git a/docs/html/google/gcm/server-ref.jd b/docs/html/google/gcm/server-ref.jd
deleted file mode 100644
index 2a41e58..0000000
--- a/docs/html/google/gcm/server-ref.jd
+++ /dev/null
@@ -1,764 +0,0 @@
-page.title=Server Reference
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
-
-<h2>In this document</h2>
-
-<ol class="toc">
- <li><a href="#downstream">Downstream Messages</a></li>
-<ol class="toc">
- <li><a href="#send-downstream">Sending a downstream message</a></li>
- <li><a href="#interpret-downstream">Interpreting a downstream message response</a></li>
- </ol>
- <li><a href="#upstream">Upstream Messages (XMPP)</a>
- <ol class="toc">
- <li><a href="#interpret-upstream">Interpreting an upstream XMPP message</a></li>
- <li><a href="#upstream-response">Sending an upstream XMPP message response</a></li>
- </ol>
- </li>
-<li><a href="#ccs">Cloud Connection Server Messages (XMPP)</a></li>
-<li><a href="#error-codes">Downstream message error response codes (HTTP and XMPP)</a></li>
-</ol>
-
-</div>
-</div>
-
-<p>This document provides a reference for the syntax used to pass
-messages back and forth in GCM. These messages fall into
-the following broad categories:</p>
-
-<ul>
- <li><a href="#downstream">Downstream messages</a></li>
- <li><a href="#upstream">Upstream messages</a></li>
- <li><a href="#ccs">Cloud Connection Server messages (XMPP)</a></li>
- <li><a href="#error-codes">Downstream message error response codes (HTTP and XMPP)</a></li>
-</ul>
-
-<p>The following sections describe the basic requirements for
-sending messages.</p>
-
-<h2 id="downstream">Downstream Messages</h2>
-<p>This is the message that a 3rd-party app server sends to a client app.
-</p>
-<p>A downstream message includes the following components:</p>
-<ul>
- <li>Target: specifies the recipient of the message.</li>
- <li>Options: specifies attributes of the message.</li>
- <li>Payload: specifies additional content to be included in the message. Optional.</li>
-</ul>
-
-<p>The syntax for each of these components is described in the tables below. </p>
-
-<h3 id="send-downstream">Sending a downstream message</h3>
-
-<p>This section gives the syntax for sending a downstream messages. For JSON,
-these messages can be either HTTP or XMPP. For plain text, these messages can only be HTTP.</p>
-
-<h4>Downstream HTTP or XMPP messages (JSON)</h4>
-
-<p>The following table lists the targets, options, and payload for HTTP or XMPP JSON messages.</p>
-<p class="table-caption" id="table1">
- <strong>Table 1.</strong> Targets, options, and payload for downstream HTTP or XMPP message (JSON).</p>
-<table border="1">
- <tr>
- <th>Parameter</th>
- <th>Protocol</th>
- <th>Usage</th>
- <th>Description</th>
- </tr>
-<tr>
- <td colspan="4"><strong>Targets</strong></td>
- </tr>
- <tr>
- <td><code>to</code></td>
- <td>XMPP</td>
- <td>Required, string</td>
- <td><p>This parameter specifies the recipient of a message. </p>
- <p>The value must be a registration ID or notification key.</p>
- <p>This parameter is used in XMPP in place of {@code registration_ids} or {@code notification_key} in HTTP.</p></td>
- </tr>
- <tr>
- <td><code>registration_ids</code></td>
- <td>HTTP</td>
- <td>Required if {@code notification_key} not present, string array</td>
- <td><p>This parameter specifies the list of devices (registration IDs)
-receiving the message. It must contain at least 1 and at most 1000 registration IDs.</p>
- <p>Multicast messages (sending to more than 1 registration IDs) are allowed using HTTP JSON format only.</p>
- <p>This parameter or {@code notification_key} is used in HTTP in place of {@code to} in XMPP.</p></td>
- </tr>
- <tr>
- <td><code>notification_key</code></td>
- <td>HTTP</td>
- <td>Required if {@code registration_ids} not present, string</td>
- <td><p>This parameter specifies the mapping of a single user to
-multiple registration IDs associated with that user.</p>
- <p>This allows a 3rd-party app server to send a single message to multiple app instances
-(typically on multiple devices) owned by a single user.</p>
- <p>A 3rd-party app server can use {@code notification_key} as the target for a
-message instead of an individual registration ID (or array of registration IDs).
-The maximum number of members allowed for a {@code notification_key} is 20.</p>
- <p>This parameter or {@code registration_ids} is used in HTTP in place of {@code to} in XMPP.</p>
- <p>See <a href="notifications.html">User Notifications</a> for details.</p></td>
- </tr>
-<tr>
- <td colspan="4"><strong>Options</strong></td>
- </tr>
- <tr>
- <td><code>message_id</code></td>
- <td>XMPP</td>
- <td>Required, string</td>
- <td><p>This parameter uniquely identifies a message in an XMPP connection.</p></td>
- </tr>
- <tr>
- <td><code>collapse_key</code></td>
- <td>HTTP, XMPP</td>
- <td>Optional, string</td>
- <td><p>This parameters identifies a group of messages (e.g., with
-{@code collapse_key: "Updates Available"}) that can be collapsed, so that only the
-last message gets sent when delivery can be resumed. This is intended to avoid sending too
-many of the same messages when the device comes back online or becomes active (see {@code delay_while_idle}).</p>
- <p>Note that there is no guarantee of the order in which messages get sent.</p>
- <p>Messages with collapse key are also called
-<a href="{@docRoot}google/gcm/server.html#s2s">send-to-sync messages</a> messages.
-</p>
- <p>Note: A maximum of 4 different collapse keys is allowed at any given time. This means a
-GCM connection server can simultaneously store 4 different send-to-sync messages per client app. If you
-exceed this number, there is no guarantee which 4 collapse keys the GCM connection server will keep. </p></td>
- </tr>
- <tr>
- <td><code>delay_while_idle</code></td>
- <td>HTTP, XMPP</td>
- <td>Optional, JSON boolean</td>
- <td>When this parameter is set to {@code true}, it indicates that the message should not be
-sent until the device becomes active.</p>
- <p>The default value is {@code false}.</p></td>
- </tr>
- <tr>
- <td><code>time_to_live</code></td>
- <td>HTTP, XMPP</td>
- <td>Optional, JSON number</td>
- <td><p>This parameter specifies how long (in seconds) the message should be kept in GCM storage
-if the device is offline. The maximum time to live supported is 4 weeks.</p>
- <p>The default value is 4 weeks. </p></td>
- </tr>
- <tr>
- <td><code>delivery_receipt_
-<br>requested</code></td>
- <td>XMPP</td>
- <td>Optional, JSON boolean</td>
- <td><p>This parameter lets 3rd-party app server request confirmation of message delivery.</p>
- <p>When this parameter is set to {@code true}, CCS sends a delivery receipt
-when the device confirms that it received the message.</p>
- <p>The default value is {@code false}.</p></td>
- </tr>
- <tr>
- <td><code>restricted_package_
-<br>name</code></td>
- <td>HTTP</td>
- <td>Optional, string</td>
- <td>This parameter specifies the package name of the application where the
-registration IDs must match in order to receive the message.</td>
- </tr>
- <tr>
- <td><code>dry_run</code></td>
- <td>HTTP</td>
- <td>Optional, JSON boolean</td>
- <td><p>This parameter, when set to {@code true}, allows developers to test a
-request without actually sending a message.</p>
- <p>The default value is {@code false}.</p></td>
- </tr>
-<tr>
- <td colspan="4"><strong>Payload</strong></td>
- </tr>
- <tr>
- <td><code>data</code></td>
- <td>HTTP, XMPP</td>
- <td>Optional, JSON object</td>
- <td><p>This parameter specifies the key-value pairs of the message's payload. There is
-no limit on the number of key-value pairs, but there is a total message size limit of 4kb.</p>
- <p>For instance, in Android, <code>data:{"score":"3x1"}</code> would result in an intent extra
-named {@code score} with the string value {@code 3x1}.</p>
- <p>The key should not be a reserved word ({@code from} or any word starting with
-{@code google}). It is also not recommended to use words defined in this table
-(such as {@code collapse_key}) because that could yield unpredictable outcomes. </p>
- <p>Values in string types are recommended. You have to convert values in objects
-or other non-string data types (e.g., integers or booleans) to string.</p></td>
- </tr>
-</table>
-
-<h3>Downstream HTTP messages (Plain Text)</h3>
-
-<p>The following table lists the syntax for targets, options, and payload in plain
-text downstream HTTP messages.</p>
-
-<p class="table-caption" id="table2">
- <strong>Table 2.</strong> Targets, options, and payload for downstream plain text HTTP messages.</p>
-
-<table border="1">
- <tr>
- <th>Parameter</th>
- <th>Usage</th>
- <th>Description</th>
- </tr>
-<tr>
- <td colspan="3"><strong>Targets</strong></td>
- </tr>
- <tr>
- <td><code>registration _id</code></td>
- <td>Required, string</td>
- <td><p>This parameter specifies the client apps (registration ID) receiving the message.</p>
- <p>Multicast messaging (sending to more than one registration ID) is allowed using HTTP JSON format only.</p></td>
- </tr>
-<tr>
- <td colspan="3"><strong>Options</strong></td>
- </tr>
- <tr>
- <td><code>collapse_key</code></td>
- <td>Optional, string</td>
- <td>See <a href="#table1">table 1</a> for details.</td>
- </tr>
- <tr>
- <td><code>delay_while_idle</code></td>
- <td>Optional, boolean or number</td>
- <td>See <a href="#table1">table 1</a> for details.</td>
- </tr>
- <tr>
- <td><code>time_to_live</code></td>
- <td>Optional, number</td>
- <td>See <a href="#table1">table 1</a> for details.</td>
- </tr>
- <tr>
- <td><code>restricted_package_name</code></td>
- <td>Optional, string</td>
- <td>See <a href="#table1">table 1</a> for details.</td>
- </tr>
- <tr>
- <td><code>dry_run </code></td>
- <td>Optional, boolean</td>
- <td>See <a href="#table1">table 1</a> for details.</td>
- </tr>
-<tr>
- <td colspan="3"><strong>Payload</strong></td>
- </tr>
- <tr>
- <td><code>data.<key></code></td>
- <td>Optional, string</td>
- <td><p>This parameter specifies the key-value pairs of the message's payload.
-There is no limit on the number of key-value parameters,
-but there is a total message size limit of 4kb.</p>
- <p>For instance, in Android, <code>data:{"score":"3x1"}</code> would result in an intent extra
-named {@code score} with the string value {@code 3x1}.</p>
- <p>The key should not be a reserved word ({@code from} or any word starting with
-{@code google}). It is also not recommended to use words defined in this table
-(such as {@code collapse_key}) because that could yield unpredictable outcomes.</p></td>
- </tr>
-</table>
-
-<h3 id="interpret-downstream">Interpreting a Downstream Message Response</h3>
-
-<p>This section describes the syntax of a response to a downstream message. A client
-app or the GCM Connection Server sends the response to 3rd-party app server upon processing
-the message request. </p>
-
-<h4>Interpreting a downstream HTTP message response </h4>
-<p>The 3rd-party app server should look at both the message response header and the body
-to interpret the message response sent from the GCM Connection Server. The following table
-describes the possible responses.</p>
-<p>
-
-<p class="table-caption" id="table3">
- <strong>Table 3.</strong> Downstream HTTP message response header.</p>
-<table border=1>
- <tr>
- <th>Response</th>
- <th>Description</th>
- </tr>
- <tr>
- <td>200</td>
- <td>Message was processed successfully. The response body will contain more
-details about the message status, but its format will depend whether the request
-was JSON or plain text. See <a href="#table4">table 4</a>
-for more details.</td>
- </tr>
- <tr>
- <td>400</td>
- <td>Only applies for JSON requests.
-Indicates that the request could not be parsed as JSON, or it contained invalid
-fields (for instance, passing a string where a number was expected). The exact
-failure reason is described in the response and the problem should be addressed
-before the request can be retried.</td>
- </tr>
- <tr>
- <td>401</td>
- <td>There was an error authenticating the sender account.
-<a href="server.html#auth_error">Troubleshoot</a></td>
- </tr>
- <tr>
- <td>5xx</td>
- <td>Errors in the 500-599 range (such as 500 or 503) indicate that there was
-an internal error in the GCM server while trying to process the request, or that
-the server is temporarily unavailable (for example, because of timeouts). Sender
-must retry later, honoring any <code>Retry-After</code> header included in the
-response. Application servers must implement exponential back-off.
-<a href="server.html#internal_error">Troubleshoot</a></td>
- </tr>
-</table>
-
-<p>The following table lists the fields in a downstream message response body
-(JSON).</p>
-
-
-<p class="table-caption" id="table4">
- <strong>Table 4.</strong> Downstream HTTP message response body (JSON).</p>
-<table>
- <tr>
- <th>Parameter</th>
- <th>Usage</th>
- <th>Description</th>
- </tr>
- <tr>
- <td><code>multicast_id</code></td>
- <td>Required, number</td>
- <td>Unique ID (number) identifying the multicast message.</td>
- </tr>
- <tr>
- <td><code>success</code></td>
- <td>Required, number</td>
- <td>Number of messages that were processed without an error.</td>
- </tr>
- <tr>
- <td><code>failure</code></td>
- <td>Required, number</td>
- <td>Number of messages that could not be processed.</td>
- </tr>
- <tr>
- <td><code>canonical_ids</code></td>
- <td>Required, number</td>
- <td>Number of results that contain a canonical registration ID. See the
-<a href="{@docRoot}google/gcm/gcm.html#canonical">Overview</a> for more discussion of this topic.</td>
- </tr>
- <tr>
- <td><code>results</code></td>
- <td>Optional, array object</td>
- <td>Array of objects representing the status of the messages processed. The
-objects are listed in the same order as the request (i.e., for each registration
-ID in the request, its result is listed in the same index in the response).<br>
- <ul>
- <li><code>message_id</code>: String specifying a unique ID for each successfully processed
- message.</li>
- <li><code>registration_id</code>: Optional string specifying the canonical registration ID
- for the client app that the message was processed and sent to. Sender should use this
- value as the Registration ID for future requests. Otherwise, the messages might
- be rejected.
- </li>
- <li><code>error</code>: String specifying the error that occurred when processing the
- message for the recipient. The possible values can be found in <a href="#table11">table 11
- </a>.</li>
- </ul></td>
- </tr>
-</table>
-
-
-<p class="table-caption" id="table5">
- <strong>Table 5.</strong> Success response for downstream HTTP message response body (Plain Text).</p>
-<table border="1">
- <tr>
- <th>Parameter</th>
- <th>Usage</th>
- <th>Description</th>
- </tr>
- <tr>
- <td><code>id</code></td>
- <td>Required, string</td>
- <td>This parameter specifies the unique message ID that GCM server processed successfully.</td>
- </tr>
- <tr>
- <td><code>registration_id</code></td>
- <td>Optional, string</td>
- <td>This parameter specifies the canonical registration ID for the client app that the message was
-processed and sent to. Sender should replace the registration ID with this value on future requests,
-otherwise, the messages might be rejected.</td>
- </tr>
-</table>
-
-<p class="table-caption" id="table6">
- <strong>Table 6.</strong> Error response for downstream HTTP message response body (Plain Text).</p>
-<table border="1">
- <tr>
- <th>Parameter</th>
- <th>Usage</th>
- <th>Description</th>
- </tr>
- <tr>
- <td>{@code Error}</td>
- <td>Required, string</td>
- <td>This parameter specifies the error value while processing the message for the recipient.
-See <a href="#table11">table 11</a> for details. </td>
- </tr>
-</table>
-
-<h4>Interpreting a downstream XMPP message response</h4>
-
-<p>The following table lists the fields that appear in a downstream XMPP message response.</p>
-
-<p class="table-caption" id="table7">
- <strong>Table 7.</strong> Downstream message XMPP message response body.</p>
-<table border="1">
- <tr>
- <th>Parameter</th>
- <th>Usage</th>
- <th>Description</th>
- </tr>
- <tr>
- <td><code>from</code></td>
- <td>Required, string</td>
- <td><p>This parameter specifies who sent this response.</p>
- <p>The value is the registration ID of the client app.</p></td>
- </tr>
- <tr>
- <td><code>message_id</code></td>
- <td>Required, string</td>
- <td>This parameter uniquely identifies a message in an XMPP connection.
-The value is a string that uniquely identifies the associated message.</td>
- </tr>
- <tr>
- <td><code>message_type</code></td>
- <td>Required, string</td>
- <td><p>This parameter specifies an 'ack' or 'nack' message from XMPP (CCS)
-to the 3rd-party app server.</p>
- <p>If the value is set to {@code nack}, the 3rd-party app server should look at
-{@code error} and {@code error_description} to get failure information. </p></td>
- </tr>
- <tr>
- <td><code>error</code></td>
- <td>Optional, string</td>
- <td>This parameter specifies an error related to the downstream message. It is set when the
-{@code message_type} is {@code nack}. See <a href="#table11">table 6</a> for details.</td>
- </tr>
- <tr>
- <td><code>error_description</code></td>
- <td>Optional, string</td>
- <td>This parameter provides descriptive information for the error. It is set
-when the {@code message_type} is {@code nack}.</td>
- </tr>
-</table>
-<h2 id="upstream">Upstream Messages (XMPP)</h2>
-
-<p>An upstream message is a message the client app sends to the 3rd-party app server.
-Currently only CCS (XMPP) supports upstream messaging.</p>
-
-<h3 id="interpret-upstream">Interpreting an upstream XMPP message </h3>
-<p>The following table describes the fields that appear in an upstream XMPP message.
-
-<p class="table-caption" id="table8">
- <strong>Table 8.</strong> Upstream XMPP messages.</p>
-<table border="1">
- <tr>
- <th>Parameter</th>
- <th>Usage</th>
- <th>Description</th>
- </tr>
- <tr>
- <td><code>from</code></td>
- <td>Required, string</td>
- <td><p>This parameter specifies who sent the message.</p>
- <p>The value is the registration ID of the client app.</p></td>
- </tr>
- <tr>
- <td><code>category</code></td>
- <td>Required, string</td>
- <td>This parameter specifies the application package name of the client app that sent the message. </td>
- </tr>
- <tr>
- <td><code>message_id</code></td>
- <td>Required, string</td>
- <td>This parameter specifies the unique ID of the message. </td>
- </tr>
- <tr>
- <td><code>data</code></td>
- <td>Optional, string</td>
- <td>This parameter specifies the key-value pairs of the message's payload.</td>
- </tr>
-</table>
-
-<h3 id="upstream-response">Sending an upstream XMPP message response</h3>
-
-<p>The following table describes the response that 3rd-party app server is expected to send to
-<a href="{@docRoot}google/gcm/ccs.html">XMPP (CCS)</a> in response to an
-upstream message it (the app server) received.</p>
-
-<p class="table-caption" id="table9">
- <strong>Table 9.</strong> Upstream XMPP message response.</p>
-<table border="1">
- <tr>
- <th>Parameter</th>
- <th>Usage</th>
- <th>Description</th>
- </tr>
- <tr>
- <td><code>to</code></td>
- <td>Required, string</td>
- <td><p>This parameter specifies the recipient of a response message. </p>
- <p>The value must be a registration ID of the client app that sent the upstream message.</p></td>
- </tr>
- <tr>
- <td><code>message_id</code></td>
- <td>Required, string</td>
- <td>This parameter specifies which message the response is intended for. The value must be
-the {@code message_id} value from the corresponding upstream message.</td>
- </tr>
- <tr>
- <td><code>message_type</code></td>
- <td>Required, string</td>
- <td>This parameter specifies an {@code ack} message from a 3rd-party app server to CCS.</td>
- </tr>
-</table>
-<h2 id="ccs">Cloud Connection Server Messages (XMPP) </h2>
-<p>This is a message sent from XMPP (CCS) to a 3rd-party app server. Here are the primary types
-of messages that XMPP (CCS) sends to the 3rd-party app server:</p>
-<ul>
- <li><strong>Delivery Receipt:</strong> If the 3rd-party app server included {@code delivery_receipt_requested}
-in the downstream message, XMPP (CCS) sends a delivery receipt when it receives confirmation
-that the device received the message.</li>
- <li><strong>Control:</strong> These CCS-generated messages indicate that
-action is required from the 3rd-party app server.</li>
-</ul>
-
-<p>The following table describes the fields included in the messages CCS
-sends to a 3rd-party app server.</p>
-
-<p class="table-caption" id="table10">
- <strong>Table 10.</strong> GCM Cloud Connection Server messages (XMPP).</p>
-<table border="1">
- <tr>
- <th>Parameter</th>
- <th>Usage</th>
- <th>Description</th>
- </tr>
- <tr>
- <td colspan="3"><strong>Common Field</strong></td>
- </tr>
- <tr>
- <td><code>message_type</code></td>
- <td>Required, string</td>
- <td><p>This parameter specifies the type of the CCS message: either delivery receipt or control.</p>
- <p>When it is set to {@code receipt}, the message includes {@code from}, {@code message_id},
- {@code category} and {@code data} fields to provide additional information.</p>
- <p>When it is set to {@code control}, the message includes {@code control_type} to indicate the
-type of control message.</p></td>
- </tr>
- <tr>
- <td colspan="3"><strong>Delivery receipt-specific</strong></td>
- </tr>
- <tr>
- <td><code>from</code></td>
- <td>Required, string</td>
- <td>This parameter is set to {@code gcm.googleapis.com}, indicating that the
-message is sent from CCS.</td>
- </tr>
- <tr>
- <td><code>message_id</code></td>
- <td>Required, string</td>
- <td>This parameter specifies the original message ID that the receipt is intended for,
-prefixed with {@code dr2:} to indicate that the message is a delivery receipt. A 3rd-party app
-server must send an {@code ack} message with this message ID to acknowledge that it
-received this delivery receipt. See <a href="#table9">table 9</a> for the 'ack' message format.</td>
- </tr>
- <tr>
- <td><code>category</code></td>
- <td>Optional, string</td>
- <td>This parameter specifies the application package name of the client app that
-receives the message that this delivery receipt is reporting. This is available when
-{@code message_type} is {@code receipt}.</td>
- </tr>
- <tr>
- <td><code>data</code></td>
- <td>Optional, string</td>
- <td>This parameter specifies the key-value pairs for the delivery receipt message. This is available
-when the {@code message_type} is {@code receipt}.
- <ul>
- <li>{@code message_status}: This parameter specifies the status of the receipt message.
-It is set to {@code MESSAGE_SENT_TO_DEVICE} to indicate the device has confirmed its receipt of
-the original message.</li>
- <li>{@code original_message_id}: This parameter specifies the ID of the original message
-that the 3rd-party app server sent to the client app.</li>
- <li>{@code device_registration_id}: This parameter specifies the registration ID of the
-client app to which the original message was sent.</li>
- </ul>
-</td>
- </tr>
- <tr>
- <td colspan="3"><strong>Control-specific</strong></td>
- </tr>
- <tr>
- <td><code>control_type</code></td>
- <td>Optional, string</td>
- <td><p>This parameter specifies the type of control message sent from CCS.</p>
- <p>Currently, only {@code CONNECTION_DRAINING} is supported. XMPP (CCS) sends this control message
-before it closes a connection to perform load balancing. As the connection drains, no more messages
-are allowed to be sent to the connection, but existing messages in the pipeline will
-continue to be processed.</p></td>
- </tr>
-</table>
-
-<h2 id="error-codes">Downstream message error response codes (HTTP and XMPP)</h2>
-
-<p>The following table lists the error response codes for downstream messages (HTTP and XMPP).</p>
-
-<p class="table-caption" id="table11">
- <strong>Table 11.</strong> Downstream message error response codes.</p>
-<table border="1">
- <tr>
- <th>Error</th>
- <th>HTTP Code</th>
- <th>XMPP Code</th>
- <th>Recommended Action</th>
- </tr>
- <tr>
- <td>Missing Registration ID</td>
- <td>200 + error:MissingRegistration</td>
- <td><code>INVALID_JSON</code></td>
- <td>Check that the request contains a registration ID (either in the
-{@code registration_id} in a plain text message, or in the {@code registration_ids} in JSON).</td>
- </tr>
- <tr>
- <td>Invalid Registration ID</td>
- <td>200 + error:InvalidRegistration</td>
- <td><code>BAD_REGISTRATION</code></td>
- <td>Check the format of the registration ID you pass to the server. Make sure it
-matches the registration ID the client app receives from registering with GCM. Do not
-truncate or add additional characters.</td>
- </tr>
- <tr>
- <td>Unregistered Device</td>
- <td>200 + error:NotRegistered</td>
- <td><code>DEVICE_UNREGISTERED</code></td>
- <td>An existing registration ID may cease to be valid in a number of scenarios, including:<br>
- <ul>
- <li>If the client app unregisters with GCM.</li>
- <li>If the client app is automatically unregistered, which can happen if the user uninstalls the application.</li>
- <li>If the registration ID expires (for example, Google might decide to refresh registration IDs).</li>
- <li>If the client app is updated but the new version is not configured to receive messages.</li>
-</ul>
- For all these cases, remove this registration ID from the 3rd-party app
-server and stop using it to send messages.</td>
- </tr>
- <tr>
- <td>Invalid Package Name</td>
- <td>200 + error:InvalidPackageName</td>
- <td></td>
- <td>Make sure the message was addressed to a registration ID whose package name
-matches the value passed in the request.</td>
- </tr>
- <tr>
- <td>Authentication Error</td>
- <td>401</td>
- <td> </td>
- <td>The sender account used to send a message couldn't be authenticated. Possible causes are:<br>
-<ul>
- <li>Authorization header missing or with invalid syntax in HTTP request.</li>
- <li>Invalid project number sent as key.</li>
- <li>Key valid but with GCM service disabled.</li>
- <li>Request originated from a server not whitelisted in the Server Key IPs.</li>
-</ul>
- Check that the token you're sending inside the Authentication header is
-the correct API key associated with your project. See
-<a href="{@docRoot}google/gcm/http.html#checkAPIkey">Checking the validity of an API Key
-</a> for details.</td>
- </tr>
- <tr>
- <td>Mismatched Sender</td>
- <td>200 + error:MismatchSenderId</td>
- <td><code>BAD_REGISTRATION</code></td>
- <td>A registration ID is tied to a certain group of senders. When a client app registers
-for GCM, it must specify which senders are allowed to send messages. You should use one
-of those sender IDs when sending messages to the client app. If you switch to a different
-sender, the existing registration IDs won't work.</td>
- </tr>
- <tr>
- <td>Invalid JSON</td>
- <td>400</td>
- <td><code>INVALID_JSON</code></td>
- <td>Check that the JSON message is properly formatted and contains valid fields
-(for instance, making sure the right data type is passed in).</td>
- </tr>
- <tr>
- <td>Message Too Big</td>
- <td>200 + error:MessageTooBig</td>
- <td><code>INVALID_JSON</code></td>
- <td>Check that the total size of the payload data included in a message does
-not exceed 4096 bytes. This includes both the the keys and the values.</td>
- </tr>
- <tr>
- <td>Invalid Data Key</td>
- <td>200 + error:
-<br />
-InvalidDataKey</td>
- <td><code>INVALID_JSON</code></td>
- <td>Check that the payload data does not contain a key (such as {@code from} or any value
-prefixed by {@code google}) that is used internally by GCM. Note that some words (such as {@code collapse_key})
-are also used by GCM but are allowed in the payload, in which case the payload value
-will be overridden by the GCM value.</td>
- </tr>
- <tr>
- <td>Invalid Time to Live</td>
- <td>200 + error:InvalidTtl</td>
- <td><code>INVALID_JSON</code></td>
- <td>Check that the value used in {@code time_to_live} is an integer representing a
-duration in seconds between 0 and 2,419,200 (4 weeks).</td>
- </tr>
- <tr>
- <td>Bad ACK message</td>
- <td>N/A</td>
- <td><code>BAD_ACK</code></td>
- <td>Check that the 'ack' message is properly formatted before retrying. See
-<a href="#table9">table 9</a> for details.</td>
- </tr>
- <tr>
- <td>Timeout</td>
- <td>5xx or 200 + error:Unavailable</td>
- <td><code>SERVICE_UNAVAILABLE</code></td>
- <td><p>The server couldn't process the request in time. Retry the same request, but you must:<br>
-<ul>
- <li>For HTTP: Honor the {@code Retry-After} header if it is included in the response from the
-GCM Connection Server.</li>
- <li>Implement exponential back-off in your retry mechanism. (e.g. if you waited one second
-before the first retry, wait at least two second before the next one, then 4 seconds and so on).
-If you're sending multiple messages, delay each one independently by an additional random amount
-to avoid issuing a new request for all messages at the same time.</li>
- <li>For CCS: The initial retry delay should be set to 1 second.</li>
-</ul>
- <p>Senders that cause problems risk being blacklisted.</p></td>
- </tr>
- <tr>
- <td>Internal Server Error</td>
- <td>500 or 200 + error:InternalServerError</td>
- <td><code>INTERNAL_SERVER_
-<br />
-ERROR</code></td>
- <td>The server encountered an error while trying to process the request. You could retry
-the same request following the requirements listed in "Timeout" (see row above). If the error persists, please
-report the problem in the {@code android-gcm group}.</td>
- </tr>
- <tr>
- <td>Device Message Rate Exceeded</td>
- <td>200 + error:
-<br />DeviceMessageRate
-<br />
-Exceeded</td>
- <td><code>DEVICE_MESSAGE_RATE<br />
-_EXCEEDED</code></td>
- <td>The rate of messages to a particular device is too high. Reduce the
-number of messages sent to this device and do not immediately retry sending to this device.</td>
- </tr>
- <tr>
- <td>Connection Draining</td>
- <td>N/A</td>
- <td><code>CONNECTION_DRAINING</code></td>
- <td>The message couldn't be processed because the connection is draining. This happens because
-periodically, XMPP (CCS) needs to close down a connection to perform load balancing. Retry the message over
-another XMPP connection.</td>
- </tr>
-</table>
diff --git a/docs/html/google/gcm/server.jd b/docs/html/google/gcm/server.jd
deleted file mode 100644
index 004fd0e..0000000
--- a/docs/html/google/gcm/server.jd
+++ /dev/null
@@ -1,440 +0,0 @@
-page.title=Implementing GCM Server
-@jd:body
-
-<div id="qv-wrapper">
-<div id="qv">
-
-<h2>In this document</h2>
-
-<ol class="toc">
- <li><a href="#role">Role of the 3rd-party Application Server</a></li>
- <li><a href="#choose">Choosing a GCM Connection Server</a></li>
- <li><a href="#send-msg">Sending Messages</a>
- <ol class="toc">
-
- <li><a href="#target">Target</a></li>
- <li><a href="#payload">Payload</a></li>
- <li><a href="#params">Message parameters</a>
- </ol>
- </li>
- <li><a href="#adv">Messaging Concepts and Best Practices</a>
-
- <ol class="toc">
-
- <li><a href="#collapsible">Send-to-Sync vs. Messages with Payload</a></li>
- <li><a href="#ttl">Setting an Expiration Date for a Message</a></li>
- <li><a href="#multi-senders">Receiving Messages from Multiple Senders</a>
- <li><a href="#lifetime">Lifetime of a Message</a>
- <li><a href="#throttling">Throttling</a>
- </ol>
-
-</li>
- </li>
-
-</ol>
-
-<h2>See Also</h2>
-
-<ol class="toc">
-<li><a href="server-ref.html">Server Reference</a></li>
-<li><a href="gs.html">Getting Started</a></li>
-<li><a href="client.html">Implementing GCM Client</a></li>
-<li><a href="ccs.html">Cloud Connection Server (XMPP)</a></li>
-<li><a href="http.html">HTTP Connection Server</a></li>
-
-
-</ol>
-
-</div>
-</div>
-
-
-<p>The server side of Google Cloud Messaging (GCM) consists of two components:</p>
-<ul>
-<li>Google-provided <strong>GCM Connection Servers</strong>
-take messages from a <a href="{@docRoot}google/gcm/server.html#role">3rd-party app server</a>
-and send them to a GCM-enabled
-application (the "client app") running on a device. For example,
-Google provides connection servers for <a href="{@docRoot}google/gcm/http.html">
-HTTP</a> and <a href="{@docRoot}google/gcm/ccs.html">XMPP (CCS)</a> (XMPP).</li>
-<li>A <strong>3rd-party application server</strong> that you must implement. This application
-server sends data to a GCM-enabled client app via the chosen GCM connection server.</li>
-</ul>
-</p>
-
-<p>A full GCM implementation requires both a client implementation and a server
-implementation. For more
-information about implementing the client side, see <a href="client.html">
-Implementing GCM Client</a>.</p>
-
-
-<h2 id="role">Role of the 3rd-party Application Server</h2>
-
-<p>Before you can write client apps that use the GCM feature, you must
-have an application server that meets the following criteria:</p>
-
-<ul>
- <li>Able to communicate with your client.</li>
- <li>Able to fire off properly formatted requests to the GCM server.</li>
- <li>Able to handle requests and resend them as needed, using
-<a href="http://en.wikipedia.org/wiki/Exponential_backoff">exponential back-off.</a></li>
- <li>Able to store the API key and client registration IDs. In HTTP, the API key is
-included in the header of POST requests that send messages. In XMPP, the API key is
-used in the SASL PLAIN authentication request as a password to authenticate the connection.</li>
- <li>Able to generate message IDs to uniquely identify each message it sends. Message IDs
-should be unique per sender ID.</li>
-</ul>
-
-<p>Here are the basic steps you follow to implement your 3rd-party app server:</p>
-
-<ul>
- <li>Decide which GCM connection server(s) you want to use. Note that if you want to use
- upstream messaging from your client applications, you must use XMPP (CCS). For a more detailed
- discussion of this, see <a href="#choose">
- Choosing a GCM Connection Server</a>.</li>
- <li>Decide how you want to implement your app server. We provide helper libraries and code
-samples to assist you with your 3rd-party app server implementation. For example:
- <ul>
- <li>If you decide to use the HTTP connection server, you can use the
-GCM server helper library and demo app to help in implementing your app server.</li>
- <li>If you decide to use the XMPP connection server, you can use
-the provided Python or Java <a href="http://www.igniterealtime.org/projects/smack/">
-Smack</a> demo apps as a starting point.</li>
- <li>Note that Google AppEngine does not support connections to XMPP (CCS).</li>
- </ul>
- </li>
- </ul>
- </li>
-</ul>
-
-
-<h2 id="choose">Choosing a GCM Connection Server</h2>
-
-<p>Currently GCM provides two connection servers: <a href="{@docRoot}google/gcm/http.html">
-HTTP</a> and <a href="{@docRoot}google/gcm/ccs.html">XMPP (CCS)</a>. You can use them
-separately or in tandem. XMPP (CCS) messaging differs from HTTP messaging in the following ways:</p>
-<ul>
- <li>Upstream/Downstream messages
- <ul>
- <li>HTTP: Downstream only, cloud-to-device up to 4KB of data. </li>
- <li>XMPP (CCS): Upstream and downstream (device-to-cloud, cloud-to-device),
- up to 4 KB of data. </li>
- </ul>
- </li>
- <li>Messaging (synchronous or asynchronous)
- <ul>
- <li>HTTP: Synchronous. 3rd-party app servers send messages as HTTP POST requests and
-wait for a response. This mechanism is synchronous and blocks the sender from sending
-another message until the response is received.</li>
- <li>XMPP (CCS): Asynchronous. 3rd-party app servers send/receive messages to/from all their
-devices at full line speed over persistent XMPP connections.
-XMPP (CCS) sends acknowledgment or failure notifications (in the
-form of special ACK and NACK JSON-encoded XMPP messages) asynchronously.</li>
- </ul>
- </li>
-
- <li>JSON
- <ul>
- <li>HTTP: JSON messages sent as HTTP POST.</li>
- <li>XMPP (CCS): JSON messages encapsulated in XMPP messages.</li>
- </ul>
- </li>
- <li>Plain Text
- <ul>
- <li>HTTP: Plain Text messages sent as HTTP POST.</li>
- <li>XMPP (CCS): Not supported.</li>
- </ul>
- </li>
- <li>Multicast downstream send to multiple registration IDs.
- <ul>
- <li>HTTP: Supported in JSON message format.</li>
- <li>XMPP (CCS): Not supported.</li>
- </ul>
- </li>
-</ul>
-
-
-<h2 id="send-msg">Sending Messages</h2>
-
-<p>This section gives an overview of sending messages. For details of message syntax,
-see <a href="{@docRoot}google/gcm/server-ref.html">Server Reference</a>.</p>
-
-<h3>Overview</h3>
-
-<p>Here is the general sequence of events that occurs when a 3rd-party application
-server sends a message (the details vary depending on the platform):</p>
-<ol>
- <li>The 3rd-party app server sends a message to GCM servers.</li>
- <li>The GCM connection server enqueues and stores the message if the device is offline.</li>
- <li>When the device is online, GCM connection server sends the message to the device.</li>
- <li>The client app processes the message. </li>
-</ol>
-
-<h3>Implement send request</h3>
-
-<p>The following sections describe the basic components involved in
-sending a request. See the <a href="{@docRoot}google/gcm/server-ref.html">Server Reference</a>
-for details.</p>
-
-<h4 id="target">Target</h4>
-<p>Required. When your app server sends a message in GCM, it must specify a target.</p>
-<p>For HTTP you must specify the target as one of the following:</p>
-<ul>
-<li><code>registration_ids</code>: For sending to 1 or more devices (up to 1000).
-When you send a message to multiple registration IDs, that is called a multicast message.</li>
-<li><code>notification_key</code>: For sending to multiple devices owned by a single user.</li>
-</ul>
-<p>For CCS (XMPP) you must specify the target as:</p>
-<ul>
-<li>{@code to}: This
-field may contain a single registration ID or a notification key.
-XMPP (CCS) does not support multicast messaging.</li>
-</ul>
-
-<h4 id="options">Options</h4>
-
-<p>There are various options the 3rd-party app server can set when sending a downstream
-message to a client app. See the <a href="{@docRoot}google/gcm/server-ref.html#table1">
-Server Reference</a> for details. Here are a few examples of possible options:</p>
-
-<ul>
- <li>{@code collapse_key}: whether a message should be "send-to-sync" or a "message with
-payload".</li>
- <li>{@code time_to_live}: setting an expiration date for a message.</li>
- <li>{@code dry_run}: Test your server.
-<p>If you want to test your request (either JSON or plain text) without delivering
-the message to the devices, you can set an optional HTTP parameter called
-<code>dry_run</code> with the value <code>true</code>. The result will be almost
-identical to running the request without this parameter, except that the message
-will not be delivered to the devices. Consequently, the response will contain fake
-IDs for the message and multicast parameters.</p>
-</li>
-</ul>
-
-<h4 id="payload">Payload</h4>
-<p>Optional. If you are including a payload in the message, you use the <code>data</code>
-parameter to include the payload. This applies for both HTTP and XMPP.</p>
-
-<p>See the <a href="{@docRoot}google/gcm/server-ref.html">Server Reference</a> for details on sending
-and receiving messages.</p>
-
-<h2 id="adv">Messaging Concepts and Best Practices</h2>
-
-<p>This section has a discussion of general messaging topics.</p>
-
-<h3 id="collapsible">Send-to-Sync vs. Messages with Payload</h3>
-
-<p>Every message sent in GCM has the following characteristics:</p>
-<ul>
- <li>It has a payload limit of 4096 bytes.</li>
- <li>By default, it is stored by GCM for 4 weeks.</li>
-</ul>
-
-<p>But despite these similarities, messages can behave very differently depending
-on their particular settings. One major distinction between messages is whether
-they are collapsed (where each new message replaces the preceding message) or not
-collapsed (where each individual message is delivered). Every message sent in GCM
-is either a "send-to-sync" (collapsible) message or a "message with
-payload" (non-collapsible message).</p>
-
-<h4 id="s2s">Send-to-sync messages</h4>
-
-<p>A send-to-sync (collapsible) message is often a "tickle" that tells
-a mobile application to sync data from the server. For example, suppose you have
-an email application. When a user receives new email on the server, the server
-pings the mobile application with a "New mail" message. This tells the
-application to sync to the server to pick up the new email. The server might send
-this message multiple times as new mail continues to accumulate, before the application
-has had a chance to sync. But if the user has received 25 new emails, there's no
-need to preserve every "New mail" message. One is sufficient. Another
-example would be a sports application that updates users with the latest score.
-Only the most recent message is relevant. </p>
-
-<p>GCM allows a maximum of 4 different collapse keys to be used by the GCM server
-at any given time. In other words, the GCM server can simultaneously store 4
-different send-to-sync messages per device, each with a different collapse key.
-For example, Device A can have A1, A2, A3, and A4. Device B can have B1, B2, B3,
-and B4, and so on. If you exceed this number GCM will only keep 4 collapse keys, with no
-guarantees about which ones they will be.</p>
-
-<h3 id="payload">Messages with payload</h3>
-
-<p>Unlike a send-to-sync message, every "message with payload"
-(non-collapsible message) is delivered. The payload the message contains can be
-up to 4kb. For example, here is a JSON-formatted message in an IM application in
-which spectators are discussing a sporting event:</p>
-
-<pre class="prettyprint pretty-json">{
- "registration_id" : "APA91bHun4MxP5egoKMwt2KZFBaFUH-1RYqx...",
- "data" : {
- "Nick" : "Mario",
- "Text" : "great match!",
- "Room" : "PortugalVSDenmark",
- },
-}</pre>
-
-<p>A "message with payload" is not simply a "ping" to the
-mobile application to contact the server to fetch data. In the aforementioned IM
-application, for example, you would want to deliver every message, because every
-message has different content. To specify a non-collapsible message, you simply
-omit the <code>collapse_key</code> parameter. Thus GCM will send each message
-individually. Note that the order of delivery is not guaranteed.</p>
-
-<p>GCM will store up to 100 non-collapsible messages. After that, all messages
-are discarded from GCM, and a new message is created that tells the client how
-far behind it is.</p>
-
-<p>The application should respond by syncing with the server to recover the
-discarded messages. </p>
-
-<h4 id="which">Which should I use?</h4>
- <p>If your application does not need to use non-collapsible messages, collapsible
-messages are a better choice from a performance standpoint. However, if you use
-collapsible messages, remember that <strong>GCM only allows a maximum of 4 different collapse
-keys to be used by the GCM server per registration ID at any given time</strong>. You must
-not exceed this number, or it could cause unpredictable consequences.</p>
-
-<h3 id="ttl">Setting an Expiration Date for a Message</h3>
-<p>You can use the <code>time_to_live</code> parameter in the send request
-to specify the maximum lifespan of a message.
-The value of this parameter must be a duration from 0 to 2,419,200 seconds, and
-it corresponds to the maximum period of time for which GCM will store and try to
-deliver the message. Requests that don't contain this field default to the maximum
-period of 4 weeks.</p>
-<p>Here are some possible uses for this feature:</p>
-<ul>
- <li>Video chat incoming calls</li>
- <li>Expiring invitation events</li>
- <li>Calendar events</li>
-</ul>
-<h4 id="bg">Background </h4>
-<p>GCM usually delivers messages immediately after they are sent. However,
-this might not always be possible. For example, if the platform is Android,
-the device could be turned off, offline, or otherwise unavailable.
-Or the sender itself might request
-that messages not be delivered until the device becomes active by using the
-<code>delay_while_idle</code> flag. Finally, GCM might intentionally delay messages
-to prevent an application from consuming excessive resources and negatively
-impacting battery life.</p>
-
-<p>When this happens, GCM will store the message and deliver it as soon as it's
-feasible. While this is fine in most cases, there are some applications for which
-a late message might as well never be delivered. For example, if the message is
-an incoming call or video chat notification, it will only be meaningful for a
-small period of time before the call is terminated. Or if the message is an
-invitation to an event, it will be useless if received after the event has ended.</p>
-
-<p>Another advantage of specifying the expiration date for a message is that GCM
-will never throttle messages with a <code>time_to_live</code> value of 0 seconds.
-In other words, GCM will guarantee best effort for messages that must be delivered
-"now or never." Keep in mind that a <code>time_to_live</code> value of
-0 means messages that can't be delivered immediately will be discarded. However,
-because such messages are never stored, this provides the best latency for
-sending notifications.</p>
-
-<p>Here is an example of a JSON-formatted request that includes TTL:</p>
-<pre class="prettyprint pretty-json">
-{
- "collapse_key" : "demo",
- "delay_while_idle" : true,
- "registration_ids" : ["xyz"],
- "data" : {
- "key1" : "value1",
- "key2" : "value2",
- },
- "time_to_live" : 3
-},
-</pre>
-
-
-<h3 id="multi-senders">Receiving Messages from Multiple Senders</h3>
-
-<p>GCM allows multiple parties to send messages to the same application. For
-example, suppose your application is an articles aggregator with multiple
-contributors, and you want each of them to be able to send a message when they
-publish a new article. This message might contain a URL so that the application
-can download the article. Instead of having to centralize all sending activity in
-one location, GCM gives you the ability to let each of these contributors send
-its own messages.</p>
-
-<p>To make this possible, all you need to do is have each sender generate its own
-project number. Then include those IDs in the sender field, separated by commas,
-when requesting a registration. Finally, share the registration ID with your
-partners, and they'll be able to send messages to your application using their
-own authentication keys.</p>
-
-<p>Note that there is limit of 100 multiple senders.</p>
-
-<h3 id="lifetime">Lifetime of a Message</h3>
-
-<p>When a 3rd-party server posts a message to GCM and receives a message ID back,
-it does not mean that the message was already delivered to the device. Rather, it
-means that it was accepted for delivery. What happens to the message after it is
-accepted depends on many factors.</p>
-
-<p>In the best-case scenario, if the device is connected to GCM, the screen is on,
-and there are no throttling restrictions (see <a href="#throttling">Throttling</a>),
-the message will be delivered right away.</p>
-
-<p>If the device is connected but idle, the message will still be
-delivered right away unless the <code>delay_while_idle</code> flag is set to true.
-Otherwise, it will be stored in the GCM servers until the device is awake. And
-that's where the <code>collapse_key</code> flag plays a role: if there is already
-a message with the same collapse key (and registration ID) stored and waiting for
-delivery, the old message will be discarded and the new message will take its place
-(that is, the old message will be collapsed by the new one). However, if the collapse
-key is not set, both the new and old messages are stored for future delivery.
-Collapsible messages are also called <a href="#s2s">send-to-sync messages</a>.</p>
-
-<p class="note"><strong>Note:</strong> There is a limit on how many messages can
-be stored without collapsing. That limit is currently 100. If the limit is reached,
-all stored messages are discarded. Then when the device is back online, it receives
-a special message indicating that the limit was reached. The application can then
-handle the situation properly, typically by requesting a full sync.
-<br><br>
-Likewise, there is a limit on how many <code>collapse_key</code>s you can have for
-a particular device. GCM allows a maximum of 4 different collapse keys to be used
-by the GCM server per device
-any given time. In other words, the GCM server can simultaneously store 4 different
-send-to-sync messages, each with a different collapse key. If you exceed this number
-GCM will only keep 4 collapse keys, with no guarantees about which ones they will be.
-See <a href="#s2s">Send-to-sync messages</a> for more information.
-</p>
-
-<p>If the device is not connected to GCM, the message will be stored until a
-connection is established (again respecting the collapse key rules). When a connection
-is established, GCM will deliver all pending messages to the device, regardless of
-the <code>delay_while_idle</code> flag. If the device never gets connected again
-(for instance, if it was factory reset), the message will eventually time out and
-be discarded from GCM storage. The default timeout is 4 weeks, unless the
-<code>time_to_live</code> flag is set.</p>
-
-<p>Finally, when GCM attempts to deliver a message to the device and the
-application was uninstalled, GCM will discard that message right away and
-invalidate the registration ID. Future attempts to send a message to that device
-will get a <code>NotRegistered</code> error. See <a href="#unreg">
-How Unregistration Works</a> for more information.</p>
-<p>Although is not possible to track the status of each individual message, the
-Google Cloud Console stats are broken down by messages sent to device, messages
-collapsed, and messages waiting for delivery.</p>
-
-<h3 id="throttling">Throttling</h3>
-<p>To prevent abuse (such as sending a flood of messages to a device) and
-to optimize for the overall network efficiency and battery life of
-devices, GCM implements throttling of messages using a token bucket
-scheme. Messages are throttled on a per application and per <a href="#collapsible">collapse
-key</a> basis (including non-collapsible messages). Each application
-collapse key is granted some initial tokens, and new tokens are granted
-periodically therefter. Each token is valid for a single message sent to
-the device. If an application collapse key exhausts its supply of
-available tokens, new messages are buffered in a pending queue until
-new tokens become available at the time of the periodic grant. Thus
-throttling in between periodic grant intervals may add to the latency
-of message delivery for an application collapse key that sends a large
-number of messages within a short period of time. Messages in the pending
-queue of an application collapse key may be delivered before the time
-of the next periodic grant, if they are piggybacked with messages
-belonging to a non-throttled category by GCM for network and battery
-efficiency reasons.</p>
-
-
diff --git a/docs/html/google/gcs/gcs-signup.jd b/docs/html/google/gcs/gcs-signup.jd
deleted file mode 100644
index 7334cec..0000000
--- a/docs/html/google/gcs/gcs-signup.jd
+++ /dev/null
@@ -1,10 +0,0 @@
-page.title=Sign Up for Google Cloud Save
-
-@jd:body
-
-<p>Sign up to be a trial partner for Google Cloud Save.</p>
-
-
-<iframe src="https://docs.google.com/a/google.com/forms/d/1_V67YIXzLDLb-UzxOOpSjUDuJFfeYg3hEUT0oliK2ck/viewform?embedded=true" width="100%" height="930" frameborder="0" marginheight="0" marginwidth="0" id="signupform">Loading...</iframe>
-</body>
-</html>
diff --git a/docs/html/google/gcs/index.jd b/docs/html/google/gcs/index.jd
deleted file mode 100644
index e5f4776..0000000
--- a/docs/html/google/gcs/index.jd
+++ /dev/null
@@ -1,30 +0,0 @@
-page.title=Google Cloud Save
-page.tags="gcs"
-header.hide=1
-@jd:body
-
-
-<div class="landing-banner">
-
-<div class="col-5" style="min-height:100px">
- <img src="{@docRoot}images/google/gcs.png" />
-</div>
-<div class="col-7">
-
- <h1 itemprop="name" style="margin-bottom:0;">Google Cloud Save</h1>
- <p itemprop="description">
- Google Cloud Save is a service that enables per-user data storage
-and sync in your apps with no backend programming required. Google Cloud Save
-stores its data
-in <a href="http://developers.google.com/datastore/">Google Cloud Datastore</a>,
- a fully managed, schemaless database for storing non-relational data. Cloud
-Datastore automatically scales with your users.
-Google Cloud Save works even when your device is offline, and it
-provides an easy transition to server-side coding because
-the same database is accessible via App Engine and Compute Engine.
-Finally, Google Cloud Save provides a generous initial per-user free quota that
-expands as your user base grows.
-</p>
-<a href="{@docRoot}google/gcs/gcs-signup.html" class="button">Sign Up</a>
-</div>
-</div>
diff --git a/docs/html/google/index.jd b/docs/html/google/index.jd
index 1ebb9f8..9df09e1 100644
--- a/docs/html/google/index.jd
+++ b/docs/html/google/index.jd
@@ -1,7 +1,7 @@
fullpage=true
page.title=Google Services
section.landing=true
-meta.tags="beautifulapps, design, ux, patterns, holo, appquality, landing"
+meta.tags="google, play, services, maps, location, gcm, messaging, places"
header.hide=1
footer.hide=1
@jd:body
@@ -16,25 +16,21 @@
<div class="col-1of2 col-pull-1of2">
<h1 class="dac-hero-title">Build better apps with Google</h1>
<p class="dac-hero-description">
- Add powerful capabilities to your apps, quickly, at low cost. Simplify development,
-grow your user base, and monetize more effectively with Google Play services.
- </p>
- <a class="dac-hero-cta" href="https://www.google.com/design/spec/material-design/introduction.html">
+ Take advantage of the latest Google technologies through a single set of APIs, delivered
+ across Android devices worldwide as part of Google Play services. </p>
+ <p class="dac-hero-description">Start by setting up the Google Play services library,
+ then build with the APIs you need. </p>
+
+ <a class="dac-hero-cta" href="https://developers.google.com/android/guides/">
<span class="dac-sprite dac-auto-chevron"></span>
- Get Started with Google Play services
+ Set up Google Play services
</a><br>
- <a class="dac-hero-cta" href="https://www.google.com/design/spec/resources/color-palettes.html">
+ <a class="dac-hero-cta" href="https://developers.google.com/android/reference/">
<span class="dac-sprite dac-auto-chevron"></span>
API Reference
</a><br>
</div>
</div>
- <div class="dac-section dac-small">
- <!--<div class="resource-widget resource-flow-layout col-16"
- data-query="collection:google/landing/google"
- data-cardSizes="6x2"
- data-maxResults="6"></div>-->
- </div>
</div>
</section>
<div class="wrap dac-offset-parent">
@@ -42,42 +38,52 @@
<i class="dac-sprite dac-arrow-down-gray"></i>
</a>
</div>
+
<section class="dac-section dac-gray dac-small dac-invert" id="latest"><div class="wrap">
<h2 class="norule">Latest</h2>
<div class="resource-widget resource-flow-layout col-16"
data-query="type:blog+tag:googleservices+tag:featured+tag:develop"
data-cardSizes="6x6"
data-maxResults="3"></div>
-</div></section>
-
-
+ </div>
+</section>
<section class="dac-section dac-light"><div class="wrap">
<h1 class="dac-section-title">Google APIs and services</h1>
<div class="dac-section-subtitle">
- Design around Android's capabilities and conventions to give users the best experience.
+ Add the latest Google-powered features to enrich your app,
+ grow your user base, and monetize.
</div>
<div class="resource-widget resource-flow-layout col-16"
data-query="collection:google/landing/services"
data-cardSizes="6x6"
data-maxResults="6"></div>
<ul class="dac-section-links">
- <li class="dac-section-link"><a href="https://developers.google.com/">
+ <li class="dac-section-link"><a href="https://developers.google.com/android/">
<span class="dac-sprite dac-auto-chevron"></span>
More Google services for Android
</a></li>
</ul>
-</div></section>
+ </div>
+</section>
+<section class="dac-section dac-gray dac-small dac-invert" id="latest"><div class="wrap">
+ <h2 class="norule">Latest</h2>
+ <div class="resource-widget resource-flow-layout col-16"
+ data-query="collection:google/landing/videos"
+ data-cardSizes="6x6"
+ data-maxResults="3"></div>
+ </div>
+</section>
<section class="dac-section dac-invert dac-darken-bg" style="background-image: url(/images/distribute/google-play-bg.jpg)"><div class="wrap">
<h1 class="dac-section-title">Google Play developer tools</h1>
<div class="dac-section-subtitle">
- Scale your operations. Essential downloads, stencils, and tools to help you create your design.
+ Scale your publishing, manage your catalog, and build revenue using Google Play developer tools.
</div>
<div class="resource-widget resource-flow-layout col-16"
data-query="collection:google/landing/googleplay"
- data-cardSizes="6x6"
+ data-cardSizes="6x3"
data-maxResults="6"></div>
<ul class="dac-section-links">
diff --git a/docs/html/google/play-services/ads.jd b/docs/html/google/play-services/ads.jd
deleted file mode 100644
index 2f915f3..0000000
--- a/docs/html/google/play-services/ads.jd
+++ /dev/null
@@ -1,110 +0,0 @@
-page.title=Google Mobile Ads
-page.tags=Ads,monetization,AdMob,Google Play services
-header.hide=1
-
-@jd:body
-
-
-<div class="landing-banner">
-
-<div class="col-6">
- <img src="{@docRoot}images/google/gps-ads.png" alt="">
-</div>
-<div class="col-6">
-
-<h1 itemprop="name" style="margin-bottom:0;">Google Mobile Ads</h1>
-<p itemprop="description">
- Monetize your app with banner or interstitial ads from
- Google's vast pool of advertisers. Integrate with top ad networks through
- mediation to maximize your revenue from impressions. Take advantage of new
- ad features and capabilities through Google Play services, without having to
- add or update a library in your APK.
-</p>
-
-<p>
- Check out the <a href=
- "{@docRoot}reference/gms-packages.html">Google Mobile Ads API reference</a> and visit <a class=
- "external-link" href=
- "http://developers.google.com/mobile-ads-sdk">developers.google.com/mobile-ads-sdk</a>
- for more information about integrating Google Mobile Ads into your app.
-</p>
-</div>
-</div>
-
-<div style=
-"background-color:#fffdeb;width:100%;margin-bottom:1em;padding:.5em;" class=
-"caution">
- <p><strong>Upgrade to the new AdMob</strong></p>
- <p>Legacy AdMob will be shutting down on August 31, 2014. Beginning August 31,
- you will no longer be able to use legacy AdMob to promote and monetize your
- apps. Specifically, please be aware of the following:</p>
- <ol style="margin-bottom:0;">
- <li>Ads will stop serving to legacy ad units that are not updated with new
- ad unit IDs.</li>
- <li>Legacy house ad campaigns will stop serving.</li>
- <li>You will not be able to access the legacy AdMob UI after August 31,
- 2014.</li>
- </ol>
- <p>If you are still using legacy AdMob, please upgrade your account to the new
- AdMob as soon as possible. Upgrading takes only 5 minutes. <a href=
- "https://apps.admob.com/admob/signup?">Start here</a>.</p>
-</div>
-
-<div class="landing-docs">
- <div class="col-6 normal-links">
- <h3 style="clear:left">Key Developer Features</h3>
-
- <h4>Seamless auto-updates</h4>
- <p>The Google Mobile Ads SDK is part of Google Play services, so you can take advantage of features
- and capabilities in each new release of Google Play services, without needing to update your
- APK. </p>
-
- <h4>Monetize your apps</h4>
- <p>Connect with over a million Google advertisers and show relevant ads in your app. Users
- engage with the ads, you make money. The Google Mobile Ads SDK fully supports AdMob,
- DoubleClick for Publishers, DoubleClick Ad Exchange, and Search Ads for Mobile Apps.
- <br>
- <a href="http://www.google.com/ads/admob/monetize.html" class="external-link">Learn more
- and sign up</a>.</p>
-
- <h4>Location-based ads</h4>
- <p>You can use the publisher-provided location API to provide Google with the location when
- requesting ads. Location-based ads can improve your app monetization.</p>
-
- <h4>Innovative ads</h4>
- <p>Choose from a range of ads across mobile devices and tablets, including interactive
- ad units.</p>
-
- <h4>Flexible and powerful tools </h4>
- <p>Filters and controls help you manage your ads. If you want to use multiple ad
- networks, you can do that too, with free ad network mediation.</p>
-
- <h4>Ad Policy Compliant</h4>
- <p>The Google Mobile Ads SDK available in Google Play services is compliant with
- Google Play Ad Poilcy regarding the usage of
- <a href="{@docRoot}google/play-services/id.html">advertising ID</a>.</p>
- </div>
-
- <div class="col-6 normal-links">
- <h3 style="clear:left">Getting Started</h3>
- <h4>1. Get the Google Play services SDK</h4>
- <p>The Google Mobile Ads APIs are part of the Google Play services platform.</p>
- <p>To get started, <a href="{@docRoot}google/play-services/setup.html">set up
- the Google Play services SDK</a>.</p>
-
- <h4>2. Run the sample</h4>
- <p>Once you've installed the Google Play services package, the Google Mobile Ads sample is located
- in <code><android-sdk>/extras/google-play-services/samples/ads</code> and shows you how to
- serve banner and interstitial ads using the Google Mobile Ads APIs.</p>
-
- <h4>3. Read the documentation</h4>
- <p>Your use of the Google Mobile Ads SDK is governed by the terms between you and Google that
- govern your use of the Google product (AdSense/AdMob, AdX or DFP) with which you use the SDK.</p>
- <p>For quick access while developing your Android apps, the <a
- href="{@docRoot}reference/gms-packages.html">Google Mobile Ads API reference</a> is available here on
- developer.android.com.</p>
- <p>Detailed documentation for Google Mobile Ads for Android is available with the rest of the Google
- Mobile Ads developer documents at <a href="https://developers.google.com/mobile-ads-sdk"
- class="external-link">developers.google.com/mobile-ads-sdk</a>. </p>
- </div>
-</div>
diff --git a/docs/html/google/play-services/cast.jd b/docs/html/google/play-services/cast.jd
deleted file mode 100644
index 656f634..0000000
--- a/docs/html/google/play-services/cast.jd
+++ /dev/null
@@ -1,92 +0,0 @@
-page.title=Google Cast Android API
-page.tags="chromecast","miracast"
-header.hide=1
-
-@jd:body
-
-
-<div class="landing-banner">
-
-<div class="col-6">
- <img src="{@docRoot}images/google/googlecast.png" alt="">
-</div>
-<div class="col-6">
-
- <h1 itemprop="name" style="margin-bottom:0;">Google Cast Android API</h1>
- <p itemprop="description">Google Cast is a technology that enables multi-screen experiences, which
- allows users to send content from mobile devices and personal computers to their TVs.
- You can use the Google Cast Android API
- to add casting functionality to your Android app so users can view your app content on their
- big screens.</p>
-
- <p>Explore the <a href="{@docRoot}reference/com/google/android/gms/cast/package-summary.html"
- >Google Cast Android API reference</a> or visit <a
- href="http://developers.google.com/cast/" class="external-link">developers.google.com/cast</a>
- for more information about how to design and build your app to support Google Cast.</p>
- <p></p>
-</div>
-</div>
-
-<div class="landing-docs">
- <div class="col-6 normal-links">
- <h3 style="clear:left">Key Developer Features</h3>
-
- <h4>Add your own style to a pre-built receiver app</h4>
- <p>Adding support for Google Cast requires that you provide a companion app on the
- Google Cast device, known as the receiver app. If your app streams audio or video content
- using one of several supported media types, then you can create a receiver app with your
- own style and branding by creating nothing more than a CSS file.
- <br/><a href="http://developers.google.com/cast/docs/styled_receiver" class="external-link"
- >Create a Styled Media Receiver</a></p>
-
- <h4>Build an entirely custom receiver app</h4>
- <p>If the Styled Media Receiver does not provide the UI components required for your
- receiver app or does not support the media types your app requires, you can build your
- own receiver app as a web page using the JavaScript Receiver API.
- <br/><a href="http://developers.google.com/cast/docs/styled_receiver" class="external-link"
- >Create a Custom Receiver</a></p>
-
- <h4>Add playback controls to your app</h4>
- <p>To allow users to control playback on the receiver app, you can add a
- controller interface to your app on Android, iOS, and Chrome. The client app on these devices
- is known as the sender app and can perform a variety of actions using the Google Cast APIs
- such as start and pause playback and control the volume on the Google Cast device.
- <br/><a href="http://developers.google.com/cast/docs/sender_apps" class="external-link"
- >Create a Sender App</a></p>
- </div>
-
-
- <div class="col-6 normal-links">
- <h3 style="clear:left">Getting Started</h3>
- <h4>1. Get the Google Play services SDK</h4>
-
- <p>The Google Cast Android APIs are part of the Google Play services platform.</p>
- <p>To get started, <a href="{@docRoot}google/play-services/setup.html">set up
- the Google Play services SDK</a> on your development machine and be sure your test device has
- Google Play services 4.2 (or higher) installed.</p>
-
- <h4>2. Register your app</h4>
- <p>Before you can develop and test apps on your own Google Cast device,
- you need to acquire an app ID for your API calls and register your Google Cast device
- (such as a Chromecast) as a development device. For details, read the
- <a href="http://developers.google.com/cast/docs/registration"
- class="external-link">Registration</a> guide.</p>
-
- <h4>3. Design the Android app UI</h4>
- <p>To provide the perfect user experience when casting your content to TVs, be sure your
- app follows all the recommendations in the <a class="external-link"
- href="https://developers.google.com/cast/docs/design_checklist"
- >Design Checklist</a>.</p>
-
- <h4>4. Start developing</h4>
- <p>Read the <a class="external-link"
- href="https://developers.google.com/cast/docs/developers">Developer's Guide</a>
- to learn how to implement the receiver app and how to send it commands from your Android app.
- </p>
-
- <p>For quick access while developing your Android app, the
- <a href="{@docRoot}reference/com/google/android/gms/cast/package-summary.html">Google Cast
- Android API reference</a> is available here on developer.android.com.</p>
-
- </div>
-</div>
\ No newline at end of file
diff --git a/docs/html/google/play-services/drive.jd b/docs/html/google/play-services/drive.jd
deleted file mode 100644
index e9662e6..0000000
--- a/docs/html/google/play-services/drive.jd
+++ /dev/null
@@ -1,141 +0,0 @@
-page.title=Google Drive Android API
-page.tags="storage","files","cloud"
-header.hide=1
-
-@jd:body
-
- <div class="landing-banner">
- <div class="col-6">
- <img src="{@docRoot}images/google/gps-drive.png" alt="">
- </div>
-
- <div class="col-6">
- <h1 itemprop="name" style="margin-bottom:0;">
- Google Drive Android API
- </h1>
-
- <p itemprop="description">
- Give your users access to their files, wherever they are, on any device.
- The Google Drive Android API makes it easier than ever to use Drive to
- store and sync user’s files between their mobile devices and the cloud.
- Simply use Drive like local file storage and the API will handle the
- syncing for you.
- </p>
-
- <p>
- Explore the <a href=
- "{@docRoot}reference/com/google/android/gms/drive/package-summary.html">Google
- Drive Android API reference</a> or visit <a href=
- "http://developers.google.com/drive/android">developers.google.com/drive/android</a>
- for more information about integrating Google Drive into your app.
- </p>
- </div>
- </div>
-
- <div class="landing-docs">
- <div class="col-6 normal-links">
- <h3 style="clear:left">
- Key Developer Features
- </h3>
-
- <h4>
- Automatic syncing
- </h4>
-
- <p>
- The Google Drive Android API uses the local device storage to create,
- edit, and open files, then syncs them automatically when the device
- is connected to the Internet. If a user requests a file that isn’t
- already cached on the device, the Drive service downloads the file
- the next time the device connects.<br>
- <a href="http://developers.google.com/drive/android/files" class=
- "external-link">Open a file</a>
- </p>
-
- <h4>
- User interface components
- </h4>
-
- <p>
- Building your own user interface for selecting or creating files is a
- thing of the past. The Drive API includes activity builders for two
- of the most common user activities: opening files and creating
- files.<br>
- <a href="http://developers.google.com/drive/android/create-file"
- class="external-link">Create a file</a>
- </p>
-
- <h4>
- Easily search for files
- </h4>
-
- <p>
- The query builder allows you to easily construct a query by joining
- together multiple parameters and filtering based on metadata. This
- allows you to easily display only relevant files, such as files
- shared with the user or files with a specific MIME type.<br>
- <a class="external-link" href=
- "http://developers.google.com/drive/android/queries">Query for a
- file</a>
- </p>
- </div>
-
- <div class="col-6 normal-links">
- <h3 style="clear:left">
- Getting Started
- </h3>
-
- <h4>
- 1. Get the Google Play services SDK
- </h4>
-
- <p>
- The Google Drive Android API is part of the Google Play services
- platform.
- </p>
-
- <p>
- To get started, <a href=
- "{@docRoot}google/play-services/setup.html">set up the Google Play
- services SDK</a>.
- </p>
-
- <h4>
- 2. Run the sample
- </h4>
-
- <p>
- Once you’ve set up the Google Play services SDK, you can run the
- Google Drive sample located in
- <code><android-sdk>/extras/google-play-services/samples/drive</code>.
- However, before you can run the sample, you need to register your app
- in the Google Developers Console.
- </p>
-
- <p>
- See <a class="external-link" href=
- "http://developers.google.com/drive/android/get-started">Getting
- Started guide</a> to learn how to get up and running with the Google
- Drive Android API.
- </p>
-
- <h4>
- 3. Read the documentation
- </h4>
-
- <p>
- For quick access while developing your Android apps, the <a href=
- "{@docRoot}reference/com/google/android/gms/drive/package-summary.html">
- Google Drive API reference</a> is available here on
- developer.android.com.
- </p>
-
- <p>
- Detailed documentation for the Google Drive Android API is available
- with the rest of the Google Drive Platform documentation at <a class=
- "external-link" href=
- "https://developers.google.com/drive/android">developers.google.com/drive</a>.
- </p>
- </div>
- </div>
-
\ No newline at end of file
diff --git a/docs/html/google/play-services/games.jd b/docs/html/google/play-services/games.jd
deleted file mode 100644
index f68751d..0000000
--- a/docs/html/google/play-services/games.jd
+++ /dev/null
@@ -1,94 +0,0 @@
-page.title=Google Play Game Services
-page.tags="games"
-header.hide=1
-
-@jd:body
-
-<div class="landing-banner">
-
-<div class="col-6">
- <img src="{@docRoot}images/google/gps-play_games_logo.png" alt="">
-</div>
-<div class="col-6">
-
-<h1 itemprop="name" style="margin-bottom:0;">Google Play Game Services</h1>
- <p itemprop="description">
-Make your games social with Google Play game services. Add achievements,
-leaderboards, real-time multiplayer, and other popular features using the
-Google Play game services SDK. Let players sign in using their Google+
-identities and share their gaming experience with friends.
- </p>
-<p>Explore the
-<a href="{@docRoot}reference/com/google/android/gms/games/package-summary.html">Google
-Play Games Android API reference</a> or visit
-<a class="external-link" href=
-"https://developers.google.com/games/services/">developers.google.com/games/services</a>
-for more information about integrating game services into your app.
-</p>
-</div>
-</div>
-
-<div class="landing-docs">
- <div class="col-6 normal-links">
- <h3 style="clear:left">Key Developer Features</h3>
-
- <h4>Reward players with achievements</h4>
- <p>Add hidden and incremental achievements to encourage users to explore your game in new
- and interesting ways. A built-in achievement UI is available to display progress. <br />
- <a class="external-link" href="https://developers.google.com/games/services/android/achievements">Add
- achievements to your game</a>.</p>
- </a>
-
- <h4>Drive engagement with leaderboards</h4>
- <p>Let players compare scores with friends using leaderboards and see how they rank
- against other players worldwide. Google Play game services automatically maintains daily,
- weekly, and all-time high scores. <br /><a class="external-link"
- href="https://developers.google.com/games/services/android/leaderboards">Build leaderboards</a></p>
-
- <h4>Save game data to the cloud</h4>
- <p>Offer seamless game progress across all of the user's devices. Use the Saved Games API
- to quickly store and synchronize game data on Google's cloud
- infrastructure. <br />
- <a
- class="external-link" href="https://developers.google.com/games/services/android/savedgames">Save
- game data to the cloud</a></p>
-
- <h4>Create real-time and turn-based multiplayer games</h4>
- <p>Make your game more dynamic with simultaneous or asynchronous gameplay.
- Use the multiplayer APIs to send match invitations, auto-match players
- anonymously, and exchange data between game clients. <br />Develop <a class="external-link"
- href="https://developers.google.com/games/services/android/multiplayer">real-time
- multiplayer</a> and <a class="external-link"
- href="https://developers.google.com/games/services/android/turnbasedMultiplayer">turn-based multiplayer</a> games</p>
- </div>
-
-
- <div class="col-6 normal-links">
- <h3 style="clear:left">Getting Started</h3>
- <h4>1. Get the Google Play services SDK</h4>
- <p>Google Play game services is part of the Google Play services platform.</p>
- <p>To use game services, <a href="{@docRoot}google/play-services/setup.html">set up
- the Google Play services SDK</a>. Then, see the <a class="external-link"
- href="https://developers.google.com/games/services/android/quickstart">
- Getting Started guide</a> to set up your app.
- </p>
-
- <h4>2. Run the sample</h4>
-
- <p>Once you've installed the Google Play services package, <a class="external-link"
- href="https://developers.google.com/games/services/downloads/">download the game services
- samples</a> to learn how to use the major components of the Google Play game services SDK.
- </p>
-
- <h4>3. Read the documentation</h4>
-
- <p>Read the <a class="external-link" href="https://developers.google.com/games/services/terms">
- API Terms of Service</a>.</p>
- <p>Detailed documentation for the Google Play game services SDK is available at <a class="external-link"
- href="https://developers.google.com/games/services/">developers.google.com/games/services</a>.
- </p>
- <p>For quick access while developing your Android apps, the
- <a href="{@docRoot}reference/com/google/android/gms/games/package-summary.html">API reference</a> is available here on developer.android.com.</p>
- </div>
-
-</div>
diff --git a/docs/html/google/play-services/id.jd b/docs/html/google/play-services/id.jd
deleted file mode 100644
index 43d1809..0000000
--- a/docs/html/google/play-services/id.jd
+++ /dev/null
@@ -1,211 +0,0 @@
-page.title=Advertising ID
-page.tags=Ads,Advertising ID,ID
-header.hide=1
-
-@jd:body
-
-<div class="landing-banner">
-
-<div class="col-8">
-
- <h1 itemprop="name" style="margin-bottom:0;">Advertising ID</h1>
- <p itemprop="description">The advertising ID is a user-specific, unique,
- resettable ID for advertising, provided by Google Play services. It gives
- users better controls and provides developers with a simple, standard system
- to continue to monetize your apps. It is an anonymous identifier for advertising
- purposes and enables users to reset their identifier or opt out of interest-based
- ads within Google Play apps. </p>
-
-<p>The advertising ID is accessible through a straightforward API that you can implement in your apps. For details,
-take a look at the
-<a href="#get_started">overview</a> and the <a
-href="{@docRoot}reference/com/google/android/gms/ads/identifier/package-summary.html">advertising ID API reference</a>.
-
-</div>
-</div>
-
-<div class="landing-docs">
- <div class="col-6 normal-links">
- <h3 style="clear:left">Key Developer Features</h3>
-
- <h4>Standard, simple ID</h4>
- <p>The advertising ID is a part of a standard, simple system for serving ads and performing analytics.</p>
-
- <h4>Giving users control</h4>
- <p>Users can reset their advertising ID or opt out of interest-based ads at any time, right from the Google Settings app.
- Their preferences apply across all ad companies that use the advertising ID.</p>
-
- </div>
-
- <div class="col-6 normal-links">
- <h3 style="clear:left">Getting Started</h3>
- <h4>1. Get the Google Play services SDK</h4>
- <p>The advertising ID APIs are part of the Google Play services platform.</p>
- <p>To get started, <a href="{@docRoot}google/play-services/setup.html">set up
- the Google Play services SDK</a>. </p>
-
- <h4>2. Read the docs and example code</h4>
- <p>Once you've installed the Google Play services package, review the <a href="#get_started">overview</a>
- below, as well as the <a href="#example">example</a>.</p>
- <p>
- For detailed documentation, take a look at the <a href=
- "{@docRoot}reference/com/google/android/gms/ads/identifier/package-summary.html">
- advertising ID API reference documentation</a>.
- </p>
- </div>
-</div>
-
-<p class="caution" style=
-"background-color:#fffdeb;width:100%;margin-bottom:1em;padding:.5em;">
- As a reminder, please note that starting <strong>1 August 2014</strong>, new
- apps and app updates distributed through Google Play must use the advertising
- ID in lieu of any other persistent identifiers for any advertising purposes,
- on devices that support the advertising ID.<br>
- <br>
- To learn how to check your app's compliance through the Developer Console, or
- for details on the associated developer policy changes, please see the
- <a href=
- "https://support.google.com/googleplay/android-developer/answer/6048248">Advertising
- ID topic</a> in the Google Play developer help center.
-</p>
-
-<h2 id="get_started">Using the Advertising ID</h2>
-
-<p>
- The <strong>advertising ID</strong> is a unique but
- user-resettable string identifier that lets ad networks and other apps anonymously
- identify a user. The user's advertising ID is made available to apps through APIs
- provided in Google Play services.
-</p>
-<p>
- Users can reset their advertising ID at any time, right from the Ads section of the
- Google Settings app on their devices. From the same app, users can also
- opt-out of targeted advertising based on the advertising ID by setting the appropriate
- <strong style="white-space:nowrap">ad tracking preference</strong>. When the
- user opts-out of targeted ads, this ad tracking preference is made available
- to apps through a Google Play services API.
-</p>
-<p>
- Apps making use of the advertising ID <strong>must check for and respect</strong> the
- user's ad tracking preference. Also please note that any use of the advertising ID
- must abide by the terms of the <a class="external-link"
- href="http://play.google.com/about/developer-content-policy.html#ADID">Google Play
- Developer Content Policies</a>.
-</p>
-
-<h3 id="format">Format of the Advertising ID</h3>
-
-<p>
- Google Play services APIs expose the user's advertising ID as a string format of UUID,
- with values similar to this:
-</p>
-<p style="margin-left:1.5em;"><code>“38400000-8cf0-11bd-b23e-10b96e40000d”</code></p>
-
-<h3 id="requirements">Requirements</h3>
-
-<ul>
- <li>The advertising ID APIs are supported in Google Play services 4.0+</li>
- <li>Support for the advertising ID on specific devices is based on their installed versions
- of Google Play services</li>
-</ul>
-
-<h3 id="obtaining">Obtaining the user's advertising ID and ad tracking preference</h3>
-
-<p>
- If you want to use the advertising ID in your app, you must first install the Google
- Play services SDK. As noted in the requirements above, you should install the
- SDK for Google Play services 4.0 or higher if you will develop using the advertising ID
- APIs. For information about how to get started, see <a href=
- "{@docRoot}google/play-services/setup.html">Set Up Google Play services</a>.
-</p>
-<p>
- The advertising ID APIs are available in the <a href=
- "{@docRoot}reference/com/google/android/gms/ads/identifier/package-summary.html">
- <code>com.google.android.gms.ads.identifier</code></a> package in the Google
- Play Services library. To obtain the user's advertising ID and tracking preference,
- call the method
- <a href=
- "{@docRoot}reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html#getAdvertisingIdInfo(android.content.Context)">
- <code>getAdvertisingIdInfo()</code></a>, which returns an <a href=
- "{@docRoot}reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.Info.html">
- <code>AdvertisingIdClient.Info</code></a> encapsulating the user's current Advertising ID
- and tracking preference.
-</p>
-
-<p class="note">
- <strong>Note:</strong> The <code>getAdvertisingIdInfo()</code> method is a
- blocking call, so you must not call it on the main (UI) thread. If called on
- the main thread, the method throws <code>IllegalStateException</code>.
-</p>
-
-<p>
- Once you've retrieved the <code>AdvertisingIdClient.Info</code> object, you
- can use it's <a href=
- "{@docRoot}reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.Info.html#getId()">
- <code>getId()</code></a> and <a href=
- "{@docRoot}reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.Info.html#isLimitAdTrackingEnabled()">
- <code>isLimitAdTrackingEnabled()</code></a> methods to access the advertising ID and
- ad tracking preference.
-</p>
-
-<table>
-<tr>
-<th>Method</th>
-<th>Description</th>
-</tr>
-<tr>
-<td><code>public String getId()</code></td>
-<td style="white-space:nowrap;">Retrieves the advertising ID.</td>
-</tr>
-<tr>
-<td style="white-space:nowrap;"><code>public boolean isLimitAdTrackingEnabled()</code></td>
-<td>Retrieves whether the user has limit ad tracking enabled or not.</td>
-</tr>
-</table>
-
-<p>
- The advertising ID APIs do not include a "reset" method. Only users can initiate a
- reset of their own advertising IDs, through the Google Settings application.
-</p>
-<p>
- For more information about the advertising ID APIs, see the <a href=
- "{@docRoot}reference/com/google/android/gms/ads/identifier/package-summary.html">
- reference documentation</a>.
-</p>
-
-<h3 id="example">Example implementation</h3>
-
-<p>
- Here's a basic illustration of how you can retrieve the user's advertising ID and ad
- tracking preference in your app:
-</p>
-
-<pre>
-import com.google.android.gms.ads.identifier.AdvertisingIdClient;
-import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
-import com.google.android.gms.common.GooglePlayServicesAvailabilityException;
-import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
-import java.io.IOException;
-...
-
-// Do not call this function from the main thread. Otherwise,
-// an IllegalStateException will be thrown.
-public void getIdThread() {
-
- Info adInfo = null;
- try {
- adInfo = AdvertisingIdClient.getAdvertisingIdInfo(mContext);
-
- } catch (IOException e) {
- // Unrecoverable error connecting to Google Play services (e.g.,
- // the old version of the service doesn't support getting AdvertisingId).
-
- } catch (GooglePlayServicesAvailabilityException e) {
- // Encountered a recoverable error connecting to Google Play services.
-
- } catch (GooglePlayServicesNotAvailableException e) {
- // Google Play services is not available entirely.
- }
- final String id = adInfo.getId();
- final boolean isLAT = adInfo.isLimitAdTrackingEnabled();
-}</pre>
diff --git a/docs/html/google/play-services/location.jd b/docs/html/google/play-services/location.jd
deleted file mode 100644
index 98b0544..0000000
--- a/docs/html/google/play-services/location.jd
+++ /dev/null
@@ -1,125 +0,0 @@
-page.title=Location APIs
-page.tags=location,geofence,geofencing,gps
-header.hide=1
-@jd:body
-
-
- <div class="landing-banner">
-
-<div class="col-6">
- <img src="{@docRoot}images/google/gps-location.png" alt="Location APIs Logo">
-</div>
-
-<div class="col-6">
-
-<h1 itemprop="name" style="margin-bottom:0;">Location & Places</h1>
-<p itemprop="description">
- The location APIs make it easy for you to build location-aware applications, without needing to
- focus on the details of the underlying location technology. The related Places APIs also help
- your app identify real-world points of interest near the user.
-</p>
-<p>
- To get started, first <a href="{@docRoot}google/play-services/setup.html">set up</a>
- the Google Play services SDK. To learn how to identify the user's location, see
- <a href="{@docRoot}training/location/index.html">Making Your App Location Aware</a>, or to
- identify nearby places, see the
-<a href="https://developers.google.com/places/android">Google Places API for Android</a>
-developer guide.
-</p>
-</div>
-</div>
-<div class="landing-docs">
- <h3 style="clear:left">Key Developer Features</h3>
- <div class="cols normal-links">
- <div class="col-6">
-
-
-
-<h4 style="font-weight:bold">Fused location provider</h4>
-
-<p>The Fused Location Provider intelligently manages the underlying location technology and gives you the best location according to your needs. </p>
-
-<ul>
- <li>
- <em>Simple APIs</em>: Lets you specify high-level needs like "high accuracy" or "low power", instead of
- having to worry about location providers.
- </li>
- <li>
- <em>Immediately available</em>: Gives your apps immediate access to the best, most recent location.
- </li>
- <li>
- <em>Power-efficiency</em>: Minimizes your app's use of power. Based on all incoming location requests and available sensors, fused location provider chooses the most efficient way to meet those needs.
- </li>
- <li>
- <em>Versatility</em>: Meets a wide range of needs, from foreground uses that need highly accurate
- location to background uses that need periodic location updates with negligible power impact.
- </li>
-
-</ul>
-
-<h4 style="font-weight:bold">Geofencing APIs</h4>
-
-<p>Lets your app setup geographic boundaries around specific locations and then receive notifications when the user enters or leaves those areas. </p>
-
-<ul>
- <li>
- <em>Simple but powerful APIs</em>: Allows batch addition and removal of geofences. Ability to manage
- multiple geofences at the same time. Ability to filter alerts for both entry and exit or
- entry only or exit only.
- </li>
- <li>
- <em>Optimized for battery</em>: Adjusts location updates based on user’s proximity to the geofence
- and user’s modality (still, walking, driving, and so on).
- </li>
-</ul>
-</div>
-
-<div class="col-6">
-
-<h4 style="font-weight:bold">Places API</h4>
-
-<p>The <a href="https://developers.google.com/places/android/">Google
-Places API for Android</a> helps you build location-aware apps
-that respond contextually to the local businesses and other places near the
-device:</p>
-
-<ul>
-<li>Use the built-in
- <a href="https://developers.google.com/places/android/placepicker">place
- picker</a> UI widget, allowing users to select a place on an interactive
- map.</li>
-<li>Identify the user's
- <a href="https://developers.google.com/places/android/current-place">current
- place</a>, such as a local business, point of interest, or other geographic location.</li>
-<li>Retrieve and display rich
- <a href="https://developers.google.com/places/android/place-details">information
- about a place</a>.</li>
-<li>Make it easy to enter place names and addresses, by
- <a href="https://developers.google.com/places/android/autocomplete">autocompleting</a>
- your users' queries as they type.</li>
-<li>Differentiate your app by supplying up-to-date local information, and
- <a href="https://developers.google.com/places/android/add-place">adding
- places</a> to Google's Places database.</li>
-</ul>
-
-
-<h4 style="font-weight:bold">Activity recognition</h4>
-
-<p>The Activity recognition API makes it easy to check the user’s current activity—still, walking, cycling, and in-vehicle—with very efficient use of the battery:</p>
-<ul>
- <li>
- <em>Optimized for battery</em>: Uses low-power sensors to recognize the user's current physical activity.
- </li>
- <li>
- <em>Enhances other services with context</em>: Great for adding movement awareness to location awareness. Apps can adjust the amount of
- location awareness they provide, based on the current user movement. For example, a
- navigation app can request more frequent updates when the user is driving.
- </li>
- <li>
- <em>Advanced activity detection</em>: For apps that want to do their own
- post-processing, the activity APIs provide confidence values for each of the activities.
- It also includes two activities that indicate unreliable measurements: unknown and tilt.
- </li>
-</ul>
-</div>
-</div>
diff --git a/docs/html/google/play-services/maps.jd b/docs/html/google/play-services/maps.jd
deleted file mode 100644
index 9bf5f80..0000000
--- a/docs/html/google/play-services/maps.jd
+++ /dev/null
@@ -1,97 +0,0 @@
-page.title=Google Maps Android API v2
-page.tags=mapview,location
-header.hide=1
-
-@jd:body
-
-
-<div class="landing-banner">
-
-<div class="col-6">
- <img src="{@docRoot}images/google/gps-maps.png" alt="">
-</div>
-<div class="col-6">
-
- <h1 itemprop="name" style="margin-bottom:0;">Google Maps Android API v2</h1>
- <p itemprop="description">Allow your users to explore the world with rich maps provided by
- Google. Identify locations with <b>custom markers</b>, augment the map data
- with <b>image overlays</b>, embed <b>one or more maps</b> as fragments,
- and much more.</p>
- <p>Explore the <a
-href="{@docRoot}reference/com/google/android/gms/maps/package-summary.html"
->Google Maps Android API v2 reference</a> or visit <a class="external-link"
-href="https://developers.google.com/maps/documentation/android/">developers.google.com/maps</a>
-for more information about adding maps to your app.</p>
-</div>
-</div>
-
-
-
-<div class="landing-docs">
- <div class="col-6 normal-links">
- <h3 style="clear:left">Key Developer Features</h3>
- <h4>Add maps to your app</h4>
- <p>With Google Maps Android API v2, you can embed maps into an activity
- as a fragment with a simple XML snippet. The new Maps offer exciting features such as 3D maps;
- indoor, satellite, terrain, and hybrid maps;
- vector-based tiles for efficient caching and drawing; animated transitions; and much more.
- <a class="external-link" href="https://developers.google.com/maps/documentation/android/map">Add
- a map object</a>.</p>
-
- <h4>Customize the map</h4>
- <p>Add markers onto the map to indicate special points of interest for your users.
- You can define custom colors or icons for your map markers to
- match your app's look and feel. To further enhance the app, draw polylines
- and polygons to indicate paths or regions, or provide complete image overlays.
- <a class="external-link" href="https://developers.google.com/maps/documentation/android/marker">Draw
- markers</a>.</p>
- </a>
-
- <h4>Control the user's view</h4>
- <p>Give your users a different view of the world with the ability to control the rotation, tilt,
- zoom, and pan properties of the "camera" perspective of the map.
- <a class="external-link" href="https://developers.google.com/maps/documentation/android/views">Change
- the view</a>.</p>
-
- <h4>Add Street View to your app</h4>
- <p>Embed Street View into an activity and let your users explore the world
- through panoramic 360-degree views. Programmatically control the zoom and
- orientation (tilt and bearing) of the Street View camera, and animate the
- camera movements over a given duration.
- <a class="external-link" href="https://developers.google.com/maps/documentation/android/streetview">Add Street View</a>.</p>
- </div>
-
-
- <div class="col-6 normal-links">
- <h3 style="clear:left">Getting Started</h3>
- <h4>1. Get the Google Play services SDK</h4>
- <p>Google Maps Android API v2 is part of the Google Play services platform.</p>
- <p>To use Google Maps, <a href="{@docRoot}google/play-services/setup.html">set up
- the Google Play services SDK</a>. Then see the <a class="external-link"
- href="https://developers.google.com/maps/documentation/android/start#installing_the_google_maps_android_v2_api">
- Getting Started guide</a> to get your API key for Maps and set up your app.
- </p>
-
- <h4>2. Run the sample</h4>
-
- <p>Once you've installed the Google Play services package, the Google Maps sample is located in
- <code><android-sdk>/extras/google-play-services/samples/maps</code> and shows you
- how to use the major components of Google Maps Android API v2.
- </p>
-
- <h4>3. Read the documentation</h4>
-
- <p>Read the <a href="https://developers.google.com/maps/terms">Google Maps
- API Terms of Service</a>.</p>
-
- <p>For quick access while developing your Android apps, the
- <a href="{@docRoot}reference/com/google/android/gms/maps/package-summary.html">Google Maps
- Android API v2 reference</a> is available here on developer.android.com.</p>
-
- <p>Detailed documentation for Google Maps Android API v2 is available with the rest of the
- Google Maps developer documents at <a class="external-link"
- href="https://developers.google.com/maps/documentation/android/">developers.google.com/maps</a>.
- </p>
- </div>
-
-</div>
diff --git a/docs/html/google/play-services/plus.jd b/docs/html/google/play-services/plus.jd
deleted file mode 100644
index 84224e7..0000000
--- a/docs/html/google/play-services/plus.jd
+++ /dev/null
@@ -1,90 +0,0 @@
-page.title=Google+ Platform for Android
-page.tags=authentication,signin,social
-header.hide=1
-
-@jd:body
-
-
-<div class="landing-banner">
-
-<div class="col-6">
- <img src="{@docRoot}images/google/gps-googleplus.png" alt="">
-</div>
-<div class="col-6">
-
- <h1 itemprop="name" style="margin-bottom:0;">Google+ Platform for Android</h1>
- <p itemprop="description">The Google+ platform for Android lets you authenticate a user
- with the same credentials they use on Google every day. Once a user signs in with Google,
- you can create more engaging experiences and drive usage of your app.</p>
-
- <p>Explore the <a href="{@docRoot}reference/com/google/android/gms/plus/package-summary.html"
->Google+ Android API reference</a> or visit <a class="external-link"
-href="https://developers.google.com/+/mobile/android/">developers.google.com/+</a> for more
-information about integrating Google+ into your app.</p>
-</div>
-</div>
-
-<div class="landing-docs">
- <div class="col-6 normal-links">
- <h3 style="clear:left">Key Developer Features</h3>
-
- <h4>Trusted authentication</h4>
- <p>Google+ Sign-In is a simple, trusted, and secure way to let people sign in
- to your app with their Google credentials and bring along their Google+ info.
- <br />
- <a href="https://developers.google.com/+/mobile/android/sign-in"
- class="external-link">Add sign-in</a>.</p>
-
- <h4>Access the profile and social graph</h4>
- <p>Once users have signed in with Google, your app can welcome them by name,
- display their picture, connect them with friends, and lots more.
- <br />
- <a href="https://developers.google.com/+/mobile/android/people"
- class="external-link">Access the social graph</a>.</p>
-
- <h4>Stand out in the stream</h4>
- <p>Interactive posts is a rich way of sharing to Google+. It lets users prompt friends
- to take specific actions in your app from a Google+ post, like "listen," "RSVP," "check-in,"
- and over 100 more actions. <br />
- <a class="external-link"
- href="https://developers.google.com/+/mobile/android/share">Post interactive content</a>.</p>
-
- <h4>Recommend content</h4>
- <p>Add a native +1 button so users can recommend content from your app. These endorsements
- can give your app more credibility and help it grow faster. <br />
- <a class="external-link"
- href="https://developers.google.com/+/mobile/android/recommend">Add the +1 button</a>.</p>
- </div>
-
-
- <div class="col-6 normal-links">
- <h3 style="clear:left">Getting Started</h3>
- <h4>1. Get the Google Play services SDK</h4>
- <p>The Google+ Android APIs are part of the Google Play services platform.</p>
- <p>To get started, <a href="{@docRoot}google/play-services/setup.html">set up
- the Google Play services SDK</a>.
- </p>
-
- <h4>2. Run the sample</h4>
- <p>Once you've installed the Google Play services package, you can run the Google+ sample located in
- <code><android-sdk>/extras/google-play-services/samples/plus</code>. However, before
- you can run the sample, you need to register your app in the Google APIs Console.</p>
- <p>See <a class="external-link"
- href="https://developers.google.com/+/mobile/android/getting-started">Getting Started with
- the Google+ Platform for Android</a> for more information about how to get the sample up and
- running.
- </p>
-
- <h4>3. Read the documentation</h4>
- <p>Read the <a href="https://developers.google.com/+/terms">Google+ Platform
- Terms of Service</a>.</p>
-
- <p>For quick access while developing your Android apps, the
- <a href="{@docRoot}reference/com/google/android/gms/plus/package-summary.html">Google+
- API reference</a> is available here on developer.android.com.</p>
-
- <p>Detailed documentation for the Google+ Android APIs is available with the rest of the
- Google+ developer documents at <a class="external-link"
- href="https://developers.google.com/+/mobile/android/">developers.google.com/+</a>.</p>
- </div>
-</div>
diff --git a/docs/html/google/play-services/wallet.jd b/docs/html/google/play-services/wallet.jd
deleted file mode 100644
index 744c8d3..0000000
--- a/docs/html/google/play-services/wallet.jd
+++ /dev/null
@@ -1,87 +0,0 @@
-page.title=Google Wallet Instant Buy for Android
-page.tags=Wallet,payments,Instant Buy
-header.hide=1
-
-@jd:body
-
-<div class="landing-banner">
-<div class="col-8">
- <h1 itemprop="name" style="margin-bottom:0;">Google Wallet Instant Buy</h1>
- <p itemprop="description">Add fast, secure checkout for users buying physical goods and
- services from your app. Transactions are monitored for fraud 24/7. Keep your existing
- payments infrastructure and integrate Google Wallet quickly, easily and free of charge.</p>
-
- <p>To ensure that your app content is consistent with the requirements for
- Instant Buy access, <a class="external-link" href="https://support.google.com/wallet/business/contact/interest">apply for content review</a> before
- starting development. Note that Instant Buy is currently only
- available to US-based merchants. Once you've completed integration, you can
- apply for production access by <a class="external-link" href="https://support.google.com/wallet/business/contact/ui_review">submitting your sandbox integration for review</a>.</p>
-
- <p>Check out the <a
- href="{@docRoot}reference/com/google/android/gms/wallet/package-summary.html">Instant
- Buy API reference</a> and visit
- <a href="https://developers.google.com/wallet/instant-buy/">developers.google.com/wallet/instant-buy/</a>
- for complete information about integrating Google Wallet Instant Buy into your app.</p>
-</div>
-
-<div class="col-4">
- <img src="{@docRoot}images/google/gps-wallet-instant.png" alt="" style="padding-bottom:14px;width:210px">
-</div>
-</div>
-
-<div class="landing-docs">
- <div class="col-6 normal-links">
- <h3 style="clear:left">Key Developer Features</h3>
-
- <h4>Add the "Buy with Google" button</h4>
- <p>Easily embed a “Buy with Google” button in your flow to let customers purchase instantly
- from your app. Customers can grant you access to their payment information with just
- a few clicks.
- <br />
- <a href="https://developers.google.com/commerce/wallet/instant-buy/android/tutorial#add_google_wallet_buttons_to_your_ui"
- class="external-link">Add a "Buy with Google" button</a>.</p>
-
- <h4>Streamline Purchases with Google+ Sign-On</h4>
- <p>For users ready to purchase, you can simplify the login and account creation steps
- by adding Google+ sign in. Users can sign in with a single click and share their
- profile information during the purchase.
- <br />
- <a href="https://developers.google.com/commerce/wallet/instant-buy/wallet-sso#android"
- class="external-link">Add Google+ Sign-In for Wallet</a>.</p>
-
- <h4>Minimize User Data Entry</h4>
- <p>Google Wallet provides auto-completion of addresses, minimizing user data entry. You can also
- retrieve billing and shipping addresses directly from the user’s Wallet to-do form pre-fills.<br />
- <a class="external-link"
- href="{@docRoot}reference/com/google/android/gms/wallet/MaskedWallet.html#getBillingAddress()">Get
- billing addresses</a>.</p>
- </div>
-
-
- <div class="col-6 normal-links">
- <h3 style="clear:left">Getting Started</h3>
- <h4>1. Get the Google Play services SDK</h4>
- <p>The Google Wallet Android APIs are part of the Google Play services platform.</p>
- <p>To get started, <a href="{@docRoot}google/play-services/setup.html">set up
- the Google Play services SDK</a>. Then see the <a class="external-link"
- href="https://developers.google.com/commerce/wallet/instant-buy/android/tutorial">tutorial</a>
- to learn how to set up your app.
- </p>
- <h4>2. Run the sample</h4>
- <p>Once you've installed the Google Play services package, try the Google Wallet
- sample located in <code><android-sdk>/extras/google-play-services/samples/wallet</code>.
- The sample shows you how to use the major components of the Instant Buy API.</p>
- <p>The <a
- class="external-link" href="https://developers.google.com/wallet/instant-buy/android/tutorial">Instant Buy Android API tutorial</a>
- provides directions on how to get the Wallet sample up and running.</p>
- <h4>3. Read the documentation</h4>
- <p>For quick access while developing your Android apps, the <a
- href="{@docRoot}reference/com/google/android/gms/wallet/package-summary.html">Google Wallet
- API reference</a> is available here on developer.android.com.</p>
-
- <p>Detailed documentation for the Instant Buy API is available at <a class="external-link"
- href="https://developers.google.com/wallet/instant-buy/">developers.google.com/wallet/instant-buy/</a></p>
-
-
- </div>
-</div>
diff --git a/docs/html/google/play/billing/billing_subscriptions.jd b/docs/html/google/play/billing/billing_subscriptions.jd
index 51fec09..e412a9d 100644
--- a/docs/html/google/play/billing/billing_subscriptions.jd
+++ b/docs/html/google/play/billing/billing_subscriptions.jd
@@ -1,7 +1,7 @@
page.title=In-app Subscriptions
parent.title=In-app Billing
parent.link=index.html
-page.metaDescription=Subscriptions let you sell content or features in your app with automated, recurring billing.
+page.metaDescription=Create a steady revenue stream by selling subscriptions to your content.
page.image=/images/play_dev.jpg
page.tags="subscriptions, billing, inapp, iap"
meta.tags="monetization, inappbilling, subscriptions"
diff --git a/docs/html/google/play/billing/index.jd b/docs/html/google/play/billing/index.jd
index c671c71..ae6e222 100644
--- a/docs/html/google/play/billing/index.jd
+++ b/docs/html/google/play/billing/index.jd
@@ -1,5 +1,5 @@
-page.title=Google Play In-app Billing
-page.metaDescription=In-app Billing lets you sell digital content as one-time purchases or subscriptions.
+page.title=In-app Billing
+page.metaDescription=Sell digital content as one-time purchases inside your app.
page.image=/images/play_dev.jpg
meta.tags="monetizing, inappbilling, subscriptions"
page.tags="billing, inapp, iap"
@@ -24,7 +24,9 @@
period.</li>
<li><strong>IAB Sandbox</strong>—The In-app Billing Sandbox now supports
testing subscription purchases.</li>
- <li><strong>IAB v2 shutdown</strong>—In-app Billing v2 API is deprecated and will be shut down in January 2015. If your app is still using In-app Billing v2, please migrate to the v3 API as soon as possible.</li>
+ <li><strong>IAB v2 shutdown</strong>—In-app Billing v2 API is deprecated
+ and will be shut down in January 2015. If your app is still using In-app Billing
+ v2, please migrate to the v3 API as soon as possible.</li>
<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
diff --git a/docs/html/google/play/safetynet/index.jd b/docs/html/google/play/safetynet/index.jd
deleted file mode 100644
index b728ca1..0000000
--- a/docs/html/google/play/safetynet/index.jd
+++ /dev/null
@@ -1,82 +0,0 @@
-page.title=SafetyNet for Android
-page.tags=compatibility, CTS
-header.hide=1
-
-@jd:body
-
-<div>
-
-<div>
-
-<h1 itemprop="name" style="margin-bottom:0;">
- Android SafetyNet API
-</h1>
-
-<p itemprop="description">
- SafetyNet provides access to Google services that help you assess the health and safety of an
- Android device. The wide variety of Android devices and configurations can make it difficult to
- know if your app will behave as you expect on all available devices. The SafetyNet API helps you
- determine if your app will function properly on a device by analyzing its compatibility with the
- Android platform specifications.
-</p>
-
-</div>
-</div>
-
-<div class="landing-docs">
- <div class="col-6 normal-links">
- <h3 style="clear:left">Key Developer Features</h3>
-
-<h4>
- Device Profile Compatibility Check
-</h4>
-
-<p>
- Check if your app is running on a device that matches a device model that has passed Android
- compatibility testing. This analysis can help you determine if your app will work as expected on
- the device where it is installed. The service evaluates both software and hardware
- characteristics of the device, and may use hardware roots of trust, when available.
-</p>
-
-</div>
-
-<div class="col-6 normal-links">
-<h3 style="clear:left">
- Getting Started
-</h3>
-
- <h4>
- 1. Review the Terms of Service
- </h4>
-
- <p>
- Use of SafetyNet is governed by specific terms of service, in addition to the <a href=
- "https://developers.google.com/terms/" class="external-link">Google APIs Terms of Service</a>.
- Before using this API, review the <a href="{@docRoot}google/play/safetynet/start.html#tos">
- Additional Terms of Service</a>.
- </p>
-
- <h4>
- 2. Get the Google Play services SDK
- </h4>
-
- <p>
- SafetyNet is part of the Google Play services platform. To get started, follow the instructions
- for <a href="{@docRoot}google/play-services/setup.html">Setting
- up Google Play services</a>.
- </p>
-
- <h4>
- 3. Read the documentation
- </h4>
-
- <p>
- Learn how to use SafetyNet in your app by reading the <a href=
- "{@docRoot}google/play/safetynet/start.html">Getting Started</a> instructions. For more
- details on the API, see the <a href=
- "{@docRoot}reference/com/google/android/gms/safetynet/package-summary.html">
- SafetyNet</a> reference documentation.
- </p>
-
-</div>
-</div>
diff --git a/docs/html/images/cards/card-analytics_2x.png b/docs/html/images/cards/card-analytics_2x.png
new file mode 100644
index 0000000..da62659
--- /dev/null
+++ b/docs/html/images/cards/card-analytics_2x.png
Binary files differ
diff --git a/docs/html/images/cards/card-android-work_2x.png b/docs/html/images/cards/card-android-work_2x.png
new file mode 100644
index 0000000..ac8b9280
--- /dev/null
+++ b/docs/html/images/cards/card-android-work_2x.png
Binary files differ
diff --git a/docs/html/images/cards/card-youtube_2x.png b/docs/html/images/cards/card-youtube_2x.png
new file mode 100644
index 0000000..28bdd89
--- /dev/null
+++ b/docs/html/images/cards/card-youtube_2x.png
Binary files differ
diff --git a/docs/html/images/develop/hero_image_studio5.png b/docs/html/images/develop/hero_image_studio5.png
new file mode 100644
index 0000000..08fa57c
--- /dev/null
+++ b/docs/html/images/develop/hero_image_studio5.png
Binary files differ
diff --git a/docs/html/images/develop/hero_image_studio5_2x.png b/docs/html/images/develop/hero_image_studio5_2x.png
new file mode 100644
index 0000000..f119749
--- /dev/null
+++ b/docs/html/images/develop/hero_image_studio5_2x.png
Binary files differ
diff --git a/docs/html/images/develop/studio-open.png b/docs/html/images/develop/studio-open.png
deleted file mode 100644
index 2e0f599..0000000
--- a/docs/html/images/develop/studio-open.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/distribute/android-work.jpg b/docs/html/images/distribute/android-work.jpg
new file mode 100644
index 0000000..91f742b
--- /dev/null
+++ b/docs/html/images/distribute/android-work.jpg
Binary files differ
diff --git a/docs/html/images/distribute/gpfw_business.png b/docs/html/images/distribute/gpfw_business.png
new file mode 100644
index 0000000..d395b4e
--- /dev/null
+++ b/docs/html/images/distribute/gpfw_business.png
Binary files differ
diff --git a/docs/html/images/distribute/gpfw_developer.png b/docs/html/images/distribute/gpfw_developer.png
new file mode 100644
index 0000000..c0f0d26
--- /dev/null
+++ b/docs/html/images/distribute/gpfw_developer.png
Binary files differ
diff --git a/docs/html/images/distribute/house-ads.png b/docs/html/images/distribute/house-ads.png
new file mode 100644
index 0000000..f4df870
--- /dev/null
+++ b/docs/html/images/distribute/house-ads.png
Binary files differ
diff --git a/docs/html/images/distribute/ota-installs.gif b/docs/html/images/distribute/ota-installs.gif
new file mode 100644
index 0000000..85e40da
--- /dev/null
+++ b/docs/html/images/distribute/ota-installs.gif
Binary files differ
diff --git a/docs/html/images/distribute/signin-apps.png b/docs/html/images/distribute/signin-apps.png
new file mode 100644
index 0000000..9891acd
--- /dev/null
+++ b/docs/html/images/distribute/signin-apps.png
Binary files differ
diff --git a/docs/html/images/distribute/signin-seamless.png b/docs/html/images/distribute/signin-seamless.png
new file mode 100644
index 0000000..01b9d73
--- /dev/null
+++ b/docs/html/images/distribute/signin-seamless.png
Binary files differ
diff --git a/docs/html/images/distribute/signin-secure.png b/docs/html/images/distribute/signin-secure.png
new file mode 100644
index 0000000..3baad23
--- /dev/null
+++ b/docs/html/images/distribute/signin-secure.png
Binary files differ
diff --git a/docs/html/images/distribute/youtube-card-example.png b/docs/html/images/distribute/youtube-card-example.png
new file mode 100644
index 0000000..e5d77f9
--- /dev/null
+++ b/docs/html/images/distribute/youtube-card-example.png
Binary files differ
diff --git a/docs/html/images/play_dev.jpg b/docs/html/images/play_dev.jpg
index 6aae165..92513b7 100644
--- a/docs/html/images/play_dev.jpg
+++ b/docs/html/images/play_dev.jpg
Binary files differ
diff --git a/docs/html/images/play_dev_old.jpg b/docs/html/images/play_dev_old.jpg
new file mode 100644
index 0000000..6aae165
--- /dev/null
+++ b/docs/html/images/play_dev_old.jpg
Binary files differ
diff --git a/docs/html/images/versions/notification-headsup.png b/docs/html/images/versions/notification-headsup.png
index 7c374c8..623b225 100644
--- a/docs/html/images/versions/notification-headsup.png
+++ b/docs/html/images/versions/notification-headsup.png
Binary files differ
diff --git a/docs/html/images/versions/rivalknights.png b/docs/html/images/versions/rivalknights.png
index 6b467ef..6137fc4 100644
--- a/docs/html/images/versions/rivalknights.png
+++ b/docs/html/images/versions/rivalknights.png
Binary files differ
diff --git a/docs/html/index.jd b/docs/html/index.jd
index 71f6d58..c0a5b4b 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -1,5 +1,4 @@
fullpage=true
-page.viewport_width=970
excludeFromSuggestions=true
page.metaDescription=The official site for Android developers. Provides the Android SDK and documentation for app developers and designers.
page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3f61-WpRguHq-aNjtF7xJjMTSi79as" />
@@ -31,13 +30,12 @@
</div><!-- end .wrap -->
</div><!-- end .actions-bar -->
-<div class="dac-hero-carousel" data-carousel-query="collection:distribute/landing/carousel">
-</div>
+
<section class="dac-section dac-section-light" id="build-apps"><div class="wrap">
<h1 class="dac-section-title">Build Beautiful Apps</h1>
<div class="dac-section-subtitle">
- See what’s new or find the resources to get you started with designing and developing for Android.
+ Resources to get you started with designing and developing for Android.
</div>
<div class="resource-widget resource-flow-layout col-16"
data-query="collection:index/primary"
@@ -45,11 +43,14 @@
data-maxResults="3"></div>
</div></section>
+<div class="dac-hero-carousel" data-carousel-query="collection:distribute/landing/carousel">
+</div>
+
<section class="dac-section dac-gray"><div class="wrap">
<div class="cols"><div class="col-10of12 col-push-1of12">
<h1 class="dac-section-title">Build for a Multi-Screen World</h1>
<div class="dac-section-subtitle">
- Android runs on hundreds of millions of handheld devices around the world,
+ Android runs on billions of handheld devices around the world,
and it now supports these exciting, new form-factors.
</div>
</div></div>
diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js
index a293131..4950d97 100644
--- a/docs/html/jd_collections.js
+++ b/docs/html/jd_collections.js
@@ -123,10 +123,18 @@
"resources": [
"https://developers.google.com/analytics/devguides/collection/android/",
"https://developers.google.com/maps/documentation/android/",
- "https://developers.google.com/+/mobile/android/sign-in",
- "https://developers.google.com/places/android/",
+ "https://developers.google.com/identity/sign-in/android/",
+ "https://developers.google.com/mobile-ads-sdk/download",
"https://developers.google.com/gcm/android/",
- "https://developers.google.com/maps/documentation/android/"
+ "https://developers.google.com/app-indexing/"
+ ]
+ },
+ "google/landing/videos": {
+ "title": "",
+ "resources": [
+ "https://www.youtube.com/watch?v=FOn64iqlphk&list=PLWz5rJ2EKKc9Qk1_iCZNbBp6adYnJf9Vf",
+ "https://www.youtube.com/watch?v=F0Kh_RnSM0w&list=PLWz5rJ2EKKc9Qk1_iCZNbBp6adYnJf9Vf",
+ "https://www.youtube.com/watch?v=fvtMtfCuEpw&list=PLWz5rJ2EKKc9Qk1_iCZNbBp6adYnJf9Vf"
]
},
"google/landing/googleplay": {
@@ -203,7 +211,7 @@
"http://youtu.be/vzvpcEffvaE"
]
},
- "launch/static": {
+/* "launch/static": {
"title": "",
"resources": [
"http://www.youtube.com/watch?v=1RIz-cmTQB4",
@@ -221,7 +229,7 @@
"distribute/users/know-your-user.html",
"distribute/googleplay/developer-console.html"
]
- },
+ }, */
"launch/static/ja": {
"title": "",
"resources": [
@@ -272,8 +280,18 @@
"distribute/googleplay/auto.html",
"distribute/googleplay/tv.html",
"distribute/googleplay/wear.html",
+ "distribute/googleplay/cardboard.html",
+ "distribute/googleplay/cast.html",
"distribute/googleplay/edu/about.html",
- "distribute/googleplay/families/about.html"
+ "distribute/googleplay/families/about.html",
+ "distribute/googleplay/work/about.html"
+ ]
+ },
+ "distribute/googleplay/gpfw": {
+ "resources": [
+ "http://www.android.com/work/",
+ "https://www.youtube.com/watch?v=jQWB_-o1kz4&list=PLOU2XLYxmsIKAK2Bhv19H2THwF-22O5WX",
+ "training/enterprise/index.html"
]
},
"distribute/essentials": {
@@ -304,6 +322,9 @@
"distribute/users/expand-to-new-markets.html",
"distribute/users/promote-with-ads.html",
"distribute/users/appindexing.html",
+ "distribute/users/ota-installs.html",
+ "distribute/users/house-ads.html",
+ "distribute/users/youtube.html",
"distribute/users/build-buzz.html",
"distribute/users/build-community.html"
]
@@ -317,6 +338,7 @@
"distribute/engage/deep-linking.html",
"distribute/engage/ads.html",
"distribute/engage/intents.html",
+ "distribute/engage/appindexing.html",
"distribute/engage/analytics.html",
"distribute/engage/game-services.html",
"distribute/engage/app-updates.html",
@@ -503,7 +525,7 @@
"distribute/googleplay/cardboard": {
"title": "Google Cast",
"resources": [
- "https://www.google.com/get/cardboard/",
+ "https://www.google.com/get/cardboard/get-cardboard/",
"https://developers.google.com/cardboard/android/download",
"http://www.google.com/design/spec-vr"
]
@@ -630,7 +652,33 @@
"resources": [
"https://developers.google.com/app-indexing/",
"https://developers.google.com/app-indexing/webmasters/details",
- "distribute/engage/search.html"
+ "distribute/engage/appindexing.html"
+ ]
+ },
+ "distribute/users/otas": {
+ "title": "",
+ "resources": [
+ "https://developers.google.com/identity/sign-in/android/",
+ "https://developers.google.com/+/features/play-installs",
+ "https://developers.google.com/+/features/analytics"
+ ]
+ },
+ "distribute/users/houseads": {
+ "title": "",
+ "resources": [
+ "https://support.google.com/admob/topic/2784623",
+ "https://developers.google.com/mobile-ads-sdk/download",
+ "http://support.google.com/googleplay/android-developer/topic/2985714",
+ "http://analyticsacademy.withgoogle.com/mobile-app",
+ "https://support.google.com/analytics/answer/2611404",
+ "https://support.google.com/admob/answer/3111064"
+ ]
+ },
+ "distribute/users/youtube": {
+ "title": "",
+ "resources": [
+ "https://support.google.com/youtube/answer/6140493",
+ "https://support.google.com/youtube/answer/2797387"
]
},
"distribute/toolsreference/bestpractices/apps": {
@@ -910,10 +958,9 @@
"distribute/engage/gplus": {
"title": "",
"resources": [
- "google/play-services/plus.html",
- "google/play-services/games.html",
- "https://developers.google.com/+/mobile/android/share/interactive-post",
- "https://developers.google.com/+/mobile/android/share/deep-link"
+ "distribute/users/ota-installs.html",
+ "https://developers.google.com/identity/sign-in/android/people",
+ "https://developers.google.com/+/mobile/android/"
]
},
"distribute/engage/community": {
@@ -1523,4 +1570,4 @@
"samples/BasicManagedProfile/index.html"
]
}
-}
+}
\ No newline at end of file
diff --git a/docs/html/jd_extras.js b/docs/html/jd_extras.js
index cf1da97..f318668 100644
--- a/docs/html/jd_extras.js
+++ b/docs/html/jd_extras.js
@@ -81,11 +81,11 @@
"title":"Get Cardboard",
"titleFriendly":"",
"summary":"Get your own Cardboard, today. Buy one from a manufacturer or build your own, and start developing.",
- "url":"https://www.google.com/get/cardboard/",
+ "url":"https://www.google.com/get/cardboard/get-cardboard/",
"group":"",
"keywords": ["carboard","vr"],
"tags": [],
- "image":"images/cards/card-cardboard_2x.jpg",
+ "image":"images/cards/card-cardboard_2x.png",
"type":"Guide"
},
{
@@ -110,9 +110,6 @@
"image":"images/cards/card-cardboard_2x.png",
"type":"Design"
},
-
-
-
{
"title":"Maps",
"titleFriendly":"",
@@ -146,22 +143,6 @@
"image":"images/gcm/gcm-logo.png",
"type":"Guide"
},
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{
"title":"ClassDojo Developer Story",
"titleFriendly":"",
@@ -315,6 +296,30 @@
"type":"video"
},
{
+ "title":"Google Play Services 6.5",
+ "titleFriendly":"",
+ "summary":"Google Play services 6.5 includes new features in Google Maps, Google Drive and Google Wallet as well as the recently launched Google Fit API. ",
+ "url":"https://www.youtube.com/watch?v=fvtMtfCuEpw&list=PLWz5rJ2EKKc9Qk1_iCZNbBp6adYnJf9Vf",
+ "group":"",
+ "keywords": ["google play services"],
+ "tags": [
+ ],
+ "image":"http://i1.ytimg.com/vi/fvtMtfCuEpw/maxresdefault.jpg",
+ "type":"video"
+ },
+ {
+ "title":"Google Play Services 7.0",
+ "titleFriendly":"",
+ "summary":"Google Play services 7.0 is here! we've added the Places API, made enhancements to Location and Google Fit, and you can also remote control your Android TV through the new Nearby Connections API.",
+ "url":"https://www.youtube.com/watch?v=F0Kh_RnSM0w&list=PLWz5rJ2EKKc9Qk1_iCZNbBp6adYnJf9Vf",
+ "group":"",
+ "keywords": ["google play services"],
+ "tags": [
+ ],
+ "image":"http://i1.ytimg.com/vi/F0Kh_RnSM0w/maxresdefault.jpg",
+ "type":"video"
+ },
+ {
"title":"Running a Successful Games Business with Google",
"titleFriendly":"",
"summary":"Sure, we all want to make the next great gaming masterpiece. But we also want to feed our families and/or dogs. Join Bob Meese from the Google Play team as he gives you some key pointers on how to make sure you're best taking advantage of Google Play and running a successful games business.",
@@ -1243,27 +1248,13 @@
"url": "http://www.google.com/analytics/mobile/",
"timestamp": 1383243492000,
"image": "images/cards/analytics-mobile_2x.jpg",
- "title": "Google Mobile App Analytics",
+ "title": "Mobile App Analytics",
"summary": "Mobile App Analytics measures what matters most at all key stages: from first discovery and download to in-app purchases. ",
"keywords": ["analytics,user behavior"],
- "type": "guide",
+ "type": "Guide",
"titleFriendly": ""
},
- {
- "lang": "en",
- "group": "",
- "tags": [
- "#engagement",
- ],
- "url": "https://developers.google.com/app-indexing/",
- "timestamp": 1383243492000,
- "image": "https://www.gstatic.com/images/icons/material/product/2x/search_64dp.png",
- "title": "Sign Up for App Indexing",
- "summary": "Surface your app content in Google seaerch. Deep link direct to your apps.",
- "keywords": [],
- "type": "guide",
- "titleFriendly": ""
- },
+
{
"lang": "en",
@@ -1284,21 +1275,6 @@
"lang": "en",
"group": "",
"tags": [
- "#googleplus",
- ],
- "url": "https://developers.google.com/+/mobile/android/people",
- "timestamp": 1383243492000,
- "image": "images/google/gps-googleplus.png",
- "title": "Google Sign In",
- "summary": "After you let users sign in with Google, you can access their age range, language, public profile information, and people that they have circled.",
- "keywords": ["googleplus"],
- "type": "guide",
- "titleFriendly": ""
- },
- {
- "lang": "en",
- "group": "",
- "tags": [
"#gcm",
],
"url": "http://developer.chrome.com/apps/cloudMessagingV2",
@@ -1373,12 +1349,12 @@
"lang": "en",
"group": "",
"tags": [],
- "url": "https://developers.google.com/+/mobile/android/sign-in",
+ "url": "https://developers.google.com/+/mobile/android/",
"timestamp": 1194884220000,
"image": 'images/google/gps-googleplus.png',
- "title": "Sign-in with Google",
- "summary": "Get users into your app quickly and securely.",
- "keywords": ["signin", "Google+"],
+ "title": "Google+ Platform",
+ "summary": "Find out about features such as interactive posts, Hangouts, accessing basic user details and their social graphs to make your app more personal.",
+ "keywords": ["Google+"],
"type": "guide",
"titleFriendly": ""
},
@@ -1596,10 +1572,9 @@
"tags": ["monetize", "ads"],
"url": "http://support.google.com/googleplay/android-developer/topic/2985714",
"timestamp": null,
- "image": "http://storage.googleapis.com/support-kms-prod/SNP_712EA2784949DDF085C46E3BE7B1DC618A09_4389397_en_v0",
- "image": "https://www.gstatic.com/images/icons/material/product/2x/play_64dp.png",
+ "image":"images/play_dev.jpg",
"title": "Policy Center: Ads",
- "summary": "Introduction to ads and system interference policies in Google Play",
+ "summary": "Introduction to ads and system interference policies in Google Play.",
"keywords": ["ads"],
"type": "distribute",
"titleFriendly": ""
@@ -1607,6 +1582,32 @@
{
"lang": "en",
"group": "",
+ "tags": [],
+ "url": "https://support.google.com/analytics/answer/2611404",
+ "timestamp": null,
+ "image": "images/cards/analytics-mobile_2x.jpg",
+ "title": "Create Audience lists in Google Analytics",
+ "summary": "Find out how to use your analytics data to discover high value users and create remarketing audiences to use in AdMob.",
+ "keywords": ["ads, analytics, monetize"],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://support.google.com/admob/answer/3111064",
+ "timestamp": null,
+ "image": "distribute/images/advertising.jpg",
+ "title": "AdMob in-app conversion tracking",
+ "summary": "Use in-app conversion tracking to attribute revenue back to your IAP promotion campaigns and determine which ones earn you the most.",
+ "keywords": ["ads, analytics, conversions"],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
"tags": ["monetize", "giftcards"],
"url": "https://play.google.com/about/giftcards/",
"timestamp": null,
@@ -1676,7 +1677,7 @@
"url": "https://developers.google.com/analytics/devguides/collection/android/",
"timestamp": null,
"image": "images/cards/analytics-mobile_2x.jpg",
- "title": "Mobile App Analytics",
+ "title": "Mobile App Analytics SDK",
"summary": "Measure everything about your app. Get started with the Google Analytics SDK for Android.",
"keywords": ["analytics, user behavior"],
"type": "sdk",
@@ -2395,7 +2396,7 @@
"tags": [],
"url": "https://support.google.com/adwords/answer/6032059",
"timestamp": null,
- "image": "https://www.gstatic.com/images/icons/material/product/2x/admob_64dp.png",
+ "image": "distribute/images/advertising.jpg",
"title": "Setting up Mobile App Install Ads",
"summary": "With Mobile app installs campaigns on the Search and Display Networks, and TrueView for mobile app promotion on YouTube, you can create custom app install ads that run exclusively on phones and tablets.",
"keywords": ["marketing", "admob"],
@@ -2408,7 +2409,7 @@
"tags": [],
"url": "https://support.google.com/adwords/answer/6167164",
"timestamp": null,
- "image": "https://www.gstatic.com/images/icons/material/product/2x/admob_64dp.png",
+ "image": "distribute/images/advertising.jpg",
"title": "Best practices for Mobile App Engagement",
"summary": "Learn how to market to your user base to drive re-engagement with your app. ",
"keywords": ["marketing", "admob"],
@@ -2452,23 +2453,15 @@
"type": "distribute",
"titleFriendly": ""
},
-
-
-
-
-
-
-
-
{
"lang": "en",
"group": "",
"tags": [],
"url": "https://support.google.com/admob/topic/2784623",
"timestamp": null,
- "image": "https://www.gstatic.com/images/icons/material/product/2x/admob_64dp.png",
+ "image": "distribute/images/advertising.jpg",
"title": "Set up your AdMob account",
- "summary": "Guide to setting up your account so that you get the most value.",
+ "summary": "Setting up your AdMob account in the right way will help you get the most value, check out the Setup and Basics guide.",
"keywords": ["marketing", "admob"],
"type": "distribute",
"titleFriendly": ""
@@ -2479,7 +2472,7 @@
"tags": [],
"url": "http://analyticsacademy.withgoogle.com/mobile-app",
"timestamp": null,
- "image": "https://www.gstatic.com/images/icons/material/product/2x/admob_64dp.png",
+ "image": "distribute/images/advertising.jpg",
"title": "Analytics Academy for Mobile Apps",
"summary": "Learn how to use Google Analytics to make your app more discoverable and profitable.",
"keywords": ["marketing", "analytics"],
@@ -2492,11 +2485,11 @@
"tags": [],
"url": "https://developers.google.com/mobile-ads-sdk/download",
"timestamp": null,
- "image": "https://www.gstatic.com/images/icons/material/product/2x/admob_64dp.png",
- "title": "Google Mobile Ads SDK",
+ "image": "distribute/images/advertising.jpg",
+ "title": "Admob Ads",
"summary": "Use the Mobile Ads SDK to start showing AdMob ads in your apps.",
"keywords": ["marketing", "adwords"],
- "type": "distribute",
+ "type": "Guide",
"titleFriendly": ""
},
{
@@ -2505,7 +2498,7 @@
"tags": [],
"url": "https://support.google.com/admob/",
"timestamp": null,
- "image": "https://www.gstatic.com/images/icons/material/product/2x/admob_64dp.png",
+ "image": "distribute/images/advertising.jpg",
"title": "AdMob Help Center",
"summary": "For setup assistance, general info, and fixes for specific problems check out the AdMob Help Center.",
"keywords": ["admob"],
@@ -2518,56 +2511,97 @@
"tags": [],
"url": "https://support.google.com/admob/answer/2753860",
"timestamp": null,
- "image": "https://www.gstatic.com/images/icons/material/product/2x/admob_64dp.png",
+ "image": "distribute/images/advertising.jpg",
"title": "AdMob Policy Guidelines",
"summary": "Learn about best practices for displaying AdMob ads in your apps to maximize revenue.",
"keywords": ["admob"],
"type": "distribute",
"titleFriendly": ""
},
+
{
"lang": "en",
"group": "",
- "tags": ["appindexing", "search", "getusers"],
+ "tags": [],
"url": "https://developers.google.com/app-indexing/",
"timestamp": 1383243492000,
- "image": "https://www.gstatic.com/images/icons/material/product/2x/search_64dp.png",
+ "image": "images/cards/google-search_2x.png",
"title": "Set Up App Indexing",
- "summary": "Learn more about how Google Search can help users discover your app, along with other ways you can integrate with Google Search.",
- "keywords": ["search"],
+ "summary": "Surface your app content in Google seaerch. Deep link direct to your apps.",
+ "keywords": ["search", "appindexing", "engagement", "getusers"],
"type": "guide",
"titleFriendly": ""
},
- {
+ {
"lang": "en",
"group": "",
- "tags": ["appindexing", "search", "getusers"],
+ "tags": [],
"url": "https://developers.google.com/app-indexing/webmasters/details",
"timestamp": null,
- "image": "https://www.gstatic.com/images/icons/material/product/2x/search_64dp.png",
- "title": "Verify and Create Deep Links",
- "summary": "Index your app today by adding deep links and verifying its official web site to ensure it starts appearing in Google Search results.",
- "keywords": ["search"],
+ "image": "images/cards/google-search_2x.png",
+ "title": "Index your app",
+ "summary": "Index your app today by adding deep links and verifying its official web site to ensure it starts appearing in Google Search results. ",
+ "keywords": ["appindexing","search","getusers"],
"type": "distribute",
"titleFriendly": ""
},
- {
+ {
"lang": "en",
"group": "",
- "tags": [
- "appindexing",
- "search",
- "getusers",
- ],
- "url": "https://support.google.com/admob/answer/2753860",
- "timestamp": null,
- "image": "https://www.gstatic.com/images/icons/material/product/2x/search_64dp.png",
- "title": "Drive use with Google Search",
- "summary": "More about how app indexing and deep links can drive users directly to the content in your app. ",
- "keywords": [],
- "type": "distribute",
+ "tags": [],
+ "url": "https://developers.google.com/identity/sign-in/android/people",
+ "timestamp": 1383243492000,
+ "image": "images/cards/google-sign-in_2x.png",
+ "title": "Get user profile details",
+ "summary": "After users sign-in with Google, you can access their age range, language, and public profile information.",
+ "keywords": ["signin", "identity", "google"],
+ "type": "guide",
"titleFriendly": ""
},
+
+
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://developers.google.com/identity/sign-in/android/",
+ "timestamp": "",
+ "image": "images/cards/google-sign-in_2x.png",
+ "title": "Google Sign-In",
+ "summary": "Discover how you can enhance user experiences on your website or in your app using information provided by their Google identity.",
+ "keywords": ["signin", "identity", "google"],
+ "type": "guide",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://developers.google.com/+/features/play-installs",
+ "timestamp": 1383243492000,
+ "image": "images/cards/google-sign-in_2x.png",
+ "title": "Over-the-air installs",
+ "summary": "Follow this step-by-step guide to quickly add Google Sign-in and over-the-air app installs to your website.",
+ "keywords": ["signin", "google", "installs"],
+ "type": "guide",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://developers.google.com/+/features/analytics",
+ "timestamp": 1383243492000,
+ "image": 'images/google/gps-googleplus.png',
+ "title": "Google+ Insights",
+ "summary": "Measure impressions of the over-the-air install prompt, resulting installs, and success rate by day, week, and month.",
+ "keywords": ["signin", "identity"],
+ "type": "guide",
+ "titleFriendly": ""
+ },
+
+
+
// TODO remove this?
{
"title":"Android Wear Materials",
@@ -2715,7 +2749,7 @@
{
"title":"Opportunities & Programs",
"titleFriendly":"",
- "summary":"This is a card body place holder text. This is a card body place holder text. This is a card body place holder text.",
+ "summary":"Take advantage of the many ways you can distribute your app to consumers, students, and businesses through Google Play.",
"url":"distribute/googleplay/index.html#opportunities",
"group":"",
"keywords": [],
@@ -2725,6 +2759,56 @@
"type":"distribute"
},
{
+ "title":"Android for Work",
+ "titleFriendly":"",
+ "summary":"Learn more about how Android for Work makes your favorite phones and tablets the perfect business tools.",
+ "url":"http://www.android.com/work/",
+ "group":"",
+ "keywords": ["work", "enterprise", "emm"],
+ "tags": [],
+ "image":"images/cards/card-android-work_2x.png",
+ "lang":"en",
+ "type":"about"
+ },
+ {
+ "title":"Android for Work DevBytes",
+ "titleFriendly":"",
+ "summary":"Watch the videos in this playlist to understand more about Android for Work and get tips on developing enterprise apps.",
+ "url":"https://www.youtube.com/watch?v=jQWB_-o1kz4&list=PLOU2XLYxmsIKAK2Bhv19H2THwF-22O5WX",
+ "group":"",
+ "keywords": ["work", "enterprise", "emm"],
+ "tags": [],
+ "image":"http://i1.ytimg.com/vi/jQWB_-o1kz4/maxresdefault.jpg",
+ "lang":"en",
+ "type":"about"
+ },
+ {
+ "title":"Discover YouTube cards",
+ "titleFriendly":"",
+ "summary":"Find out more about YouTube cards, the options available, and how to use them to get the most from your YouTube content.",
+ "url":"https://support.google.com/youtube/answer/6140493",
+ "group":"",
+ "keywords": ["youtube", "video", "users", "installs"],
+ "tags": [],
+ "image":"images/cards/card-youtube_2x.png",
+ "lang":"en",
+ "type":"distribute"
+ },
+ {
+ "title":"What is YouTube account good standing?",
+ "titleFriendly":"",
+ "summary":"Learn what it means for an account to be in good standing from the YouTube Help Center.",
+ "url":"https://support.google.com/youtube/answer/2797387",
+ "group":"",
+ "keywords": ["youtube", "video", "users", "installs"],
+ "tags": [],
+ "image":"images/cards/card-youtube_2x.png",
+ "lang":"en",
+ "type":"distribute"
+ },
+
+
+ {
"lang": "ja",
"title": "Gaming Everywhere",
"titleFriendly": "",
diff --git a/docs/html/preview/api-changes.jd b/docs/html/preview/api-changes.jd
new file mode 100644
index 0000000..e4be2a1
--- /dev/null
+++ b/docs/html/preview/api-changes.jd
@@ -0,0 +1,338 @@
+page.title=Behavior Changes
+page.keywords=preview,sdk,compatibility
+sdk.platform.apiLevel=23
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+<h2>In this document</h2>
+
+<ol id="toc44" class="hide-nested">
+ <li><a href="#behavior-runtime-permissions">Runtime Permissions</a></li>
+ <li><a href="#behavior-notifications">Notifications</a></li>
+ <li><a href="#behavior-openssl">OpenSSL</a></li>
+ <li><a href="#behavior-project-volta">Project Volta</a>
+ <ol>
+ <li><a href="#behavior-doze">Doze Mode</a></li>
+ <li><a href="#behavior-app-standby">App Standby Mode</a></li>
+ </ol>
+ </li>
+ <li><a href="#behavior-adoptable-storage">Adoptable Storage Devices</a></li>
+ <li><a href="#behavior-apache-http-client">Apache HTTP Client Removal</a></li>
+ <li><a href="#behavior-audiomanager-Changes">AudioManager Changes</a></li>
+ <li><a href="#behavior-test-selection">Text Selection</a></li>
+ <li><a href="#behavior-keystore">Android Keystore Changes</a></li>
+ <li><a href="#behavior-themeable-colorstatelists">Themeable ColorStateLists</a></li>
+ <li><a href="#night-mode">Night Mode</a></li>
+ <li><a href="#behavior-art-runtime">ART Runtime</a></li>
+ <li><a href="#behavior-afw">Android for Work Changes</a></li>
+</ol>
+
+<h2>API Differences</h2>
+<ol>
+<li><a href="">API level 22 to M »</a> </li>
+</ol>
+
+
+<h2>See Also</h2>
+<ol>
+<li><a href="{@docRoot}preview/api-overview.html">M Developer Preview API Overview</a> </li>
+</ol>
+
+</div>
+</div>
+
+<p>API Level: M</p>
+<p>Along with new features and capabilities, M includes a variety of
+system changes and API behavior changes. This document highlights
+some of the key changes that you should be understand and account for in your apps.</p>
+
+<p>If you have previously published an app for Android, be aware that your app
+ might be affected by these changes in M.</p>
+
+<h2 id="behavior-runtime-permissions">Runtime Permissions</h1>
+<p>This release introduces a new runtime permissions model, where users can now directly manage
+their app permissions at runtime. This model gives users improved visibility and control over
+permissions, while streamlining the installation and auto-update processes for app developers.
+Users can set permissions on or off for all apps running on Android M. However, apps that don’t
+target M cannot request permissions at runtime.</p>
+
+<p>On your apps that target M, make sure to check and request for permissions at
+runtime. To determine if your app has been granted a permission, call the
+new {@code Context.checkSelfPermission()} method. To request for a permission, call the new
+{@code Activity.requestPermission()} method.</p>
+
+<p>For more information on supporting the new permissions model in your app, see the
+<a href="{@docRoot}preview/features/runtime-permissions.html">
+Android M Runtime Permissions guide</a>.</p>
+
+<h2 id="behavior-openssl">OpenSSL</h2>
+<p>Android is moving away from OpenSSL to the
+<a href="https://boringssl.googlesource.com/boringssl/" class="external-link">BoringSSL</a>
+library. If you’re using the Android NDK in your app, don't link against cryptographic libraries
+that are not a part of the NDK API, such as {@code libcrypto.so} and {@code libssl.so}. These
+libraries are not public APIs, and may change or break without notice across releases and devices.
+In addition, you may expose yourself to security vulnerabilities. Instead, modify your
+native code to call the Java cryptography APIs via JNI or to statically link against a
+cryptography library of your choice.</p>
+
+<h2 id="behavior-project-volta">Project Volta</h2>
+<p>This release introduces new power-saving optimizations for idle devices and apps.</p>
+
+<h3 id="behavior-doze">Doze mode</h3>
+<p>If a device is unplugged and not used for up to an hour, it goes into <em>doze</em> mode where
+it attempts to keep the system in a sleep state. In this mode, devices may briefly resume normal
+operations for up to 5 minutes every few hours so that app syncing can occur and the system can
+perform any pending operations.</p>
+
+<p>The following restrictions apply to your apps while in device doze mode:</p>
+<ul>
+<li>Network access is disabled</li>
+<li>Alarms scheduled with the {@link android.app.AlarmManager} class are disabled, except for
+alarms that you've set with the
+{@link android.app.AlarmManager#setAlarmClock(android.app.AlarmManager.AlarmClockInfo,android.app.PendingIntent) setAlarmClock()}
+method</li>
+<li>WiFi scans are not performed</li>
+<li>Syncs and jobs for your sync adapters and {@link android.app.job.JobScheduler} are not
+permitted to run</li>
+</ul>
+</p>
+<p>When the system comes out of doze mode, it executes jobs and syncs that are pending.</p>
+
+<h3 id="behavior-app-standby">App standby mode</h3>
+<p>In M, the system may determine that apps are idle when they are not in active use by the user.
+Your app goes into <em>app standby</em> mode after two days unless the system detects any of these
+signals:</p>
+
+<ul>
+<li>The app has a process currently in the foreground (either as an activity or foreground service,
+or in use by another activity or foreground service)</li>
+<li>The app generates a notification that the user can see</li>
+<li>The user explicitly asks for the app to remain running</li>
+</ul>
+
+<p>If the system is running on battery power, apps that are in standby mode will have their
+network access disabled and their syncs and jobs suspended. When the system is plugged into a power
+supply, it brings an app out of standby mode and executes any jobs and syncs that are pending.</p>
+
+<p>Apps that use <a href="{@docRoot}google/gcm/index.html">Google Cloud Messaging</a> will
+continue to receive messages even if they are idle. When the system is plugged into a power
+supply, apps resume normal operations and can run any pending syncs and jobs.</p>
+
+<p>You can test this feature by connecting a device running M to your development machine and
+calling the following commands:
+</p>
+<pre>
+$ adb shell am broadcast -a android.os.action.DISCHARGING
+$ adb shell am set-idle <packageName> true
+$ adb shell am set-idle <packageName> false
+$ adb shell am get-idle <packageName>
+</pre>
+
+<h2 id="behavior-adoptable-storage">Adoptable Storage Devices</h2>
+<p>
+In M, users can adopt external storage devices such as SD cards. Adopting an external storage
+device encrypts and formats the device to behave like internal storage. This feature allows users
+to move both apps and private data of those apps between storage devices. When moving apps, the
+system respects the <a href="{@docRoot}guide/topics/manifest/manifest-element.html#install">
+{@code android:installLocation}</a> preference in the manifest.</p>
+
+<p>If your app accesses the following APIs or fields, be aware that the file paths they return
+will dynamically change when the app is moved between internal and external storage devices.
+When building file paths, it is strongly recommended that you always call these APIs dynamically.
+Don’t use hardcoded file paths or persist fully-qualified file paths that were built previously.</p>
+
+<ul>
+<li>{@link android.content.Context} methods:
+ <ul>
+ <li>{@link android.content.Context#getFilesDir() getFilesDir()}</li>
+ <li>{@link android.content.Context#getCacheDir() getCacheDir()}</li>
+ <li>{@link android.content.Context#getCodeCacheDir() getCodeCacheDir()}</li>
+ <li>{@link android.content.Context#getDatabasePath(java.lang.String) getDatabasePath()}</li>
+ <li>{@link android.content.Context#getDir(java.lang.String,int) getDir()}</li>
+ <li>{@link android.content.Context#getNoBackupFilesDir() getNoBackupFilesDir()}</li>
+ <li>{@link android.content.Context#getFileStreamPath(java.lang.String) getFileStreamPath()}</li>
+ <li>{@link android.content.Context#getPackageCodePath() getPackageCodePath()}</li>
+ <li>{@link android.content.Context#getPackageResourcePath() getPackageResourcePath()}</li>
+ </ul>
+</li>
+<li>{@link android.content.pm.ApplicationInfo} fields:
+ <ul>
+ <li>{@link android.content.pm.ApplicationInfo#dataDir dataDir}</li>
+ <li>{@link android.content.pm.ApplicationInfo#sourceDir sourceDir}</li>
+ <li>{@link android.content.pm.ApplicationInfo#nativeLibraryDir nativeLibraryDir}</li>
+ <li>{@link android.content.pm.ApplicationInfo#publicSourceDir publicSourceDir}</li>
+ <li>{@link android.content.pm.ApplicationInfo#splitSourceDirs splitSourceDirs}</li>
+ <li>{@link android.content.pm.ApplicationInfo#splitPublicSourceDirs splitPublicSourceDirs}</li>
+ </ul>
+</li>
+</ul>
+
+<p>To debug this feature in the developer preview, you can enable adoption of a USB drive that is
+connected to an Android device through a USB On-The-Go (OTG) cable, by running these
+commands:</p>
+
+<pre>
+$ adb root
+$ sleep 2
+$ adb shell setprop persist.fw.force_adoptable 1
+$ adb reboot
+</pre>
+
+<h2 id="behavior-apache-http-client">Apache HTTP Client Removal</h2>
+<p>This release removes support for the Apache HTTP client. If your app is using this client and
+targets Android 2.3 (API level 9) or higher, use the {@link java.net.HttpURLConnection} class
+instead. This API is more efficient because it reduces network use through transparent compression
+and response caching, and minimizes power consumption. To continue using the Apache HTTP APIs, you
+must first declare the following compile-time dependency in your {@code build.gradle} file:
+</p>
+<pre>
+android {
+ compileSdkVersion M
+ useLibrary 'org.apache.http.legacy'
+}
+</pre>
+
+<h2 id="behavior-audiomanager-Changes">AudioManager Changes</h2>
+<p>Setting the volume directly or muting specific streams via the {@link android.media.AudioManager}
+class is no longer supported. The {@link android.media.AudioManager#setStreamSolo(int,boolean)
+setStreamSolo()} method is deprecated, and you should call the
+{@code AudioManager.requestAudioFocus()} method instead. Similarly, the
+{@link android.media.AudioManager#setStreamMute(int,boolean) setStreamMute()} method is
+deprecated; instead, call the {@code AudioManager.adjustStreamVolume()} method
+and pass in the direction value {@code ADJUST_MUTE} or {@code ADJUST_UNMUTE}.</p>
+
+<h2 id="behavior-test-selection">Text Selection</h2>
+
+<img src="{@docRoot}preview/images/text-selection.gif"
+style="float:right; margin:0 0 20px 30px" width="270" height="480" />
+
+<p>When users selects text in your app, you can now display text selection actions such as
+<em>Cut</em>, <em>Copy</em>, and <em>Paste</em> in a
+<a href="http://www.google.com/design/spec/patterns/selection.html#selection-text-selection"
+class="external-link">floating toolbar</a>. The user interaction implementation is similar to that
+for the contextual action bar, as described in
+<a href="{@docRoot}guide/topics/ui/menus.html#CABforViews">
+Enabling the contextual action mode for individual views</a>.</p>
+
+<p>To implement a floating toolbar for text selection, make the following changes in your existing
+apps:</p>
+<ol>
+<li>In your {@link android.view.View} or {@link android.app.Activity} object, change your
+{@link android.view.ActionMode} calls from
+{@code startActionMode(Callback)} to {@code startActionMode(Callback, ActionMode.TYPE_FLOATING)}.</li>
+<li>Take your existing implementation of ActionMode.Callback and make it extend
+{@code ActionMode.Callback2} instead.</li>
+<li>Override the {@code Callback2.onGetContentRect()} method to provide the coordinates of the
+content {@link android.graphics.Rect} object (such as a text selection rectangle) in the view.</li>
+<li>If the rectangle positioning is no longer valid, and this is the only element to be invalidated,
+call the {@code ActionMode.invalidateContentRect()} method.</li>
+</ol>
+
+<p>If you are using <a href="{@docRoot}tools/support-library/index.html">
+Android Support Library</a> revision 22.2, be aware that floating toolbars are not
+backward-compatible and appcompat takes control over {@link android.view.ActionMode} objects by
+default. This prevents floating toolbars from being displayed in M. To enable
+{@link android.view.ActionMode} support in an
+{@link android.support.v7.app.AppCompatActivity}, call
+{@code android.support.v7.app.AppCompatActivity.getDelegate()}, then call
+{@code android.support.v7.app.AppCompatDelegate.setHandleNativeActionModesEnabled()} on the returned
+{@link android.support.v7.app.AppCompatDelegate} object and set the input
+parameter to {@code false}. This call returns control of {@link android.view.ActionMode} objects to
+the framework. In devices running M, that allows the framework to support
+{@link android.support.v7.app.ActionBar} or floating toolbar modes, while on pre-M devices, only the
+{@link android.support.v7.app.ActionBar} modes are supported.</p>
+
+<h2 id="behavior-keystore">Android Keystore Changes</h2>
+<p>Starting this release, the
+<a href="{@docRoot}training/articles/keystore.html">Android Keystore provider</a> no longer supports
+DSA. ECDSA is still supported.</p>
+
+<p>Keys which do not require encryption at rest will no longer be deleted when secure lock screen
+is disabled or reset (for example, by the user or a Device Administrator). Keys which require
+encryption at rest will be deleted during these events.</p>
+
+<h2 id="behavior-themeable-colorstatelists">Themeable ColorStateLists</h2>
+<p>Theme attributes are now supported in
+{@link android.content.res.ColorStateList} for devices running M. The
+{@link android.content.res.Resources#getColorStateList(int) getColorStateList()} and
+{@link android.content.res.Resources#getColor(int) getColor()} methods have been deprecated. If
+you are calling these APIs, call the new {@code Context.getColorStateList()} or
+{@code Context.getColor()} methods instead. These methods are also available in the
+v4 appcompat library via {@link android.support.v4.content.ContextCompat}.</p>
+
+<h2 id="night-mode">Night Mode (User-configurable Dark Theme)</h2>
+<p>
+Support for the {@code -night} resource qualifier has been updated in M. Previously, night mode was
+only available when a device was docked and in car mode. Starting in M, night mode is available on
+all devices and is user-configurable via <em>Settings > Display > Theme</em>. You can adjust this
+setting globally using {@link android.app.UiModeManager#setNightMode(int) setNightMode()}. The
+Dark theme corresponds to {@link android.app.UiModeManager#MODE_NIGHT_YES}. When the device is in
+night mode, the resource framework will prefer resources that have the -night qualifier. To
+take advantage of user-configurable Dark mode in your app, extend from the
+{@code Theme.Material.DayNight} set of themes rather than {@code Theme.Material} or
+{@code Theme.Material.Light}.
+</p>
+
+<h2 id="behavior-art-runtime">ART Runtime</h2>
+<p>The ART runtime now properly implements access rules for the
+{@link java.lang.reflect.Constructor#newInstance(java.lang.Object...) newInstance()} method. This
+change fixes a problem where Dalvik was checking access rules incorrectly in previous versions.
+If your app uses the
+{@link java.lang.reflect.Constructor#newInstance(java.lang.Object...) newInstance()} method and you
+want to override access checks, call the
+{@link java.lang.reflect.Constructor#setAccessible(boolean) setAccessible()} method with the input
+parameter set to {@code true}. If your app uses the
+<a href="{@docRoot}tools/support-library/features.html#v7">v7 appcompat library</a> or the
+<a href="{@docRoot}tools/support-library/features.html#v7-recyclerview">v7 recyclerview library</a>,
+you must update your app to use to the latest versions of these libraries. Otherwise, make sure that
+any custom classes referenced from XML are updated so that their class constructors are accessible.</p>
+
+<p>The M release updates the behavior of the dynamic linker. The dynamic linker now understands the
+difference between a library’s {@code soname} and its path
+(<a href="https://code.google.com/p/android/issues/detail?id=6670" class="external-link">
+public bug 6670</a>), and search by {@code soname} is now
+implemented. Apps which previously worked that have bad {@code DT_NEEDED} entries
+(usually absolute paths on the build machine’s file system) may fail when loaded on M.</p>
+
+<p>The {@code dlopen(3) RTLD_LOCAL} flag is now correctly implemented in M. Note that
+{@code RTLD_LOCAL} is the default, so calls to {@code dlopen(3)} that didn’t explicitly use
+{@code RTLD_LOCAL} will be affected (unless your app explicitly used {@code RTLD_GLOBAL}). With
+{@code RTLD_LOCAL}, symbols will not be made available to libraries loaded by later calls to
+{@code dlopen(3)} (as opposed to being referenced by {@code DT_NEEDED} entries).</p>
+</p>
+
+<h2 id="behavior-afw">Android for Work Changes</h2>
+<p>This release includes the following behavior changes for Android for Work:</p>
+<ul>
+<li><strong>Work contacts in personal contexts.</strong> Google Messenger and the Google Dialer
+Call Log now display work contacts when the user views past messages or calls. Furthermore, both
+work and personal contacts are now available to devices over Bluetooth, but you can hide work
+profile contacts through a device policy by calling the new
+{@code DevicePolicyManager.setBluetoothContactSharingDisabled()} method. Initiating a call or
+creating a new message will only show personal contacts, as consistent with the experience in
+Android 5.0.
+</li>
+<li><strong>WiFi configuration removal:</strong> WiFi configurations added by a Profile Owner
+(for example, through calls to the
+{@link android.net.wifi.WifiManager#addNetwork(android.net.wifi.WifiConfiguration)
+addNetwork()} method) are now removed if that work profile is deleted.</li>
+<li><strong>WiFi configuration lockdown:</strong> Any WiFi configuration created by an active Device
+Owner can no longer be modified or deleted by the user. The user can still create and
+modify their own WiFi configurations, so long as the {@link android.os.UserManager} constant
+{@link android.os.UserManager#DISALLOW_CONFIG_WIFI} has not been set for that user.</li>
+<li><strong>VPN in Settings:</strong> VPN apps are now visible in <em>Settings > More > VPN</em>.
+Additionally, the notifications that accompany VPN usage are now specific to whether that VPN is
+configured for a managed profile or the entire device.</li>
+<li><strong>Work status notification:</strong> A status bar briefcase icon now appears whenever
+an app from the managed profile has an activity in the foreground. Furthermore, if the device is
+unlocked directly to the activity of an app in the managed profile, a toast is displayed notifying
+the user that they are within the work profile.
+</li>
+<li><strong>Download Work Policy Controller via Google account addition:</strong> When a Google
+account that requires management via a Work Policy Controller (WPC) app is added to a device
+outside of a managed context, the add account flow now prompts the user to install the
+appropriate WPC. This behavior also applies to accounts added via
+<em>Settings > Accounts</em> in the initial device setup wizard.</li>
+</ul>
diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd
index dde3c7be..5ab4b89 100644
--- a/docs/html/preview/api-overview.jd
+++ b/docs/html/preview/api-overview.jd
@@ -1,6 +1,7 @@
page.title=API Overview
page.keywords=preview,sdk,compatibility
-sdk.platform.apiLevel=22
+sdk.platform.apiLevel=22-mnc
+page.image=images/cards/card-key-changes_16-9_2x.png
@jd:body
@@ -13,24 +14,24 @@
<span class="less" style="display:none">show less</span></a></h2>
<ol id="toc44" class="hide-nested">
- <li><a href="#">Important Behavior Changes</a>
- <ol>
- <li><a href="#">change 1</a></li>
- <li><a href="#">change 2</a></li>
- </ol>
+ <li><a href="#backup">Auto Backup for Apps</a></li>
+ <li><a href="#notifications">Notifications</a></li>
+ <li><a href="#authentication">Authentication</a>
+ <ul>
+ <li><a href="#fingerprint-authentication">Fingerprint Authentication</a></li>
+ <li><a href="#confirm-credentials">Confirm Credentials</a></li>
+ </ul>
</li>
- <li><a href="#">Feature Group 1</a>
- <ol>
- <li><a href="#">change 1</a></li>
- <li><a href="#">change 2</a></li>
- </ol>
- </li>
- <li><a href="#">Feature Group 2</a>
- <ol>
- <li><a href="#">change 1</a></li>
- <li><a href="#">change 2</a></li>
- </ol>
- </li>
+ <li><a href="#direct-share">Direct Share</a></li>
+ <li><a href="#voice-interactions">Voice Interactions</a></li>
+ <li><a href="#bluetooth-stylus">Bluetooth Stylus Support</a></li>
+ <li><a href="#audio">New Audio Features</a></li>
+ <li><a href="#afw">New Android for Work Features</a></li>
+</ol>
+
+<h2>API Differences</h2>
+<ol>
+<li><a href="">API level 22 to M »</a> </li>
</ol>
</div>
@@ -54,65 +55,313 @@
href="{@docRoot}">developer.android.com</a>. These API elements are
formatted in {@code code style} in this document (without hyperlinks). For the
preliminary API documentation for these elements, download the <a
-href="http://storage.googleapis.com/androiddevelopers/preview/l-developer-preview-reference.zip">preview
-reference</a>.</p>
+href="http://storage.googleapis.com/androiddevelopers/preview/m-developer-preview-reference.zip">
+preview reference</a>.</p>
-<h2 id="Behaviors">Important Behavior Changes</h2>
+<h3>Important behavior changes</h3>
-<p>If you have previously published an app for Android, be aware that your app
- might be affected by changes in the upcoming release.</p>
+<p>If you have previously published an app for Android, be aware that your app might be affected
+by changes in M.</p>
-<h3 id="id">Behavior Change 1</h3>
+<p>Please see <a href="api-changes.html">Behavior Changes</a> for complete information.</p>
-<p>
- Bacon ipsum dolor amet biltong picanha t-bone, jowl salami tri-tip jerky kielbasa sirloin boudin
- porchetta fatback cow meatloaf capicola. Short ribs kielbasa pig drumstick rump boudin jowl chuck
- beef ribs doner tenderloin biltong swine.
+<h2 id="backup">Auto Backup for Apps</h2>
+<p>The system now performs automatic full data backup and restore for apps. This behavior is
+enabled by default for apps targeting M; you do not need to add any additional code. If users
+delete their Google account, their backup data is deleted as well.</p>
+<p>To learn how this feature works and how to configure what to back up on the file system,
+see the <a href="">App Backup for Apps guide</a>.</p>
+
+<h2 id="notifications">Notifications</h2>
+<p>M adds the following API changes for notifications:</p>
+<ul>
+ <li>New {@code NotificationListenerService.INTERRUPTION_FILTER_ALARMS} filter level that
+ corresponds to the new <em>Alarms only</em> do not disturb mode.</li>
+ <li>New {@code Notification.CATEGORY_REMINDER} category value that is used to distinguish
+ user-scheduled reminders from other events
+ ({@link android.app.Notification#CATEGORY_EVENT}) and alarms
+ ({@link android.app.Notification#CATEGORY_ALARM}).</li>
+ <li>New {@code android.graphics.drawable.Icon} class which can be attached to your notifications
+ via the Notification.Builder.setIcon() and Notification.Builder.setLargeIcon() methods.</li>
+ <li>New {@code NotificationManager.getActiveNotifications()} method that allows your apps to
+ find out which of their notifications are currently alive.</li>
+</ul>
+
+<h2 id="authentication">Authentication</h2>
+<p>The M release offers new APIs to let you authenticate users by using their fingerprint scans on
+supported devices, and check how recently the user was last authenticated using a device unlocking
+mechanism (such as a lockscreen password). Use these APIs in conjunction with
+the <a href="{@docRoot}training/articles/keystore.html">Android Keystore system</a>.</p>
+
+<h3 id="fingerprint-authentication">Fingerprint Authentication</h3>
+
+<p>To authenticate users via fingerprint scan, get an instance of the new
+{@code android.hardware.fingerprint.FingerprintManager} class and call the
+{@code FingerprintManager.authenticate()} method. Your app must be running on a device with a
+fingerprint sensor. You must implement the user interface for the fingerprint
+authentication flow on your app, and use the standard fingerprint Android icon in your UI.
+If you are developing multiple apps that use fingerprint authentication, note that each app must
+authenticate the user’s fingerprint independently.
</p>
+<img src="{@docRoot}preview/images/fingerprint-screen_2x.png"
+srcset="{@docRoot}preview/images/fingerprint-screen.png 1x, preview/images/fingerprint-screen_2x.png 2x"
+style="margin:0 0 10px 20px" width="282" height="476" />
+<p>To use this feature in your app, first add the {@code USE_FINGERPRINT} permission in your
+manifest.</p>
-<h2 id="id">Feature Group 1</h2>
+<pre>
+<uses-permission
+ android:name="android.permission.USE_FINGERPRINT" />
+</pre>
-<h3 id="id">Feature item 1</h3>
+<p>The following snippet shows how you might listen for fingerprint events in your
+{@code FingerprintManager.AuthenticationCallback} implementation.</p>
-<p>
- Bacon ipsum dolor amet landjaeger capicola tail sausage shank swine biltong pork andouille t-bone
- alcatra chicken. Strip steak bacon tongue beef bresaola landjaeger. Shankle boudin pork belly
- jowl pig. Rump swine ham hock frankfurter pork shankle. Shank corned beef alcatra doner flank
- turducken. Tongue brisket ham shoulder:
+<pre>
+// Call this to start listening for fingerprint events
+public void startListening(FingerprintManager.CryptoObject cryptoObject) {
+ if (!isFingerprintAuthAvailable()) {
+ return;
+ }
+ mCancellationSignal = new CancellationSignal();
+ mSelfCancelled = false;
+ mFingerprintManager.authenticate(cryptoObject,
+ mCancellationSignal, this, 0 /* flags */);
+ // Icon to display when prompting users to start a fingerprint scan
+ mIcon.setImageResource(R.drawable.ic_fp_40px);
+}
+
+// Helper method to check if the device supports fingerprint
+// scanning and if the user has enrolled at least one fingerprint.
+public boolean isFingerprintAuthAvailable() {
+ return mFingerprintManager.isHardwareDetected()
+ && mFingerprintManager.hasEnrolledFingerprints();
+}
+</pre>
+
+<h3 id="confirm-credentials">Confirm Credentials</h3>
+<p>Your app can authenticate users based on how recently they last unlocked their device. You can
+use the same public or secret key to authenticate users into multiple apps. This feature frees
+users from having to remember additional app-specific passwords, and avoids the need for you to
+implement your own authentication user interface.</p>
+
+<p>You can set your own authentication policy by setting constraints against the key that you are
+generating or importing. To set the constraints for using a key, use the
+{@code android.security.KeyPairGeneratorSpec.Builder} and
+{@code android.security.KeyGeneratorSpec.Builder} classes for public key pairs and secret keys
+respectively. If you are importing keys, use the {@link android.security.KeyStoreParameter.Builder}
+class to set your constraints.</p>
+
+<p>The following example shows how you might create a symmetric key in the Keystore which can only be
+used if the user has successfully unlocked the device within the last 5 minutes.</p>
+
+<pre>
+private void createKey() {
+ // Generate a key to decrypt payment credentials, tokens, etc.
+ // This will most likely be a registration step for the user when
+ // they are setting up your app.
+ try {
+ KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
+ ks.load(null);
+ KeyGenerator keyGenerator = KeyGenerator.getInstance("AES",
+ "AndroidKeyStore");
+ keyGenerator.init(new KeyGeneratorSpec.Builder(this)
+ // Alias of the entry in Android KeyStore where the key will appear
+ .setAlias(KEY_NAME)
+ // Key use constraints
+ .setPurposes(KeyStoreKeyProperties.Purpose.ENCRYPT
+ | KeyStoreKeyProperties.Purpose.DECRYPT)
+ .setBlockModes("CBC")
+ .setUserAuthenticationRequired(true)
+ // Require that the user has unlocked in the last 5 minutes
+ .setUserAuthenticationValidityDurationSeconds(5 * 60)
+ .setEncryptionPaddings("PKCS7Padding")
+ .build());
+ keyGenerator.generateKey();
+ } catch (NoSuchAlgorithmException | NoSuchProviderException
+ | InvalidAlgorithmParameterException | KeyStoreException
+ | CertificateException | IOException e) {
+ throw new RuntimeException(e);
+ }
+}
+</pre>
+
+<p>To determine the last time users logged into their account, call the
+{@code android.accounts.AccountManager.confirmCredentials()} method. If the call is successful, the
+method returns an bundle that includes a {@code KEY_LAST_AUTHENTICATED_TIME} value which indicates
+the last time, in milliseconds, that the credential for that account was validated or created.</p>
+
+<h2 id="direct-share">Direct Share</h2>
+
+<img src="{@docRoot}preview/images/direct-share-screen_2x.png"
+srcset="{@docRoot}preview/images/direct-share-screen.png 1x, preview/images/direct-share-screen_2x.png 2x"
+style="float:right; margin:0 0 20px 30px" width="312" height="385" />
+
+<p>This release provides you with APIs to makes sharing intuitive and quick for users. You can now
+define <em>deep links</em> that target a specific activity in your app. These deep links are
+exposed to users via the <em>Share</em> menu. This feature allows users to share content to
+targets, such as contacts, within other apps. For example, the deep link might launch an
+activity in another social network app, which lets the user share content directly to a specific
+friend or community in that app.</p>
+
+<p>To enable sharing via deep links, you must define a class that extends the
+{@code android.service.} <br>
+{@code chooser.ChooserTargetService} class. Declare your
+{@code ChooserTargetService} in the manifest. Within that declaration, specify the
+{@code BIND_CHOOSER_TARGET_SERVICE} permission and an intent filter with the
+{@code SERVICE_INTERFACE} action.</p>
+<p>The following example shows how you might declare the {@code ChooserTargetService} in your
+manifest.</p>
+<br>
+<br>
+<br>
+<pre>
+<service android:name=".ChooserTargetService"
+ android:label="@string/service_name"
+ android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.chooser.ChooserTargetService" />
+ </intent-filter>
+</service>
+</pre>
+
+<p>For each activity that you want to expose to the {@code ChooserTargetService}, add a
+{@code <meta-data>} element with the name
+{@code "android.service.chooser.chooser_target_service"} in your app manifest.
</p>
-<h3 id="id">Feature item 2</h3>
+<pre>
+<activity android:name=".MyShareActivity”
+ android:label="@string/share_activity_label">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND" />
+ </intent-filter>
+<meta-data
+ android:name="android.service.chooser.chooser_target_service"
+ android:value=".ChooserTargetService" />
+</activity>
+</pre>
+<h2 id="voice-interactions">Voice Interactions</h2>
<p>
- Bacon ipsum dolor amet landjaeger capicola tail sausage shank swine biltong pork andouille t-bone
- alcatra chicken. Strip steak bacon tongue beef bresaola landjaeger. Shankle boudin pork belly
- jowl pig. Rump swine ham hock frankfurter pork shankle. Shank corned beef alcatra doner flank
- turducken. Tongue brisket ham shoulder:
+This release provides a new voice interaction API which, together with
+<a href="https://developers.google.com/voice-actions/" class="external-link">Voice Actions</a>,
+allows you to build conversational voice experiences into your apps. Call the
+{@code android.app.Activity.isVoiceInteraction()} method to determine if your activity was
+started in response to a voice action. If so, your app can use the
+{@code android.app.VoiceInteractor} class to request a voice confirmation from the user, select
+from a list of options, and more.</p>
+<p>To learn more about implementing voice actions, see the voice interaction API
+<a href="https://developers.google.com/voice-actions/interaction/"
+class="external-link">guide</a>.
</p>
-<h2 id="id">Feature Group 2</h2>
+<h2 id="bluetooth-stylus">Bluetooth Stylus Support</h2>
+<p>The M release provides improved support for user input using a Bluetooth stylus. If the user
+touches a stylus with a button on the screen of your app, the
+{@link android.view.MotionEvent#getToolType(int) getTooltype()} method now returns
+{@code TOOL_TYPE_STYLUS}. The {@link android.view.MotionEvent#getButtonState() getButtonState()}
+method returns {@link android.view.MotionEvent#BUTTON_SECONDARY} when the user
+presses the primary stylus button. If the stylus has a second button, the same method returns
+{@link android.view.MotionEvent#BUTTON_TERTIARY} when the user presses it. If the user presses
+both buttons simultaneously, the method returns both these values. In addition, the system reports
+the user button-press action to the new {@code View.onStylusButtonPressListener} and
+{@code GestureDetector.OnStylusButtonPressListener} callbacks in your activity, if you have
+registered these listeners in your app.</p>
-<h3 id="id">Feature item 1</h3>
+<h2 id="audio">New Audio Features</h2>
-<p>
- Bacon ipsum dolor amet landjaeger capicola tail sausage shank swine biltong pork andouille t-bone
- alcatra chicken. Strip steak bacon tongue beef bresaola landjaeger. Shankle boudin pork belly
- jowl pig. Rump swine ham hock frankfurter pork shankle. Shank corned beef alcatra doner flank
- turducken. Tongue brisket ham shoulder:
-</p>
+<p>This release adds enhancements to audio processing on Android, including: </p>
+<ul>
+ <li>Support for the <a href="http://en.wikipedia.org/wiki/MIDI" class="external-link">MIDI</a>
+protocol, with the new {@code android.media.midi} APIs. Use these APIs to send and receive MIDI
+events.</li>
+ <li>New {@code android.media.AudioRecord.Builder} and {@code android.media.AudioTrack.Builder}
+classes to create digital audio capture and playback objects respectively, and configure audio
+source and sink properties to override the system defaults.</li>
+ <li>API hooks for associating audio and input devices. This is particularly useful if your app
+allows users to start a voice search from a game controller or remote control connected to Android
+TV. The system invokes the new {@code android.app.Activity.onSearchRequested()} callback when the
+user starts a search. To determine if the user's input device has a built-in microphone, retrieve
+the {@link android.view.InputDevice} object from that callback, then call the new
+{@code InputDevice.hasMic()} method.</li>
+ <li>New {@code android.media.AudioDevicesManager} class which lets you retrieve a list of all
+attached source and sink audio devices. You can also specify an
+{@code android.media.OnAudioDeviceConnectionListener} object if you want your app to be notified
+when an audio device is connected or disconnected.</li>
+</ul>
-<h3 id="id">Feature item 2</h3>
-
-<p>
- Bacon ipsum dolor amet landjaeger capicola tail sausage shank swine biltong pork andouille t-bone
- alcatra chicken. Strip steak bacon tongue beef bresaola landjaeger. Shankle boudin pork belly
- jowl pig. Rump swine ham hock frankfurter pork shankle. Shank corned beef alcatra doner flank
- turducken. Tongue brisket ham shoulder:
-</p>
-
-
+<h2 id="afw">New Android for Work Features</h2>
+<p>This release includes the following new APIs for Android for Work:</p>
+<ul>
+ <li><strong>Enhanced controls for Corporate-Owned, Single-Use devices:</strong> The Device Owner
+can now control the following settings to improve management of
+Corporate-Owned, Single-Use (COSU) devices:
+ <ul>
+ <li>Disable or re-enable the keyguard with the
+{@code DevicePolicyManager.setKeyguardEnabledState()} method.</li>
+ <li>Disable or re-enable the status bar (including quick settings, notifications, and the
+navigation swipe-up gesture that launches Google Now) with the
+{@code DevicePolicyManager.setStatusBarEnabledState()} method.</li>
+ <li>Disable or re-enable safe boot with the {@link android.os.UserManager} constant
+{@code DISALLOW_SAFE_BOOT}.</li>
+ <li>Prevent the screen from turning off while plugged in with the
+ {@link android.provider.Settings.Global} constant {@code STAY_ON_WHILE_PLUGGED_IN}.</li>
+ </ul>
+ </li>
+ <li><strong>Silent install and uninstall of apps by Device Owner:</strong> A Device Owner can now
+silently install and uninstall applications using the {@link android.content.pm.PackageInstaller}
+APIs, independent of Google Play for Work. You can now provision devices through a Device Owner that
+fetches and installs apps without user interaction. This feature is useful for enabling one-touch
+provisioning of kiosks or other such devices without activating a Google account.</li>
+<li><strong>Silent enterprise certificate access: </strong> When an app calls
+{@link android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity,android.security.KeyChainAliasCallback,java.lang.String[],java.security.Principal[],java.lang.String,int,java.lang.String) choosePrivateKeyAlias()},
+prior to the user being prompted to select a certificate, the Profile or Device Owner can now call
+the {@code DeviceAdminReceiver.onChoosePrivateKeyAlias()} method to provide the alias silently to
+the requesting application. This feature lets you grant managed apps access to certificates
+without user interaction.</li>
+<li><strong>Auto-acceptance of system updates.</strong> By setting a system update policy with
+{@code DevicePolicyManager.setSystemUpdatePolicy()}, a Device Owner can now auto-accept a system
+update, for instance in the case of a kiosk device, or postpone the update and prevent it being
+taken by the user for up to 30 days. Furthermore, an administrator can set a time window in which an
+update must be taken, for example during the hours when a kiosk device is not in use. When a
+system update is available, the system checks if the Work Policy Controller app has set a system
+update policy, and behaves accordingly.
+</li>
+<li>
+<strong>Delegated certificate installation.</strong> A Profile or Device Owner can now grant a
+third-party app the ability to call these {@link android.app.admin.DevicePolicyManager} certificate
+management APIs:
+<ul>
+ <li>{@link android.app.admin.DevicePolicyManager#getInstalledCaCerts(android.content.ComponentName)
+getInstalledCaCerts()}</li>
+ <li>{@link android.app.admin.DevicePolicyManager#hasCaCertInstalled(android.content.ComponentName,byte[])
+hasCaCertInstalled()}</li>
+ <li>{@link android.app.admin.DevicePolicyManager#installCaCert(android.content.ComponentName,byte[])
+installCaCert()}</li>
+ <li>{@link android.app.admin.DevicePolicyManager#uninstallCaCert(android.content.ComponentName,byte[])
+uninstallCaCert()}</li>
+ <li>{@link android.app.admin.DevicePolicyManager#uninstallAllUserCaCerts(android.content.ComponentName)
+uninstallAllUserCaCerts()}</li>
+ <li>{@link android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName,java.security.PrivateKey,java.security.cert.Certificate,java.lang.String)
+installKeyPair()}</li>
+</ul>
+</li>
+<li><strong>Enterprise factory reset protection.</strong> When provisioning a Device Owner, you can
+now configure parameters for bypassing Factory Reset Protection (FRP), by setting the
+{@code DeviceManagerPolicy.EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS} bundle. An NFC Programmer
+app can provide these parameters after a device has been reset to bypass FRP and provision the device,
+without requiring the previously configured Google account. If you don't modify these parameters,
+FRP remains in-place and prevents the device from being activated without the previously activated
+Google credentials.</li>
+<li><strong>Data usage tracking.</strong> A Profile or Device Owner can now query for the data
+usage statistics visible in <em>Settings > Data</em> usage by using the new
+{@code android.app.usage.NetworkStatsManager} methods. Profile Owners are automatically granted
+permission to query data on the profile they manage, while Device Owners get access to usage data
+of the managed primary user.</li>
+</ul>
<p class="note">
For a detailed view of all API changes in the M Developer Preview, see the <a href=
diff --git a/docs/html/preview/backup/index.jd b/docs/html/preview/backup/index.jd
new file mode 100644
index 0000000..9ef5db0
--- /dev/null
+++ b/docs/html/preview/backup/index.jd
@@ -0,0 +1,300 @@
+page.title=Automatic App Data Backup
+page.tags=backup
+
+@jd:body
+
+<p>
+ Users often invest significant time and effort collecting data and setting preferences within
+ apps. Preserving that data for users if they replace a broken device or upgrade to a new one is
+ an important part of ensuring a great user experience. The Android M Preview system helps ensure
+ a good experience for users in this circumstances by automatically backing up app data to the
+ cloud.
+</p>
+
+<p>
+ This behavior is enabled by default for all apps installed on devices running Android M or
+ higher. No additional app code is required. The system provides users with the ability opt out of
+ automatic data backups for individual apps. You can also choose to limit what data from your app
+ is backed up.
+</p>
+
+<p>
+ This document describes the new system behavior and how to specify what data is backed up for
+ your app.
+</p>
+
+<h2>Overview</h2>
+
+<p>
+ The automatic backup feature preserves the data your app creates on a user device by uploading to
+ the user’s Google Drive account and encrypting it. There is no charge to you or the user for data
+ storage and the saved data does not count towards the user's personal Drive quota. During the M
+ Preview period, users can store up to 25MB per Android app.
+</p>
+
+<p>
+ Automatic backups occur every 24 hours, when the device is idle, charging, and connected to a
+ Wi-Fi network. When these conditions are met, the Backup Manager service uploads all available
+ backup data to the cloud. When the user transitions to a new device, or uninstalls and reinstalls
+ the backed up application, a restore operation will take place, copying the backed up data into
+ the newly installed application’s data directory.
+</p>
+
+
+<h3>Automatically Excluded Data Files</h3>
+
+<p>
+ Not all app data should be backed up, such as temporary files and caches, so the automatic backup
+ service excludes certain data files by default:
+</p>
+
+<ul>
+ <li>Files in the directories referred to by the <code><a href=
+ "{@docRoot}reference/android/content/Context.html#getCacheDir()">getCacheDir()</a></code> and
+ <code><a href=
+ "{@docRoot}reference/android/content/ContextWrapper.html#getCodeCacheDir()">getCodeCacheDir()</a></code>
+ methods.
+ </li>
+
+ <li>Files located on external storage, unless they reside in the directory referred to by the
+ <code><a href=
+ "{@docRoot}reference/android/content/Context.html#getExternalFilesDir(java.lang.String)">getExternalFilesDir()</a></code>
+ method.
+ </li>
+
+ <li>Files located in the directory referred to by the <code><a href=
+ "{@docRoot}reference/android/content/Context.html#getNoBackupFilesDir()">getNoBackupFilesDir()</a></code>
+ method.
+ </li>
+</ul>
+
+<h2>Configuring Data Backup</h2>
+
+<p>
+ The data created by any app installed on an M device is backed up, except for the automatically
+ excluded files listed in the previous section. You can further limit and configure what data gets
+ backed up from your app using settings in your app manifest.
+</p>
+
+<h3>Including or Excluding Data</h3>
+
+<p>
+ Depending on what data your application needs and how you save it, you may need to set specific
+ rules for including or excluding certain files or directories. The automatic backup service
+ supports setting these backup rules through use of an XML configuration file and the app
+ manifest. In the app manifest, you can specify a backup scheme configuration file as shown in the
+ following example:
+</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.my.appexample">
+ <uses-sdk android:minSdkVersion="9"/>
+ <uses-sdk android:targetSdkVersion="android-MNC"/>
+ <application ...
+<strong> android:fullBackupContent="@xml/mybackupscheme"></strong>
+ </application>
+ ...
+</manifest>
+</pre>
+
+<p>
+ In this example code, the android:fullBackupContent attribute specifies an XML file, located in
+ the <code>res/xml/</code> directory of your app development project, named
+ <code>mybackupscheme.xml</code>. This configuration file can include rules for what files are
+ backed up. The following example code shows a configuration file that excludes a specific file
+ from backups:
+</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+ <full-backup-content>
+ <exclude domain="database" path="device_info.db"/>
+</full-backup-content>
+</pre>
+
+<p>
+ This backup configuration only excludes a specific database file from being backed up. All other
+ files are backed up.
+</p>
+
+<h4>Backup Configuration Syntax</h4>
+
+<p>
+ The backup service configuration allows you to specify what files to include or exclude from
+ backup. The syntax for the data backup configuration xml file is as follows:
+</p>
+
+<pre>
+<full-backup-content>
+ <include domain=["file" | "database" | "sharedpref" | "external" | "root"] path="string" />
+ <exclude domain=["file" | "database" | "sharedpref" | "external" | "root"] path="string" />
+</full-backup-content>
+</pre>
+
+<p>
+ The following elements and attributes allow you to specify the files to include and exclude from
+ backup:
+</p>
+
+<ul>
+ <li>
+ <code><include></code>. Use this element if you want to specify a set of resources to
+ back up, instead of having the system back up all data in your app by default. When you specify
+ an <code><include></code> tag, the system backs up only the resources specified with this
+ element.
+ </li>
+
+ <li>
+ <code><exclude></code>. Use this element to specify a set of resources to exclude from
+ backup. The system backs up all data in your app, except for resources specified with this
+ element.
+ </li>
+
+ <li>
+ <code>domain.</code> The type of resource you want to include or exclude from backup. The valid
+ values you can specify for this attribute include:
+ </li>
+
+ <li style="list-style: none">
+ <ul>
+ <li>
+ <code>root</code>. Specifies that the resource is in the app’s root directory.
+ </li>
+
+ <li>
+ <code>file</code>. Corresponds to a resource in the directory returned by the
+ <code><a href="{@docRoot}reference/android/content/Context.html#getFilesDir()">getFilesDir()</a></code>
+ method.
+ </li>
+
+ <li>
+ <code>database</code>. Corresponds to a database returned by the <code><a href=
+ "{@docRoot}reference/android/content/Context.html#getDatabasePath(java.lang.String)">getDatabasePath()</a></code>
+ method or by using the <code><a href=
+ "{@docRoot}reference/android/database/sqlite/SQLiteOpenHelper.html">SQLiteOpenHelper</a></code>
+ class.
+ </li>
+
+ <li>
+ <code>sharedpref</code>. Corresponds to a <code><a href=
+ "{@docRoot}reference/android/content/SharedPreferences.html">SharedPreferences</a></code>
+ object returned by the <code><a href=
+ "{@docRoot}reference/android/content/Context.html#getSharedPreferences(java.lang.String,%20int)">
+ getSharedPreferences()</a></code> method.
+ </li>
+
+ <li>
+ <code>external</code>. Specifies that the resource is in external storage, and corresponds
+ to a file in the directory returned by the <code><a href=
+ "{@docRoot}reference/android/content/Context.html#getExternalFilesDir(java.lang.String)">getExternalFilesDir()</a></code>
+ method.
+ </li>
+
+ <li>
+ <code>path</code>. The file path to a resource that you want to include or exclude from
+ backup.
+ </li>
+ </ul>
+ </li>
+</ul>
+
+
+<h3>Prohibiting Data Backups</h3>
+
+<p>
+ You can choose to prevent automatic backups of any of your app data by setting the
+ <code>android:allowBackup</code> attribute to <code>false</code> in the application element of
+ your manifest. This setting is illustrated in the following example code:
+</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.my.appexample">
+ <uses-sdk android:minSdkVersion="9"/>
+ <uses-sdk android:targetSdkVersion="android-MNC"/>
+ <application ...
+<strong> android:allowBackup="false"></strong>
+ </application>
+ ...
+</manifest>
+</pre>
+
+
+<h2>Testing Backup Configuration</h2>
+
+<p>
+ Once you have created a backup configuration, you should test it to make sure your app saves data
+ and can be restored properly.
+</p>
+
+
+<h4>Enabling Backup Logging</h4>
+
+<p>
+ To help determine how the backup feature is parsing your XML file, enable logging before
+ performing a test backup:
+</p>
+
+<pre>$ adb shell setprop log.tag.BackupXmlParserLogging VERBOSE</pre>
+
+<h4>Testing Backup</h4>
+
+<p>
+ To manually enable a backup, call the following command, specifying the package name for your app
+ as the <code><PACKAGE></code> parameter:
+</p>
+
+<pre>$ adb shell bmgr fullbackup <PACKAGE></pre>
+
+<h4>Testing Restore</h4>
+<p>
+ To manually initiate a restore after your app data is backed-up, call the following command,
+ specifying the package name for your app as the <code><PACKAGE></code> parameter:
+</p>
+
+<pre>$ adb shell bmgr restore <PACKAGE></pre>
+
+<p class="warning">
+ <b>Warning:</b> This action stops your app and wipes its data before performing the restore
+ operation.
+</p>
+
+<p>
+ You initiate the restore process for your app by uninstalling and reinstalling your app. The app
+ data is automatically restored from the cloud once the app installation is complete.
+</p>
+
+
+<h4>Troubleshooting Backups</h4>
+
+<p>
+ If you run into issues, clear the backup data and associated metadata by calling this command.
+</p>
+
+<pre>$ adb shell bmgr wipe <TRANSPORT> <PACKAGE></pre>
+
+<p>
+ The <code><TRANSPORT></code> value must be prefixed by <code>com.google.android.gms</code>.
+ To get the list of transports, call the following command:
+</p>
+
+<pre>$ adb shell bmgr list transports</pre>
+
+<h2>Known Issues</h2>
+
+<p>The following are known issues with the automatic backup service:</p>
+
+<ul>
+ <li>For apps that use Google Cloud Messaging for push notifications, there is a known issue where
+ backing up the registration id returned by Google Cloud Messaging registration can break push
+ notifications for the restored application. It is important to query the API for a new
+ registration id after being installed on a new device - which will not be the case if the old
+ registration id was backed up. To avoid this, exclude the registration id from the set of backed
+ up files.
+ </li>
+</ul>
\ No newline at end of file
diff --git a/docs/html/preview/features/runtime-permissions.jd b/docs/html/preview/features/runtime-permissions.jd
new file mode 100644
index 0000000..4a01010
--- /dev/null
+++ b/docs/html/preview/features/runtime-permissions.jd
@@ -0,0 +1,352 @@
+page.title=Android M Preview Runtime Permissions
+
+@jd:body
+
+
+<p>
+ The M Developer Preview introduces a new app permissions model which makes it
+ less frustrating for users to install and upgrade apps. If an app running on
+ M supports the new permissions model, the user does not have to grant any
+ permissions when they install or upgrade the app. Instead, the app requests
+ permissions as they are needed, and the system shows a dialog to the user
+ asking for the permission.
+</p>
+
+<p>
+ If an app supports the new permissions model, it can still be installed and
+ run on devices running older versions of Android, using the old permissions
+ model on those devices.
+</p>
+
+<h2>
+ Overview
+</h2>
+
+<p>
+ If an app's target SDK version is the M developer preview, that indicates
+ that the app uses the new permissions model:
+</p>
+
+<ul>
+ <li>Permissions are divided into <em>permission groups</em>, based on their
+ functionality. For example, all permissions relating to the camera and photo
+ roll are grouped in the Camera permission group,
+ [link]android.permission-group.CAMERA[/link].
+ </li>
+
+ <li>The app declares all the permissions it needs in the manifest, as in
+ earlier Android platforms.
+ </li>
+
+ <li>When the user installs or updates the app, the app is granted just those
+ permissions it requests that fall under <a href=
+ "https://android-preview.googleplex.com/reference/android/content/pm/PermissionInfo.html#PROTECTION_NORMAL">
+ <code>PROTECTION_NORMAL</code></a>, as well as signature and system permissions, as
+ described below. The user is <em>not</em> prompted to grant any permissions
+ at this time.
+ </li>
+
+ <li>When the app needs to perform any action that requires a permission, it
+ first checks whether it has that permission already. If it does not, it
+ requests to be granted that permission.
+ </li>
+
+ <li>When the app requests a permission, the system shows a dialog box to the
+ user, then calls the app's callback function to notify it whether the
+ permission was granted. If a user grants a permission, the app is given all
+ permissions in that permission's functional area that were declared in the
+ app manifest.
+ </li>
+
+ <li>If the app is not granted an appropriate permission, it should handle the
+ failure cleanly. For example, if the permission is just needed for an added
+ feature, the app can disable that feature. If the permission is essential for
+ the app to function, the app might disable all its functionality and inform
+ the user that they need to grant that permission.
+ </li>
+
+ <li>Users can always go to the app's <b>Settings</b> screen and turn on or
+ off any of the app's permissions.
+ <!-- insert screenshot of settings screen-->
+ If a user turns off an app's permissions, the app is
+ <em>not</em> notified.
+ </li>
+</ul>
+
+<h3>
+ System Apps and Signature Permissions
+</h3>
+
+<p>
+ Ordinarily, an app is just granted the <a href=
+ "https://android-preview.googleplex.com/reference/android/content/pm/PermissionInfo.html#PROTECTION_NORMAL">
+ <code>PROTECTION_NORMAL</code></a> permissions when it is installed. However,
+ under some circumstances the app is granted more permissions:
+</p>
+
+<ul>
+ <li>If an app is part of the system image, it is automatically granted all
+ the permissions listed in its manifest.
+ </li>
+
+ <li>Apps are granted all permissions listed in the manifest that fall under
+ <a href=
+ "https://android-preview.googleplex.com/reference/android/content/pm/PermissionInfo.html#PROTECTION_SIGNATURE">
+ PROTECTION_SIGNATURE</a>, if the app's signature matches the signature of
+ the app that declares the permissions.
+ </li>
+</ul>
+
+<p>
+ In both cases, the user can still revoke permissions at any time by going to
+ the app's <b>Settings</b> screen, so the app should continue to check for
+ permissions at run time and request them if necessary.
+</p>
+
+<h3>
+ Forwards and Backwards Compatibility
+</h3>
+
+<p>
+ If an app does not target the M developer preview, it continues to use the
+ old permissions model even on M devices. When the app is installed, the
+ system asks the user to grant all permissions listed in the app's manifest.
+</p>
+
+<p>
+ If an app using the new permissions model is run on a pre-M device, the
+ system treats it the same as any other app. Once again, the system asks the
+ user to grant all declared permissions at install time.
+</p>
+
+<h2 id="">Coding for Runtime Permissions</h2>
+
+<p>
+ If your app targets the new M Developer Preview, you must use the new
+ permissions model. This means that in addition to declaring your needed
+ permissions in the manifest, you must also check to see if you have the
+ permissions at run time, and request the permissions if you do not already
+ have them.
+</p>
+
+<h3>
+ Enabling the New Permissions Model
+</h3>
+
+<p>
+ To enable the new M Developer Preview permissions model, set the app's
+ <a href=
+ "http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#target">
+ targetSdkVersion</a> attribute to "M". Doing this enables all the new
+ permissions features.
+</p>
+
+<!-- TODO: Insert manifest snippet -->
+
+<h3>
+ Designating a Permission for M Only
+</h3>
+
+<p>
+ You can use the new <code><uses-permission-sdk-m></code> element in the
+ app manifest to indicate that a permission is only needed on the M platform.
+ If you declare a permission this way, then whenever the app is installed on
+ an older device, the user is not prompted to grant the permission and the
+ permission is not granted to the app. This allows you to add new permissions
+ to updated versions of your app without forcing users to grant permissions
+ when they install the update.
+</p>
+
+<p>
+ If the app is running on a device with the M developer preview,
+ <code><uses-permission-sdk-m></code> behaves the same as
+ <code><uses-permission></code>. The user is not prompted to grant any
+ permissions when the app is installed, and the app requests permissions as
+ they are needed.
+</p>
+
+<h3 id="prompting">
+ Prompting for Permissions on the M Preview
+</h3>
+
+<p>
+ If your app uses the new M Developer Preview permissions model, the user is
+ not asked to grant all permissions when the app is first launched on a device
+ running the M Preview. Instead, your app requests permissions as they are
+ needed. When your app requests a permission, the system shows a dialog to the
+ user.
+</p>
+
+<p>
+ An app should follow this workflow to request permissions on an Android M
+ device. The device can check what platform it's running on by checking the
+ value of {@link android.os.Build.VERSION#SDK_INT Build.VERSION.SDK_INT}. If
+ the device is running the M Developer Preview, {@link
+ android.os.Build.VERSION#SDK_INT SDK_INT} is 23.
+ <!-- TODO: Confirm this number -->
+</p>
+<ol>
+ <li>When the user tries to do something that requires a permission, the app
+ checks to see if it currently has permission to perform this operation. To do
+ this, the app calls
+ <a href="https://android-preview.googleplex.com/reference/android/content/Context.html#checkSelfPermission(java.lang.String)"><code>Context.CheckSelfPermission(<em>permission_name</em>)</code></a> . The
+ app should do this even if it knows the user has already granted that
+ permission, since the user can revoke an app's permissions at any time. For
+ example, if a user wants to use an app to take a picture, the app calls
+ <a href="https://android-preview.googleplex.com/reference/android/content/Context.html#checkSelfPermission(java.lang.String)"><code>Context.CheckSelfPermission(Manifest.permission.CAMERA)</code></a>.
+ </li>
+
+<!-- TODO: Full list of permissions (or link to that list),
+ and how they break down by functional area]-->
+
+ <li>If the permission is not already granted to the app, the app calls
+ <a href=
+ "https://android-preview.googleplex.com/reference/android/app/Activity.html#requestPermissions(java.lang.String[],%20int)">
+ <code>requestPermissions()</code></a> to request the
+ appropriate permission or permissions. This method functions
+ asynchronously.
+ <!-- TODO: insert code snippet showing permission check and request -->
+ </li>
+
+ <li>The system presents a dialog box to the user.
+ <!-- TODO: insert screenshot of permissions dialog box -->
+ When the user responds, the system calls <a href=
+ "https://android-preview.googleplex.com/reference/android/app/Activity.html#onRequestPermissionsResult(int,%20java.lang.String[],%20int[])">
+ <code>Activity.onRequestPermissionsResult()</code></a> with the
+ results; your app needs to override that method. The callback is passed the
+ same request code you passed to <a href=
+ "https://android-preview.googleplex.com/reference/android/app/Activity.html#requestPermissions(java.lang.String[],%20int)">
+ <code>requestPermissions()</code></a>.
+ <!-- TODO: Insert code snippet of callback method -->
+ </li>
+
+ <li>If the user grants a permission, the app is given all permissions
+ in that functional area that are listed in the app manifest.
+ If the request is denied, you should take appropriate action. For
+ example, you might disable any menu actions that depend on this permission.
+ </li>
+</ul>
+
+<p>
+ When the system asks the user to grant a permission, the user has the option
+ of telling the system not to ask for that permission again. In that case,
+ when an app asks for that permission with <a href=
+ "https://android-preview.googleplex.com/reference/android/app/Activity.html#requestPermissions(java.lang.String[],%20int)">
+ <code>requestPermissions()</code></a>, the
+ system immediately denies the request. For this reason, your app cannot
+ assume that any direct interaction with the user has taken place.
+</p>
+
+<p>
+ If your app runs on a device that has SDK 22 or lower, the app uses the old
+ permissions model. When the user installs the app, they are prompted to grant
+ all the permissions your app requests in its manifest, except for those
+ permissions which are labeled with <code><uses-permission-sdk-m></code>.
+</p>
+
+<h2 id="">Best Practices</h2>
+
+<p>
+ The new permissions model gives users a smoother experience, and makes it
+ easier for them to install apps and feel comfortable with what the apps are
+ doing. We recommend the following best practices to take full advantage of
+ the new model.
+</p>
+
+<h3>
+ Don't Overwhelm the User
+</h3>
+
+<p>
+ If you confront the user with a lot of permissions requests at once, you may
+ overwhelm the user and cause them to quit your app. Instead, you should ask
+ for permissions as you need them.
+</p>
+
+<p>
+ In some cases, one or more permissions might be absolutely essential to your
+ app. In that case, it might make sense to ask for all the permissions as soon
+ as the app launches.
+ <!-- TODO: insert screenshot of dialog box asking for several permissions -->
+ For example, if you make a photography app, the app would
+ need access to the device camera. When the user launches the app for the
+ first time, they won't be surprised to be asked to give permission to use the
+ camera. But if the same app also had a feature to share photos with the
+ user's contacts, you probably should <em>not</em> ask for that permission at
+ first launch. Instead, wait until the user tries to use the "sharing" feature
+ and ask for the permission then.
+</p>
+
+<p>
+ If your app provides a tutorial, it may make sense to request app's essential
+ permissions at the end of the tutorial sequence.
+</p>
+
+<h3>
+ Explain Why You Need Permissions
+</h3>
+
+<p>
+ The permissions screen shown by the system when you call <a href=
+ "https://android-preview.googleplex.com/reference/android/app/Activity.html#requestPermissions(java.lang.String[],%20int)">
+ <code>requestPermissions()</code></a> says what permission your app wants,
+ but doesn't say why you want it. In some cases, the user may find that
+ puzzling. It's a good idea to explain to the user why your app wants the
+ permissions before you call <a href=
+ "https://android-preview.googleplex.com/reference/android/app/Activity.html#requestPermissions(java.lang.String[],%20int)">
+ <code>requestPermissions()</code></a>.
+</p>
+
+<p>
+ For example, a photography app might want to use location services, so it can
+ geotag the photos. A typical user might not understand that a photo can
+ contain location information, and would be puzzled why their photography app
+ wanted to know the location. So in this case, it's a good idea for the app to
+ tell the user about this feature <em>before</em> calling
+ <a href=
+ "https://android-preview.googleplex.com/reference/android/app/Activity.html#requestPermissions(java.lang.String[],%20int)">
+ <code>requestPermissions()</code></a>.
+</p>
+
+<p>
+ As noted, one way to do this is to incorporate these requests into an app
+ tutorial. The tutorial can show each of the app's features in turn, and as it
+ does this, it can explain what permissions are needed. For example, the
+ photography app's tutorial demonstrate its "share photos with your contacts"
+ feature, then tell the user that they'll need to give permission for the app
+ to see the user's contacts, and <em>then</em> call <a href=
+ "https://android-preview.googleplex.com/reference/android/app/Activity.html#requestPermissions(java.lang.String[],%20int)">
+ <code>requestPermissions()</code></a>
+ to get that access. Of course, some users will want to skip the tutorial, so
+ you'll still need to check for and request permissions during the app's
+ normal operation.
+</p>
+
+<h3>
+ Opt Out If Necessary
+</h3>
+
+<p>
+ Until you are ready to use the new permissions model, you can opt out simply
+ by setting your app's <a href=
+ "http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#target">
+ targetSdkVersion</a> to 22 or less. If you do this, the system will use the
+ old permissions model. When the user downloads the app, they will be prompted
+ to grant all the permissions listed in the manifest.
+</p>
+
+<p>
+ With the M Developer Preview, users can turn off permissions for <em>any</em>
+ app from the app's Settings page, regardless of what SDK version the app
+ targets. For this reason, it's a good idea to follow the steps described in
+ <a href="#prompting">"Prompting for Permissions on the M Preview"</a> even if
+ your app doesn't fully support the new permissions model.
+</p>
+
+<p class="note">
+ <strong>Note:</strong> If a user turns off permissions for a legacy app, the system
+ silently disables the appropriate functionality. When the app attempts to
+ perform an operation that requires that permission, the operation will not
+ necessarily cause an exception. Instead, it might return an empty data set or
+ otherwise signal an error.
+</p>
diff --git a/docs/html/preview/images/direct-share-screen.png b/docs/html/preview/images/direct-share-screen.png
new file mode 100644
index 0000000..9e879e0
--- /dev/null
+++ b/docs/html/preview/images/direct-share-screen.png
Binary files differ
diff --git a/docs/html/preview/images/direct-share-screen_2x.png b/docs/html/preview/images/direct-share-screen_2x.png
new file mode 100644
index 0000000..3bbfa7d
--- /dev/null
+++ b/docs/html/preview/images/direct-share-screen_2x.png
Binary files differ
diff --git a/docs/html/preview/images/fingerprint-screen.png b/docs/html/preview/images/fingerprint-screen.png
new file mode 100644
index 0000000..0bb49ef
--- /dev/null
+++ b/docs/html/preview/images/fingerprint-screen.png
Binary files differ
diff --git a/docs/html/preview/images/fingerprint-screen_2x.png b/docs/html/preview/images/fingerprint-screen_2x.png
new file mode 100644
index 0000000..25ce51a
--- /dev/null
+++ b/docs/html/preview/images/fingerprint-screen_2x.png
Binary files differ
diff --git a/docs/html/preview/images/text-selection.gif b/docs/html/preview/images/text-selection.gif
new file mode 100644
index 0000000..1d82fc6
--- /dev/null
+++ b/docs/html/preview/images/text-selection.gif
Binary files differ
diff --git a/docs/html/preview/index.jd b/docs/html/preview/index.jd
index c6c2068..3b5fdbd 100644
--- a/docs/html/preview/index.jd
+++ b/docs/html/preview/index.jd
@@ -22,21 +22,9 @@
Test your apps and give us feedback!
</p>
- <a class="dac-hero-cta" href="{@docRoot}preview/overview.html">
- <span class="dac-sprite dac-auto-chevron"></span>
- Preview Program Overview
- </a><br>
- <a class="dac-hero-cta" href="{@docRoot}preview/api-overview.html">
- <span class="dac-sprite dac-auto-chevron"></span>
- Review the API changes
- </a><br>
<a class="dac-hero-cta" href="{@docRoot}preview/setup-sdk.html">
<span class="dac-sprite dac-auto-chevron"></span>
- Set up the Preview SDK
- </a><br>
- <a class="dac-hero-cta" href="https://code.google.com/p/android-developer-preview/">
- <span class="dac-sprite dac-auto-chevron"></span>
- Report issues
+ Get started
</a><br>
</div>
@@ -46,7 +34,20 @@
<div class="resource-widget resource-flow-layout col-16"
data-query="collection:preview/landing/resources"
data-cardSizes="6x2"
- data-maxResults="6"></div>
+ data-maxResults="3"></div>
</div>
</div>
-</section>
\ No newline at end of file
+</section>
+<div class="wrap dac-offset-parent">
+ <a class="dac-fab dac-scroll-button" data-scroll-button href="#latest">
+ <i class="dac-sprite dac-arrow-down-gray"></i>
+ </a>
+</div>
+
+<section class="dac-section dac-gray dac-small dac-invert" id="latest"><div class="wrap">
+ <h2 class="norule">Latest</h2>
+ <div class="resource-widget resource-flow-layout col-16"
+ data-query="type:blog+tag:featured+tag:preview"
+ data-cardSizes="6x6"
+ data-maxResults="3"></div>
+</div></section>
diff --git a/docs/html/preview/overview.jd b/docs/html/preview/overview.jd
index 0c8931d..2c79eba 100644
--- a/docs/html/preview/overview.jd
+++ b/docs/html/preview/overview.jd
@@ -1,4 +1,5 @@
page.title=Preview Program Overview
+page.image=images/cards/card-preview_16-9_2x.png
@jd:body
diff --git a/docs/html/preview/preview_toc.cs b/docs/html/preview/preview_toc.cs
index fbf73f6..a053718 100644
--- a/docs/html/preview/preview_toc.cs
+++ b/docs/html/preview/preview_toc.cs
@@ -16,9 +16,33 @@
</li>
<li class="nav-section">
+ <div class="nav-section-header empty"><a href="<?cs var:toroot ?>preview/api-changes.html">
+ Behavior Changes</a></div>
+ </li>
+
+ <li class="nav-section">
+ <div class="nav-section-header empty">
+ <a href="<?cs var:toroot ?>preview/features/runtime-permissions.html">
+ Runtime Permissions</a></div>
+ </li>
+
+ <li class="nav-section">
+ <div class="nav-section-header empty">
+ <a href="<?cs var:toroot ?>preview/backup/index.html">
+ Automatic Backups</a></div>
+ </li>
+
+ <li class="nav-section">
+ <div class="nav-section-header empty">
+ <a href="<?cs var:toroot ?>preview/data-binding/guide.html">
+ Data Binding</a></div>
+ </li>
+
+ <li class="nav-section">
<div class="nav-section-header empty"><a href="<?cs var:toroot ?>preview/samples.html">
Samples</a></div>
</li>
+
<li class="nav-section">
<div class="nav-section-header empty"><a href="<?cs var:toroot ?>preview/reference.html">
Reference</a></div>
diff --git a/docs/html/preview/samples.jd b/docs/html/preview/samples.jd
index fb80e30..b3836f8 100644
--- a/docs/html/preview/samples.jd
+++ b/docs/html/preview/samples.jd
@@ -13,25 +13,72 @@
</p>
-<h3 id="id">Sample 1</h3>
+<h3 id="RuntimePermissions">Runtime Permissions</h3>
<p>
- This sample demonstrates how to turducken frankfurter boudin, ham brisket alcatra kielbasa pork
- loin pork. Jowl kielbasa kevin, sausage landjaeger corned beef cow spare ribs pastrami leberkas
- drumstick.
+ Android M changes the way system permissions work. Users are asked to approve permission
+ requests at runtime instead of during installation. This sample shows how to request these
+ permissions.
</p>
-<p><a href="#">Get it on GitHub</a></p>
+<p><a href="https://github.com/googlesamples/android-RuntimePermissions">Get it on GitHub</a></p>
-
-<h3 id="id">Sample 2</h3>
+<h3 id="RuntimePermissionsCompat">Runtime Permissions Compat</h3>
<p>
- This sample demonstrates how to turducken frankfurter boudin, ham brisket alcatra kielbasa pork
- loin pork. Jowl kielbasa kevin, sausage landjaeger corned beef cow spare ribs pastrami leberkas
- drumstick.
+ To support devices on previous versions of Android, this sample demonstrates how to work with the
+ new permissions system introduced in Android M by using a support library.
</p>
-<p><a href="#">Get it on GitHub</a></p>
+<p><a href="https://github.com/googlesamples/android-RuntimePermissionsCompat">Get it on
+GitHub</a></p>
+
+<h3 id="VoiceCamera">Voice Camera</h3>
+
+<p>
+ This sample demonstrates how to implement the "OK Google, take a selfie" voice command and confirm
+ the user intent with the <code>VoiceInteraction</code> API.
+</p>
+
+<p><a href="https://github.com/googlesamples/android-VoiceCamera">Get it on GitHub</a></p>
+
+
+<h3 id="DeepLinkSharing">Deep Link Sharing</h3>
+
+<p>
+ This sample shows how to handle content shared via deep links in your app. It demonstrates how to
+ implement the <code>ChooserTargetService</code>.
+</p>
+
+<p><a href="https://github.com/googlesamples/android-DeepLinkSharing">Get it on GitHub</a></p>
+
+<h3 id="NotificationBuilder">Notification Builder</h3>
+
+<p>
+ This sample demonstrates the improved
+ <a href="{@docRoot}reference/android/app/Notification.html"><code>Notification</code></a>
+ memory footprint and simplified creation of notifications with custom views.
+</p>
+
+<p><a href="https://github.com/googlesamples/android-NotificationBuilder">Get it on GitHub</a></p>
+
+<h3 id="ActiveNotification">Active Notification</h3>
+
+<p>
+ This sample demonstrates how the
+ <a href="{@docRoot}reference/android/app/NotificationManager.html"><code>NotificationManager</code></a>
+ can tell you how many notifications your app is currently showing.
+</p>
+
+<p><a href="https://github.com/googlesamples/android-ActiveNotification">Get it on GitHub</a></p>
+
+<h3 id="VoiceSynthesizer">Voice Synthesizer</h3>
+
+<p>
+ This sample demonstrates how to use the <code>NativeAudio</code> APIs to demonstrate low-latency
+ audio processing.
+</p>
+
+<p><a href="https://github.com/googlesamples/android-VoiceSynthesizer">Get it on GitHub</a></p>
diff --git a/docs/html/preview/setup-sdk.jd b/docs/html/preview/setup-sdk.jd
index 11b009e..0d6c498 100644
--- a/docs/html/preview/setup-sdk.jd
+++ b/docs/html/preview/setup-sdk.jd
@@ -1,4 +1,5 @@
-page.title=Setting Up the Preview SDK
+page.title=Set Up the Preview SDK
+page.image=images/cards/card-set-up_16-9_2x.png
@jd:body
@@ -192,16 +193,18 @@
<h2 id="setupHardware">Set Up Hardware and AVDs</h2>
-<p>The Android M Developer Preview provides you with 32-bit system images
+<p>The Android M Developer Preview provides you with system images
to flash the following devices:
</p>
<ul>
<li>Nexus 5</li>
- <li>Nexus 7 Wi-Fi (version 2, released in 2013)</li>
+ <li>Nexus 6</li>
+ <li>Nexus 9</li>
+ <li>Nexus Player</li>
</ul>
-<p>In addition, you also get the emulator system images, which includes
+<p>In addition, the Preview SDK provides emulator system images, which include
experimental 64-bit system images along with standard 32-bit system images.
</p>
@@ -213,37 +216,54 @@
<h3 id="installImage">Install the M Preview System Image</h3>
-<p class="warning"><b>Warning:</b> This is a preview version of the Android
-system image, and is subject to change. Your use of this system image is
+<p class="warning"><b>Warning:</b> The following Android system images are
+previews and are subject to change. Your use of these system images is
governed by the Android SDK Preview License Agreement. The Android preview
-system image is not a stable release, and may contain errors and defects that
+system images are not stable releases, and may contain errors and defects that
can result in damage to your computer systems, devices, and data. The preview
-Android system image is not subject to the same testing as the factory OS and
+Android system images are not subject to the same testing as the factory OS and
can cause your phone and installed services and applications to stop working.
</p>
<ol>
<li>Download and uncompress the Android Developer Preview package.
- <table style="width:860px">
+ <table>
<tr>
<th scope="col">Device</th>
- <th scope="col">Download</th>
- <th scope="col">Checksum</th>
+ <th scope="col">Download / Checksums</th>
</tr>
<tr id="hammerhead">
<td>Nexus 5 (GSM/LTE) <br>"hammerhead"</td>
<td><a href="#top" onclick="onDownload(this)"
- >hammerhead-lpv79-preview-ac1d8a8e.tgz</a></td>
- <td>MD5: <code>5a6ae77217978cb7b958a240c2e80b57</code>
- <br>SHA-1: <code>ac1d8a8e4f4a1dca5864dc733caa940bffc28616</code></td>
+ >hammerhead-mpv79-preview-ac1d8a8e.tgz</a><br>
+ MD5: 5a6ae77217978cb7b958a240c2e80b57<br>
+ SHA-1: ac1d8a8e4f4a1dca5864dc733caa940bffc28616
+ </td>
</tr>
- <tr id="razor">
- <td>Nexus 7 (Wifi) <br>"razor"</td>
+ <tr id="shamu">
+ <td>Nexus 6 <br>"shamu"</td>
<td><a href="#top" onclick="onDownload(this)"
- >razor-lpv79-preview-d0ddf8ce.tgz</a></td>
- <td>MD5: <code>b293a5d3a4e07beabebcc0be85ad68a2</code>
- <br><nobr>SHA-1: <code>d0ddf8ce733ba2a34279cdff8827fd604762c2342d</nobr></td>
+ >shamu-mpv79-preview-ac1d8a8e.tgz</a><br>
+ MD5: 5a6ae77217978cb7b958a240c2e80b57<br>
+ SHA-1: ac1d8a8e4f4a1dca5864dc733caa940bffc28616
+ </td>
+ </tr>
+ <tr id="volantis">
+ <td>Nexus 9 <br>"volantis"</td>
+ <td><a href="#top" onclick="onDownload(this)"
+ >volantis-mpv79-preview-ac1d8a8e.tgz</a><br>
+ MD5: 5a6ae77217978cb7b958a240c2e80b57<br>
+ SHA-1: ac1d8a8e4f4a1dca5864dc733caa940bffc28616
+ </td>
+ </tr>
+ <tr id="fugu">
+ <td>Nexus Player <br>"fugu"</td>
+ <td><a href="#top" onclick="onDownload(this)"
+ >fugu-mpv79-preview-d0ddf8ce.tgz</a><br>
+ MD5: b293a5d3a4e07beabebcc0be85ad68a2<br>
+ SHA-1: d0ddf8ce733ba2a34279cdff8827fd604762c2342d
+ </td>
</tr>
</table>
</li>
@@ -278,11 +298,10 @@
<a href="{@docRoot}tools/devices/managing-avds.html">Managing AVDs with AVD
Manager</a>. Use the following settings:
<ul>
- <li><b>Device:</b> Either Nexus 5 or Nexus 7</li>
- <li><b>Target:</b> <!-- Confirm exact text when we have final distro -->
+ <li><b>Device:</b> Nexus 5, Nexus 6, Nexus 9, or Nexus Player</li>
+ <li><b>Target:</b>
Android M (Preview) - API Level M</li>
</ul>
- <!-- Confirm this works when you can download image through SDK manager! -->
</li>
</ol>
diff --git a/docs/html/preview/support.jd b/docs/html/preview/support.jd
index 4be6dd7..3ed1487 100644
--- a/docs/html/preview/support.jd
+++ b/docs/html/preview/support.jd
@@ -1,4 +1,5 @@
page.title=Support
+page.image=images/cards/card-support_16-9_2x.png
@jd:body
diff --git a/docs/html/tools/building/building-cmdline.jd b/docs/html/tools/building/building-cmdline.jd
index 33798a5..0e4c8b2 100644
--- a/docs/html/tools/building/building-cmdline.jd
+++ b/docs/html/tools/building/building-cmdline.jd
@@ -43,7 +43,7 @@
<p>Whether you're building with the debug or release build type, you need to run
and build your module. This will create the .apk file that you can install on an emulator or device.
When you build using the debug build type, the .apk file is automatically signed by the SDK tools
- with a debug key based on the <code>debuggable true</code> setting in the module's gradle.build file,
+ with a debug key based on the <code>debuggable true</code> setting in the module's build.gradle file,
so it's instantly ready for installation onto an emulator or attached
development device. You cannot distribute an application that is signed with a debug key.
When you build using the release build type, the .apk file is <em>unsigned</em>, so you
@@ -174,7 +174,7 @@
the build will prompt you for your keystore and alias password when you build using the release
build type and produce your final application package, which will be ready for distribution.</p>
- <p>To specify your keystore and alias, open the module gradle.build file (found in
+ <p>To specify your keystore and alias, open the module build.gradle file (found in
the root of the module directory) and add entries for {@code storeFile}, {@code storePassword},
{@code keyAlias} and {@code keyPassword}.
For example:</p>
@@ -188,7 +188,7 @@
<ol>
<li>Open a command-line and navigate to the root of your module directory.</li>
- <li>Edit the gradle.build file to build your project in release mode:
+ <li>Edit the build.gradle file to build your project in release mode:
<p><pre>
...
android {
@@ -222,7 +222,7 @@
<p>This creates your Android application .apk file inside the module <code>build/</code>
directory, named <code><em><your_module_name></em>-release.apk</code>. This .apk file has
- been signed with the private key specified in gradle.build file and aligned with {@code
+ been signed with the private key specified in build.gradle file and aligned with {@code
zipalign}. It's ready for installation and distribution.</p>
<h3 id="OnceBuilt">Once built and signed in release mode</h3>
diff --git a/docs/html/tools/devices/index.jd b/docs/html/tools/devices/index.jd
index 1ea4c47..6263c8b 100644
--- a/docs/html/tools/devices/index.jd
+++ b/docs/html/tools/devices/index.jd
@@ -5,16 +5,16 @@
<p>An Android Virtual Device (AVD) is an emulator configuration that lets you model an actual
device by defining hardware and software options to be emulated by the Android Emulator.</p>
- <p>The easiest way to create an AVD is to use the graphical <a href=
- "{@docRoot}tools/devices/managing-avds.html">AVD Manager</a>, which you launch
- from Eclipse by clicking <strong>Window > AVD Manager</strong>. You can also start the AVD
-Manager from the command line by calling the <code>android</code> tool with the <code>avd</code>
-options, from the <strong><sdk>/tools/</strong> directory.</p>
+ <p>The easiest way to create an AVD is to use the graphical
+ <a href="{@docRoot}tools/devices/managing-avds.html">AVD Manager</a>, which you launch
+ from Android Studio by clicking <strong>Tools > Android > AVD Manager</strong>. You can
+ also start the AVD Manager from the command line by calling the <code>android</code> tool with
+ the <code>avd</code> options, from the <strong><sdk>/tools/</strong> directory.</p>
<p>You can also create AVDs on the command line by passing the <code>android</code> tool options.
- For more information on how to create AVDs in this manner, see <a href=
- "{@docRoot}tools/devices/managing-avds-cmdline.html">Managing Virtual
- Devices from the Command Line</a>.</p>
+ For more information on how to create AVDs in this manner, see
+ <a href="{@docRoot}tools/devices/managing-avds-cmdline.html">Managing Virtual Devices from the
+ Command Line</a>.</p>
<p>An AVD consists of:</p>
diff --git a/docs/html/tools/devices/managing-avds-cmdline.jd b/docs/html/tools/devices/managing-avds-cmdline.jd
index ba353c1..c16b1f8 100644
--- a/docs/html/tools/devices/managing-avds-cmdline.jd
+++ b/docs/html/tools/devices/managing-avds-cmdline.jd
@@ -84,8 +84,8 @@
<h2 id="AVDCmdLine">Creating AVDs</h2>
-<p>In addition to creating AVDs with the
-<a href="{@docRoot}tools/devices/managing-avds-cmdline.html">AVD Manager user interface</a>,
+<p>In addition to creating AVDs with the
+<a href="{@docRoot}tools/help/avd-manager.html">AVD Manager user interface</a>,
you can also create them by passing in command line arguments to the <code>android</code> tool.
</p>
diff --git a/docs/html/training/building-location.jd b/docs/html/training/building-location.jd
new file mode 100644
index 0000000..9ab0908
--- /dev/null
+++ b/docs/html/training/building-location.jd
@@ -0,0 +1,11 @@
+page.title=Building Apps with Location & Maps
+page.trainingcourse=true
+
+@jd:body
+
+
+<p>
+ These classes teach you how to add user location and mapping information to your app.
+ Make your app more helpful and relevant to users by providing information about where
+ they are and the world around them.
+</p>
\ No newline at end of file
diff --git a/docs/html/training/maps/index.jd b/docs/html/training/maps/index.jd
new file mode 100644
index 0000000..3efa493
--- /dev/null
+++ b/docs/html/training/maps/index.jd
@@ -0,0 +1,70 @@
+page.title=Adding Maps
+page.tags=mapview,location
+page.article=true
+page.trainingcourse=true
+@jd:body
+
+
+<img src="{@docRoot}images/google/gps-maps.png"
+ width="300"
+ style="float:right;margin:0 0 20px 20px"
+ alt="Google maps sample image">
+
+<p>
+ Allow your users to explore the world with rich maps provided by Google. Identify
+ locations with custom markers, augment the map data with image overlays, embed one
+ or more maps as fragments, and much more.
+</p>
+
+<p>
+ The <a href="https://developers.google.com/maps/documentation/android/">Google
+ Maps Android API</a> allows you to include maps and customized mapping information
+ in your app.
+</p>
+
+
+<h2 id="features">Key Developer Features</h2>
+
+<h4>Add maps to your app</h4>
+<p>
+ With Google Maps Android API v2, you can embed maps into an activity as a fragment with a simple
+ XML snippet. The new Maps offer exciting features such as 3D maps; indoor, satellite, terrain,
+ and hybrid maps; vector-based tiles for efficient caching and drawing; animated transitions; and
+ much more. <a class="external-link" href=
+ "https://developers.google.com/maps/documentation/android/map">Add a map object</a>.
+</p>
+
+<h4>Customize the map</h4>
+<p>
+ Add markers onto the map to indicate special points of interest for your users. You can define
+ custom colors or icons for your map markers to match your app's look and feel. To further enhance
+ the app, draw polylines and polygons to indicate paths or regions, or provide complete image
+ overlays. <a class="external-link" href=
+ "https://developers.google.com/maps/documentation/android/marker">Draw markers</a>.
+</p>
+
+<h4>Control the user's view</h4>
+<p>
+ Give your users a different view of the world with the ability to control the rotation, tilt,
+ zoom, and pan properties of the "camera" perspective of the map. <a class="external-link" href=
+ "https://developers.google.com/maps/documentation/android/views">Change the view</a>.
+</p>
+
+<h4>Add Street View to your app</h4>
+<p>
+ Embed Street View into an activity and let your users explore the world through panoramic
+ 360-degree views. Programmatically control the zoom and orientation (tilt and bearing) of the
+ Street View camera, and animate the camera movements over a given duration. <a class=
+ "external-link" href="https://developers.google.com/maps/documentation/android/streetview">Add
+ Street View</a>.
+</p>
+
+
+<h2 id="start">Get Started</h2>
+<p>
+ Google Maps Android API is part of the Google Play services platform. To use Google Maps,
+ set up the Google Play services SDK in your app development project. For more information,
+ see the <a class="external-link"
+ href="https://developers.google.com/maps/documentation/android/start">Getting Started</a> guide
+ for the Google Maps Android API.
+</p>
diff --git a/docs/html/training/monetization/ads-and-ux.jd b/docs/html/training/monetization/ads-and-ux.jd
deleted file mode 100644
index d3ab676..0000000
--- a/docs/html/training/monetization/ads-and-ux.jd
+++ /dev/null
@@ -1,250 +0,0 @@
-page.title=Advertising without Compromising User Experience
-parent.title=Monetizing Your App
-parent.link=index.html
-@jd:body
-
-
-<!-- This is the training bar -->
-<div id="tb-wrapper">
-<div id="tb">
-
-<h2>This lesson teaches you to</h2>
-<ol>
- <li><a href="#ObtainPubAccountAndSDK">Obtain a Publisher Account and Ad SDK</a></li>
- <li><a href="#DeclarePermissions">Declare Proper Permissions</a></li>
- <li><a href="#SetupAdPlacement">Set Up Ad Placement</a></li>
- <li><a href="#InitializeAd">Initialize the Ad</a></li>
- <li><a href="#EnableTestMode">Enable Test Mode</a></li>
- <li><a href="#ImplementListeners">Implement Ad Event Listeners</a></li>
-</ol>
-
-<h2>You should also read</h2>
-<ul>
- <li><a href="http://code.google.com/mobile/ads/">AdMob SDK</a></li>
-</ul>
-
-
-<h2>Try it out</h2>
-
-<div class="download-box">
- <a href="http://developer.android.com/shareables/training/MobileAds.zip" class="button">Download
-the sample app</a>
- <p class="filename">MobileAds.zip</p>
-</div>
-
-
-</div>
-</div>
-
-<p>Advertising is one of the means to monetize (make money with) mobile applications. In this
-lesson, you are going to learn how to incorporate banner ads in your Android application.</p>
-
-<p>While this lesson and the sample application use <a
-href="http://code.google.com/mobile/ads/">AdMob</a> to serve ads, the Android platform doesn’t
-impose any restrictions on the choice of mobile advertising network. To the extent possible, this
-lesson generically highlights concepts that are similar across advertising networks.</p>
-
-<p>For example, each advertising network may have some network-specific configuration settings such
-as geo-targeting and ad-text font size, which may be configurable on some networks but not on
-others. This lesson does not touch not these topics in depth and you should consult documentation
-provided by the network you choose.</p>
-
-
-<h2 id="ObtainPubAccountAndSDK">Obtain a Publisher Account and Ad SDK</h2>
-
-<p>In order to integrate advertisements in your application, you first must become a publisher by
-registering a publishing account with the mobile advertising network. Typically, an identifier is
-provisioned for each application serving advertisements. This is how the advertising network
-correlates advertisements served in applications. In the case of AdMob, the identifier is known as
-the Publisher ID. You should consult your advertising networks for details.</p>
-
-<p>Mobile advertising networks typically distribute a specific Android SDK, which consists of code
-that takes care of communication, ad refresh, look-and-feel customization, and so on.</p>
-
-<p>Most advertising networks distribute their SDK as a JAR file. Setting up ad network JAR file in
-your Android project is no different from integrating any third-party JAR files. First, copy the
-JAR files to the <code>libs/</code> directory of your project. If you’re using Eclipse as IDE, be
-sure to add the JAR file to the Build Path. It can be done through <b>Properties >
-Java Build Path > Libraries > Add JARs</b>.</p>
-
-<img src="/images/training/ads-eclipse-build-path.png" id="figure1" />
-<p class="img-caption">
- <strong>Figure 1.</strong> Eclipse build path settings.
-</p>
-
-
-<h2 id="DeclarePermissions">Declare Proper Permissions</h2>
-
-<p>Because the mobile ads are fetched over the network, mobile advertising SDKs usually
-require the declaration of related permissions in the Android manifest. Other kinds of permissions
-may also be required.</p>
-
-<p>For example, here's how you can request the {@link android.Manifest.permission#INTERNET}
-permission:</p>
-
-<pre>
-</manifest>
- <uses-permission android:name="android.permission.INTERNET" />
- ...
- <application>...</application>
-</manifest>
-</pre>
-
-
-<h2 id="SetupAdPlacement">Set Up Ad Placement</h2>
-
-<div class="figure" style="width:262px">
-<img src="/images/training/ads-top-banner.png" id="figure2" />
-<p class="img-caption">
- <strong>Figure 2.</strong> Screenshot of the ad layout in the Mobile Ads sample.
-</p>
-</div>
-
-<p>Banner ads typically are implemented as a custom {@link android.webkit.WebView} (a view for
-viewing web pages). Ads also come in different dimensions and shapes. Once you’ve decided to put an
-ad on a particular screen, you can add it in your activity's XML layout. The XML snippet below
-illustrates a banner ad displayed on top of a screen.</p>
-
-<pre>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/ad_catalog_layout"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <com.google.ads.AdView
- xmlns:googleads="http://schemas.android.com/apk/lib/com.google.ads"
- android:id="@+id/ad"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- googleads:adSize="BANNER"
- googleads:adUnitId="@string/admob_id" />
- <TextView android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/banner_top" />
- <TextView android:id="@+id/status"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-</LinearLayout>
-</pre>
-
-<p>You should consider using alternative ad sizes based on various configurations such as screen
-size or screen orientation. This can easily be addressed by <a
-href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">providing
-alternative resources</a>. For instance, the above sample layout might placed under the
-<code>res/layout/</code> directory as the default layout. If larger ad
-sizes are available, you can consider using them for "large" (and above) screens. For example, the
-following snippet comes from a layout file in the <code>res/layout-large/</code> directory, which
-renders a larger ad for "large" screen sizes.</p>
-
-<pre>
-...
-<com.google.ads.AdView
- xmlns:googleads="http://schemas.android.com/apk/lib/com.google.ads"
- android:id="@+id/ad"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- <strong>googleads:adSize="IAB_LEADERBOARD"</strong>
- googleads:adUnitId="@string/admob_id" />
-...
-</pre>
-
-<p>Notice that the custom view name and it’s configuration attributes are network-specific. Ad
-networks might support configurations with XML layout attributes (as shown above), runtime APIs, or
-both. In the sample application, Mobile Ads, the {@code AdView} ad size
-(<code>googleads:adSize</code>) and publisher ID (<code>googleads:adUnitId</code>) are set up in the
-XML layout.</p>
-
-<p>When deciding where to place ads within your application, you should carefully
-consider user-experience. For example, you don’t want to fill the screen with
-multiple ads that will quite likely annoy your users. In fact, this practice is banned by some ad
-networks. Also, avoid placing ads too closely to UI controls to avoid inadvertent clicks.</p>
-
-<p>Figures 3 and 4 illustrate what <strong>not</strong> to do.</p>
-
-<div style="float:left;width:275px">
-<img src="/images/training/ads-close-to-button.png" />
-<p class="img-caption">
- <strong>Figure 3.</strong> Avoid putting UI
-inputs too closely to an ad banner to prevent inadvertent ad clicks.
-</p>
-</div>
-
-<div style="float:left;width:275px;height:530px;margin-left:2em">
-<img src="/images/training/ads-cover-content.png" />
-<p class="img-caption">
- <strong>Figure 4.</strong> Don't overlay ad banner on useful content.
-</p>
-</div>
-
-
-<h2 id="InitializeAd" style="clear:left">Initialize the Ad</h2>
-
-<p>After setting up the ad in the XML layout, you can further customize the ad in {@link
-android.app.Activity#onCreate Activity.onCreate()} or {@link
-android.app.Fragment#onCreateView Fragment.onCreateView()} based on how your application is
-architected. Depending on the ad network, possible configuration parameters are: ad size, font
-color, keyword, demographics, location targeting, and so on.</p>
-
-<p>It is important to respect user privacy if certain parameters, such as demographics or location,
-are passed to ad networks for targeting purposes. Let your users know and give them a chance to opt
-out of these features.</p>
-
-<p>In the below code snippet, keyword targeting is used. After the keywords are set, the
-application calls <code>loadAd()</code> to begin serving ads.</p>
-
-<pre>
-public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- ...
- View v = inflater.inflate(R.layout.main, container, false);
- mAdStatus = (TextView) v.findViewById(R.id.status);
- mAdView = (AdView) v.findViewById(R.id.ad);
- mAdView.setAdListener(new MyAdListener());
-
- AdRequest adRequest = new AdRequest();
- adRequest.addKeyword("sporting goods");
- mAdView.loadAd(adRequest);
- return v;
-}
-</pre>
-
-
-
-<h2 id="EnableTestMode">Enable Test Mode</h2>
-
-<p>Some ad networks provide a test mode. This is useful during development and testing in which ad
-impressions and clicks are not counted.</p>
-
-<p class="caution"><strong>Important:</strong> Be sure to turn off test mode before publishing your
-application.</p>
-
-
-<h2 id="ImplementListeners">Implement Ad Event Listeners</h2>
-
-<p>Where available, you should consider implementing ad event listeners, which provide callbacks on
-various ad-serving events associated with the ad view. Depending on the ad network, the listener
-might provide notifications on events such as before the ad is loaded, after the ad is loaded,
-whether the ad fails to load, or other events. You can choose to react to these events based on
-your specific situation. For example, if the ad fails to load, you can display a custom banner
-within the application or create a layout such that the rest of content fills up the screen.</p>
-
-<p>For example, here are some event callbacks available from AdMob's {@code AdListener}
-interface:</p>
-
-<pre>
-private class MyAdListener implements AdListener {
- ...
-
- @Override
- public void onFailedToReceiveAd(Ad ad, ErrorCode errorCode) {
- mAdStatus.setText(R.string.error_receive_ad);
- }
-
- @Override
- public void onReceiveAd(Ad ad) {
- mAdStatus.setText("");
- }
-}
-</pre>
-
diff --git a/docs/html/training/monetization/index.jd b/docs/html/training/monetization/index.jd
deleted file mode 100644
index f90bfc7..0000000
--- a/docs/html/training/monetization/index.jd
+++ /dev/null
@@ -1,44 +0,0 @@
-page.title=Monetizing Your App
-
-trainingnavtop=true
-startpage=true
-next.title=Advertising without Compromising User Experience
-next.link=ads-and-ux.html
-
-@jd:body
-
-<div id="tb-wrapper">
-<div id="tb">
-
-<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
-<h2>Dependencies and prerequisites</h2>
-<ul>
- <li>Android 1.0 or higher</li>
- <li>Experience with <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML layouts</a></li>
-</ul>
-
-
-<h2>Try it out</h2>
-
-<div class="download-box">
- <a href="http://developer.android.com/shareables/training/MobileAds.zip" class="button">Download
-the sample app</a>
- <p class="filename">MobileAds.zip</p>
-</div>
-
-</div>
-</div>
-
-<p>Apart from offering paid apps, there are a number of other ways to monetize your mobile applications. In this class, we are going to examine a number of typical methods (more lessons are to come) and their associated technical best practices. Obviously, each application is different and you should experiment with different combinations of these and other monetization methods to determine what works best for you.</p>
-
-<h2>Lessons</h2>
-
-<!-- Create a list of the lessons in this class along with a short description of each lesson.
-These should be short and to the point. It should be clear from reading the summary whether someone
-will want to jump to a lesson or not.-->
-
-<dl>
- <dt><b><a href="ads-and-ux.html">Advertising without Compromising User Experience</a></b></dt>
- <dd>In this lesson, you will learn how to monetize your application with mobile
-advertisements.</dd>
-</dl>
diff --git a/docs/html/google/play/safetynet/start.jd b/docs/html/training/safetynet/index.jd
similarity index 95%
rename from docs/html/google/play/safetynet/start.jd
rename to docs/html/training/safetynet/index.jd
index 8307928..6090f41 100644
--- a/docs/html/google/play/safetynet/start.jd
+++ b/docs/html/training/safetynet/index.jd
@@ -1,11 +1,10 @@
-page.title=Getting Started with SafetyNet
-parent.title=SafetyNet for Android
-parent.link=index.html
+page.title=Checking Device Compatibility with SafetyNet
+
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>In this document</h2>
<ol>
@@ -68,7 +67,9 @@
You acknowledge and understand that the SafetyNet API works by collecting hardware and software
information, such as device and application data and the results of integrity checks, and sending
that data to Google for analysis. Pursuant to Section 3(d) of the
-<a href= "https://developers.google.com/terms/">Google APIs Terms of Service</a>, you agree that if you use the APIs that it is your responsibility to provide any necessary notices or consents for the collection and sharing of this data with Google.
+<a href= "https://developers.google.com/terms/">Google APIs Terms of Service</a>, you agree that if
+you use the APIs that it is your responsibility to provide any necessary notices or consents for the
+collection and sharing of this data with Google.
</div>
<h2 id="connect-play">
@@ -256,9 +257,9 @@
<a href=
"{@docRoot}reference/com/google/android/gms/safetynet/SafetyNetApi.AttestationResult.html#getJwsResult()">
{@code getJwsResult()}</a> method is null or contains an {@code error:} field, then communication
- with the service failed and should be retried. You should use an <a href=
- "{@docRoot}google/gcm/gcm.html#retry">exponential backoff</a> technique for
- retries, to avoid flooding the service with additional requests.
+ with the service failed and should be retried. You should use an <a class="external-link" href=
+ "https://developers.google.com/api-client-library/java/google-http-java-client/backoff">
+ exponential backoff</a> technique for retries, to avoid flooding the service with additional requests.
</p>
<h3 id="verify-compat-check">
@@ -362,7 +363,7 @@
</li>
</ol>
-<p>
+<p class="note">
<strong>Important:</strong> This use of the Android Device Verification API only validates that the
provided JWS message was received from the SafetyNet service. It <em>does not</em> verify that the
payload data matches your original compatibility check request.
diff --git a/docs/html/training/sign-in/index.jd b/docs/html/training/sign-in/index.jd
new file mode 100644
index 0000000..9d49fd9
--- /dev/null
+++ b/docs/html/training/sign-in/index.jd
@@ -0,0 +1,68 @@
+page.title=Adding Sign-In
+page.tags=authentication,signin,social,google+
+page.article=true
+page.trainingcourse=true
+@jd:body
+
+
+<img src="{@docRoot}images/google/gps-googleplus.png"
+ width="300"
+ style="float:right;margin:0 0 20px 20px"
+ alt="Google maps sample image">
+
+<p>
+ The Google+ platform for Android lets you authenticate a user with the same credentials they use
+ on Google every day. Once a user signs in with Google, you can create more engaging experiences
+ and drive usage of your app.
+</p>
+
+<p>
+ The <a href="https://developers.google.com/+/mobile/android/">Google+ Android API</a> allows
+ you to integrate sign-in and social features into your app.
+</p>
+
+
+<h2 id="features">Key Developer Features</h2>
+
+<h4>Trusted authentication</h4>
+<p>
+ Google+ Sign-In is a simple, trusted, and secure way to let people sign in to your app with their
+ Google credentials and bring along their Google+ info.<br>
+ <a href="https://developers.google.com/+/mobile/android/sign-in" class="external-link">Add
+ sign-in</a>.
+</p>
+
+<h4>Access the profile and social graph</h4>
+<p>
+ Once users have signed in with Google, your app can welcome them by name, display their picture,
+ connect them with friends, and lots more.<br>
+ <a href="https://developers.google.com/+/mobile/android/people" class="external-link">Access the
+ social graph</a>.
+</p>
+
+<h4>Stand out in the stream</h4>
+<p>
+ Interactive posts is a rich way of sharing to Google+. It lets users prompt friends to take
+ specific actions in your app from a Google+ post, like "listen," "RSVP," "check-in," and over 100
+ more actions.<br>
+ <a class="external-link" href="https://developers.google.com/+/mobile/android/share">Post
+ interactive content</a>.
+</p>
+
+<h4>Recommend content</h4>
+<p>
+ Add a native +1 button so users can recommend content from your app. These endorsements can give
+ your app more credibility and help it grow faster.<br>
+ <a class="external-link" href="https://developers.google.com/+/mobile/android/recommend">Add the
+ +1 button</a>.
+</p>
+
+
+<h2 id="start">Get Started</h2>
+<p>
+ The Google+ Android APIs are part of the Google Play services platform. To use Google+ features,
+ set up the Google Play services SDK in your app development project. For more information, see
+ the <a class="external-link" href=
+ "https://developers.google.com/+/mobile/android/getting-started">Getting Started</a> guide for
+ the Google+ Platform for Android
+</p>
\ No newline at end of file
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 089739b..7adc45a 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -672,12 +672,59 @@
<!-- End connectivity and cloud -->
+ <li class="nav-section">
+ <div class="nav-section-header">
+ <a href="<?cs var:toroot ?>training/building-location.html">
+ <span class="small">Building Apps with</span><br/>
+ Location & Maps
+ </a>
+ </div>
+ <ul>
+ <li class="nav-section">
+ <div class="nav-section-header">
+ <a href="<?cs var:toroot ?>training/location/index.html"
+ description="How to add location-aware features to your app by getting the user's current location.">
+ Making Your App Location-Aware
+ </a>
+ </div>
+ <ul>
+ <li>
+ <a href="<?cs var:toroot ?>training/location/retrieve-current.html">
+ Getting the Last Known Location
+ </a>
+ </li>
+ <li>
+ <a href="<?cs var:toroot ?>training/location/receive-location-updates.html">
+ Receiving Location Updates
+ </a>
+ </li>
+ <li>
+ <a href="<?cs var:toroot ?>training/location/display-address.html">
+ Displaying a Location Address
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/location/geofencing.html">
+ Creating and Monitoring Geofences
+ </a>
+ </li>
+ </ul>
+ </li>
+ <li class="nav-section">
+ <a href="<?cs var:toroot ?>training/maps/index.html"
+ description="How to add maps and mapping information to your app.">
+ Adding Maps
+ </a>
+ </li>
+ </ul>
+ </li>
+ <!-- End location and maps -->
+
<li class="nav-section">
<div class="nav-section-header">
<a href="<?cs var:toroot ?>training/building-userinfo.html">
<span class="small">Building Apps with</span><br/>
- User Info & Location
+ User Info & Sign-In
</a>
</div>
<ul>
@@ -712,39 +759,15 @@
</li>
</ul>
</li>
-
<li class="nav-section">
- <div class="nav-section-header">
- <a href="<?cs var:toroot ?>training/location/index.html"
- description="How to add location-aware features to your app by getting the user's current location.">
- Making Your App Location-Aware
- </a>
- </div>
- <ul>
- <li>
- <a href="<?cs var:toroot ?>training/location/retrieve-current.html">
- Getting the Last Known Location
- </a>
- </li>
- <li>
- <a href="<?cs var:toroot ?>training/location/receive-location-updates.html">
- Receiving Location Updates
- </a>
- </li>
- <li>
- <a href="<?cs var:toroot ?>training/location/display-address.html">
- Displaying a Location Address
- </a>
- </li>
- <li><a href="<?cs var:toroot ?>training/location/geofencing.html">
- Creating and Monitoring Geofences
- </a>
- </li>
- </ul>
+ <a href="<?cs var:toroot ?>training/sign-in/index.html"
+ description="How to add user sign-in functionality to your app.">
+ Adding Sign-In
+ </a>
</li>
</ul>
</li>
- <!-- End privacy and location -->
+ <!-- End user info and sign-in -->
<li class="nav-section">
@@ -1802,19 +1825,18 @@
>Updating Your Security Provider to Protect Against SSL Exploits</a>
</li>
- <li class="nav-section">
- <div class="nav-section-header">
- <a href="<?cs var:toroot ?>training/enterprise/index.html"
- description=
- "How to implement device management policies for enterprise-oriented apps."
- >Developing for Enterprise</a>
- </div>
- <ul>
- <li><a href="<?cs var:toroot ?>training/enterprise/device-management-policy.html">
- Enhancing Security with Device Management Policies
- </a>
- </li>
- </ul>
+ <li>
+ <a href="<?cs var:toroot ?>training/safetynet/index.html"
+ description=
+ "How to use the SafetyNet service to analyze a device where your app is running
+ and get information about its compatibility with your app."
+ >Checking Device Compatibility with SafetyNet</a>
+ </li>
+
+ <li>
+ <a href="<?cs var:toroot ?>training/enterprise/device-management-policy.html"
+ description="How to create an application that enforces security policies on devices."
+ >Enhancing Security with Device Management Policies</a>
</li>
</ul>
</li>
@@ -1956,21 +1978,6 @@
</li>
</ul>
</li>
- <li class="nav-section">
- <div class="nav-section-header">
- <a href="<?cs var:toroot ?>training/monetization/index.html"
- description=
- "How to implement monetization strategies for your app without compromising
- the user experience."
- >Monetizing Your App</a>
- </div>
- <ul>
- <li><a href="<?cs var:toroot ?>training/monetization/ads-and-ux.html">
- Advertising without Compromising User Experience
- </a>
- </li>
- </ul>
- </li>
</ul>
</li>
<!-- End best Publishing -->
diff --git a/docs/html/wear/images/partners/tagheuer.png b/docs/html/wear/images/partners/tagheuer.png
new file mode 100644
index 0000000..507f90c
--- /dev/null
+++ b/docs/html/wear/images/partners/tagheuer.png
Binary files differ
diff --git a/docs/html/wear/index.jd b/docs/html/wear/index.jd
index 74e4939..e18ada4 100644
--- a/docs/html/wear/index.jd
+++ b/docs/html/wear/index.jd
@@ -246,6 +246,9 @@
<div class="col-4">
<img src="/wear/images/partners/sony.png" alt="Sony">
</div>
+ <div class="col-4">
+ <img src="/wear/images/partners/tagheuer.png" alt="Tag Heuer">
+ </div>
</div>
</div> <!-- end .wrap -->
</div>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 2d8b0b2..a999b71 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -558,6 +558,22 @@
}
/**
+ * Creates a new immutable bitmap backed by ashmem which can efficiently
+ * be passed between processes.
+ *
+ * @hide
+ */
+ public Bitmap createAshmemBitmap() {
+ checkRecycled("Can't copy a recycled bitmap");
+ Bitmap b = nativeCopyAshmem(mFinalizer.mNativeBitmap);
+ if (b != null) {
+ b.setPremultiplied(mRequestPremultiplied);
+ b.mDensity = mDensity;
+ }
+ return b;
+ }
+
+ /**
* Creates a new bitmap, scaled from an existing bitmap, when possible. If the
* specified width and height are the same as the current width and height of
* the source bitmap, the source bitmap is returned and no new bitmap is
@@ -1636,6 +1652,7 @@
int nativeConfig, boolean mutable);
private static native Bitmap nativeCopy(long nativeSrcBitmap, int nativeConfig,
boolean isMutable);
+ private static native Bitmap nativeCopyAshmem(long nativeSrcBitmap);
private static native void nativeDestructor(long nativeBitmap);
private static native boolean nativeRecycle(long nativeBitmap);
private static native void nativeReconfigure(long nativeBitmap, int width, int height,
diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java
index 0412e35..5ba2f93 100644
--- a/graphics/java/android/graphics/drawable/RippleComponent.java
+++ b/graphics/java/android/graphics/drawable/RippleComponent.java
@@ -57,14 +57,19 @@
mBounds = bounds;
}
+ public void onBoundsChange() {
+ if (!mHasMaxRadius) {
+ mTargetRadius = getTargetRadius(mBounds);
+ onTargetRadiusChanged(mTargetRadius);
+ }
+ }
+
public final void setup(float maxRadius, float density) {
if (maxRadius >= 0) {
mHasMaxRadius = true;
mTargetRadius = maxRadius;
} else {
- final float halfWidth = mBounds.width() / 2.0f;
- final float halfHeight = mBounds.height() / 2.0f;
- mTargetRadius = (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
+ mTargetRadius = getTargetRadius(mBounds);
}
mDensity = density;
@@ -72,6 +77,12 @@
onTargetRadiusChanged(mTargetRadius);
}
+ private static float getTargetRadius(Rect bounds) {
+ final float halfWidth = bounds.width() / 2.0f;
+ final float halfHeight = bounds.height() / 2.0f;
+ return (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
+ }
+
/**
* Starts a ripple enter animation.
*
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index efc171c..f7e8ed0 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -31,7 +31,6 @@
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Outline;
import android.graphics.Paint;
@@ -294,6 +293,14 @@
onHotspotBoundsChanged();
}
+ if (mBackground != null) {
+ mBackground.onBoundsChange();
+ }
+
+ if (mRipple != null) {
+ mRipple.onBoundsChange();
+ }
+
invalidateSelf();
}
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index b15caeb..5130e6c 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1132,6 +1132,24 @@
// chars. Interpreted in conjunction with the locale field.
char localeVariant[8];
+ enum {
+ // screenLayout2 bits for round/notround.
+ MASK_SCREENROUND = 0x03,
+ SCREENROUND_ANY = ACONFIGURATION_SCREENROUND_ANY,
+ SCREENROUND_NO = ACONFIGURATION_SCREENROUND_NO,
+ SCREENROUND_YES = ACONFIGURATION_SCREENROUND_YES,
+ };
+
+ // An extension of screenConfig.
+ union {
+ struct {
+ uint8_t screenLayout2; // Contains round/notround qualifier.
+ uint8_t screenConfigPad1; // Reserved padding.
+ uint16_t screenConfigPad2; // Reserved padding.
+ };
+ uint32_t screenConfig2;
+ };
+
void copyFromDeviceNoSwap(const ResTable_config& o);
void copyFromDtoH(const ResTable_config& o);
@@ -1160,6 +1178,7 @@
CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT,
CONFIG_UI_MODE = ACONFIGURATION_UI_MODE,
CONFIG_LAYOUTDIR = ACONFIGURATION_LAYOUTDIR,
+ CONFIG_SCREEN_ROUND = ACONFIGURATION_SCREEN_ROUND,
};
// Compare two configuration, returning CONFIG_* flags set for each value
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 2ae7b08..a95db9f 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1894,6 +1894,8 @@
if (diff != 0) return diff;
diff = (int32_t)(screenLayout - o.screenLayout);
if (diff != 0) return diff;
+ diff = (int32_t)(screenLayout2 - o.screenLayout2);
+ if (diff != 0) return diff;
diff = (int32_t)(uiMode - o.uiMode);
if (diff != 0) return diff;
diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp);
@@ -1951,6 +1953,9 @@
if (screenLayout != o.screenLayout) {
return screenLayout < o.screenLayout ? -1 : 1;
}
+ if (screenLayout2 != o.screenLayout2) {
+ return screenLayout2 < o.screenLayout2 ? -1 : 1;
+ }
if (uiMode != o.uiMode) {
return uiMode < o.uiMode ? -1 : 1;
}
@@ -1975,6 +1980,7 @@
if (version != o.version) diffs |= CONFIG_VERSION;
if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) diffs |= CONFIG_LAYOUTDIR;
if ((screenLayout & ~MASK_LAYOUTDIR) != (o.screenLayout & ~MASK_LAYOUTDIR)) diffs |= CONFIG_SCREEN_LAYOUT;
+ if ((screenLayout2 & MASK_SCREENROUND) != (o.screenLayout2 & MASK_SCREENROUND)) diffs |= CONFIG_SCREEN_ROUND;
if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE;
if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE;
if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE;
@@ -2080,6 +2086,13 @@
}
}
+ if (screenLayout2 || o.screenLayout2) {
+ if (((screenLayout2^o.screenLayout2) & MASK_SCREENROUND) != 0) {
+ if (!(screenLayout2 & MASK_SCREENROUND)) return false;
+ if (!(o.screenLayout2 & MASK_SCREENROUND)) return true;
+ }
+ }
+
if (orientation != o.orientation) {
if (!orientation) return false;
if (!o.orientation) return true;
@@ -2267,6 +2280,13 @@
}
}
+ if (screenLayout2 || o.screenLayout2) {
+ if (((screenLayout2^o.screenLayout2) & MASK_SCREENROUND) != 0 &&
+ (requested->screenLayout2 & MASK_SCREENROUND)) {
+ return screenLayout2 & MASK_SCREENROUND;
+ }
+ }
+
if ((orientation != o.orientation) && requested->orientation) {
return (orientation);
}
@@ -2480,6 +2500,15 @@
return false;
}
}
+
+ if (screenConfig2 != 0) {
+ const int screenRound = screenLayout2 & MASK_SCREENROUND;
+ const int setScreenRound = settings.screenLayout2 & MASK_SCREENROUND;
+ if (screenRound != 0 && screenRound != setScreenRound) {
+ return false;
+ }
+ }
+
if (screenSizeDp != 0) {
if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) {
if (kDebugTableSuperNoisy) {
@@ -2770,6 +2799,20 @@
break;
}
}
+ if ((screenLayout2&MASK_SCREENROUND) != 0) {
+ if (res.size() > 0) res.append("-");
+ switch (screenLayout2&MASK_SCREENROUND) {
+ case SCREENROUND_NO:
+ res.append("notround");
+ break;
+ case SCREENROUND_YES:
+ res.append("round");
+ break;
+ default:
+ res.appendFormat("screenRound=%d", dtohs(screenLayout2&MASK_SCREENROUND));
+ break;
+ }
+ }
if (orientation != ORIENTATION_ANY) {
if (res.size() > 0) res.append("-");
switch (orientation) {
diff --git a/libs/androidfw/tests/Config_test.cpp b/libs/androidfw/tests/Config_test.cpp
index ef30df4..738947a 100644
--- a/libs/androidfw/tests/Config_test.cpp
+++ b/libs/androidfw/tests/Config_test.cpp
@@ -100,4 +100,82 @@
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
}
+TEST(ConfigTest, shouldMatchRoundQualifier) {
+ ResTable_config deviceConfig;
+ memset(&deviceConfig, 0, sizeof(deviceConfig));
+
+ ResTable_config roundConfig;
+ memset(&roundConfig, 0, sizeof(roundConfig));
+ roundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
+
+ EXPECT_FALSE(roundConfig.match(deviceConfig));
+
+ deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
+
+ EXPECT_TRUE(roundConfig.match(deviceConfig));
+
+ deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_NO;
+
+ EXPECT_FALSE(roundConfig.match(deviceConfig));
+
+ ResTable_config notRoundConfig;
+ memset(¬RoundConfig, 0, sizeof(notRoundConfig));
+ notRoundConfig.screenLayout2 = ResTable_config::SCREENROUND_NO;
+
+ EXPECT_TRUE(notRoundConfig.match(deviceConfig));
+}
+
+TEST(ConfigTest, RoundQualifierShouldHaveStableSortOrder) {
+ ResTable_config defaultConfig;
+ memset(&defaultConfig, 0, sizeof(defaultConfig));
+
+ ResTable_config longConfig = defaultConfig;
+ longConfig.screenLayout = ResTable_config::SCREENLONG_YES;
+
+ ResTable_config longRoundConfig = longConfig;
+ longRoundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
+
+ ResTable_config longRoundPortConfig = longConfig;
+ longRoundPortConfig.orientation = ResTable_config::ORIENTATION_PORT;
+
+ EXPECT_TRUE(longConfig.compare(longRoundConfig) < 0);
+ EXPECT_TRUE(longConfig.compareLogical(longRoundConfig) < 0);
+ EXPECT_TRUE(longRoundConfig.compare(longConfig) > 0);
+ EXPECT_TRUE(longRoundConfig.compareLogical(longConfig) > 0);
+
+ EXPECT_TRUE(longRoundConfig.compare(longRoundPortConfig) < 0);
+ EXPECT_TRUE(longRoundConfig.compareLogical(longRoundPortConfig) < 0);
+ EXPECT_TRUE(longRoundPortConfig.compare(longRoundConfig) > 0);
+ EXPECT_TRUE(longRoundPortConfig.compareLogical(longRoundConfig) > 0);
+}
+
+TEST(ConfigTest, ScreenShapeHasCorrectDiff) {
+ ResTable_config defaultConfig;
+ memset(&defaultConfig, 0, sizeof(defaultConfig));
+
+ ResTable_config roundConfig = defaultConfig;
+ roundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
+
+ EXPECT_EQ(defaultConfig.diff(roundConfig), ResTable_config::CONFIG_SCREEN_ROUND);
+}
+
+TEST(ConfigTest, RoundIsMoreSpecific) {
+ ResTable_config deviceConfig;
+ memset(&deviceConfig, 0, sizeof(deviceConfig));
+ deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
+ deviceConfig.screenLayout = ResTable_config::SCREENLONG_YES;
+
+ ResTable_config targetConfigA;
+ memset(&targetConfigA, 0, sizeof(targetConfigA));
+
+ ResTable_config targetConfigB = targetConfigA;
+ targetConfigB.screenLayout = ResTable_config::SCREENLONG_YES;
+
+ ResTable_config targetConfigC = targetConfigB;
+ targetConfigC.screenLayout2 = ResTable_config::SCREENROUND_YES;
+
+ EXPECT_TRUE(targetConfigB.isBetterThan(targetConfigA, &deviceConfig));
+ EXPECT_TRUE(targetConfigC.isBetterThan(targetConfigB, &deviceConfig));
+}
+
} // namespace android.
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index f893fdd..d21762b 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -98,6 +98,7 @@
/** Maximum value for sample rate */
private static final int SAMPLE_RATE_HZ_MAX = 192000;
+ // FCC_8
/** Maximum value for AudioTrack channel count */
private static final int CHANNEL_COUNT_MAX = 8;
@@ -191,13 +192,20 @@
/**
* The write mode indicating the write operation will block until all data has been written,
- * to be used in {@link #write(ByteBuffer, int, int)}
+ * to be used as the actual value of the writeMode parameter in
+ * {@link #write(byte[], int, int, int)}, {@link #write(short[], int, int, int)},
+ * {@link #write(float[], int, int, int)}, {@link #write(ByteBuffer, int, int)}, and
+ * {@link #write(ByteBuffer, int, int, long)}.
*/
public final static int WRITE_BLOCKING = 0;
+
/**
* The write mode indicating the write operation will return immediately after
- * queuing as much audio data for playback as possible without blocking, to be used in
- * {@link #write(ByteBuffer, int, int)}.
+ * queuing as much audio data for playback as possible without blocking,
+ * to be used as the actual value of the writeMode parameter in
+ * {@link #write(ByteBuffer, int, int)}, {@link #write(short[], int, int, int)},
+ * {@link #write(float[], int, int, int)}, {@link #write(ByteBuffer, int, int)}, and
+ * {@link #write(ByteBuffer, int, int, long)}.
*/
public final static int WRITE_NON_BLOCKING = 1;
@@ -206,14 +214,16 @@
//--------------------
/**
* Indicates the state of the AudioTrack instance.
+ * One of STATE_UNINITIALIZED, STATE_INITIALIZED, or STATE_NO_STATIC_DATA.
*/
private int mState = STATE_UNINITIALIZED;
/**
* Indicates the play state of the AudioTrack instance.
+ * One of PLAYSTATE_STOPPED, PLAYSTATE_PAUSED, or PLAYSTATE_PLAYING.
*/
private int mPlayState = PLAYSTATE_STOPPED;
/**
- * Lock to make sure mPlayState updates are reflecting the actual state of the object.
+ * Lock to ensure mPlayState updates reflect the actual state of the object.
*/
private final Object mPlayStateLock = new Object();
/**
@@ -234,9 +244,9 @@
/**
* The audio data source sampling rate in Hz.
*/
- private int mSampleRate; // initialized by all constructors
+ private int mSampleRate; // initialized by all constructors via audioParamCheck()
/**
- * The number of audio output channels (1 is mono, 2 is stereo).
+ * The number of audio output channels (1 is mono, 2 is stereo, etc.).
*/
private int mChannelCount = 1;
/**
@@ -255,7 +265,7 @@
private final AudioAttributes mAttributes;
/**
- * The way audio is consumed by the audio sink, streaming or static.
+ * The way audio is consumed by the audio sink, one of MODE_STATIC or MODE_STREAM.
*/
private int mDataLoadMode = MODE_STREAM;
/**
@@ -265,7 +275,7 @@
*/
private int mChannelConfiguration = AudioFormat.CHANNEL_OUT_MONO;
/**
- * The current audio channel index configuration (if specified).
+ * The channel index mask if specified, otherwise 0.
*/
private int mChannelIndexMask = 0;
/**
@@ -274,7 +284,7 @@
* @see AudioFormat#ENCODING_PCM_16BIT
* @see AudioFormat#ENCODING_PCM_FLOAT
*/
- private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
+ private int mAudioFormat; // initialized by all constructors via audioParamCheck()
/**
* Audio session ID
*/
@@ -475,7 +485,7 @@
IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
mAppOps = IAppOpsService.Stub.asInterface(b);
- mAttributes = (new AudioAttributes.Builder(attributes).build());
+ mAttributes = new AudioAttributes.Builder(attributes).build();
if (sessionId < 0) {
throw new IllegalArgumentException("Invalid audio session ID: "+sessionId);
@@ -683,7 +693,8 @@
}
}
- // mask of all the channels supported by this implementation
+ // mask of all the positional channels supported, however the allowed combinations
+ // are further restricted by the matching left/right rule and CHANNEL_COUNT_MAX
private static final int SUPPORTED_OUT_CHANNELS =
AudioFormat.CHANNEL_OUT_FRONT_LEFT |
AudioFormat.CHANNEL_OUT_FRONT_RIGHT |
@@ -834,8 +845,7 @@
// To update when supporting compressed formats
int frameSizeInBytes;
if (AudioFormat.isEncodingLinearPcm(mAudioFormat)) {
- frameSizeInBytes = mChannelCount
- * (AudioFormat.getBytesPerSample(mAudioFormat));
+ frameSizeInBytes = mChannelCount * AudioFormat.getBytesPerSample(mAudioFormat);
} else {
frameSizeInBytes = 1;
}
@@ -941,7 +951,7 @@
* <p> For example, refer to {@link AudioFormat#CHANNEL_OUT_MONO},
* {@link AudioFormat#CHANNEL_OUT_STEREO}, {@link AudioFormat#CHANNEL_OUT_5POINT1}.
* This method may return {@link AudioFormat#CHANNEL_INVALID} if
- * a channel index mask is used. Consider
+ * a channel index mask was used. Consider
* {@link #getFormat()} instead, to obtain an {@link AudioFormat},
* which contains both the channel position mask and the channel index mask.
*/
@@ -978,9 +988,9 @@
* Returns the state of the AudioTrack instance. This is useful after the
* AudioTrack instance has been created to check if it was initialized
* properly. This ensures that the appropriate resources have been acquired.
+ * @see #STATE_UNINITIALIZED
* @see #STATE_INITIALIZED
* @see #STATE_NO_STATIC_DATA
- * @see #STATE_UNINITIALIZED
*/
public int getState() {
return mState;
@@ -1455,14 +1465,27 @@
//--------------------
/**
* Starts playing an AudioTrack.
+ * <p>
* If track's creation mode is {@link #MODE_STATIC}, you must have called one of
- * the {@link #write(byte[], int, int)}, {@link #write(short[], int, int)},
- * or {@link #write(float[], int, int, int)} methods.
- * If the mode is {@link #MODE_STREAM}, you can optionally prime the
- * output buffer by writing up to bufferSizeInBytes (from constructor) before starting.
- * This priming will avoid an immediate underrun, but is not required.
+ * the write methods ({@link #write(byte[], int, int)}, {@link #write(byte[], int, int, int)},
+ * {@link #write(short[], int, int)}, {@link #write(short[], int, int, int)},
+ * {@link #write(float[], int, int, int)}, or {@link #write(ByteBuffer, int, int)}) prior to
+ * play().
+ * <p>
+ * If the mode is {@link #MODE_STREAM}, you can optionally prime the data path prior to
+ * calling play(), by writing up to <code>bufferSizeInBytes</code> (from constructor).
+ * If you don’t call write() first, or if you call write() but with an insufficient amount of
+ * data, then the track will be in underrun state at play(). In this case,
+ * playback will not actually start playing until the data path is filled to a
+ * device-specific minimum level. This requirement for the path to be filled
+ * to a minimum level is also true when resuming audio playback after calling stop().
+ * Similarly the buffer will need to be filled up again after
+ * the track underruns due to failure to call write() in a timely manner with sufficient data.
+ * For portability, an application should prime the data path to the maximum allowed
+ * by writing data until the write() method returns a short transfer count.
+ * This allows play() to start immediately, and reduces the chance of underrun.
*
- * @throws IllegalStateException
+ * @throws IllegalStateException if the track isn't properly initialized
*/
public void play()
throws IllegalStateException {
@@ -1567,21 +1590,30 @@
* or copies audio data for later playback (static buffer mode).
* The format specified in the AudioTrack constructor should be
* {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
- * In streaming mode, will block until all data has been written to the audio sink.
+ * <p>
+ * In streaming mode, the write will normally block until all the data has been enqueued for
+ * playback, and will return a full transfer count. However, if the track is stopped or paused
+ * on entry, or another thread interrupts the write by calling stop or pause, or an I/O error
+ * occurs during the write, then the write may return a short transfer count.
+ * <p>
* In static buffer mode, copies the data to the buffer starting at offset 0.
- * Note that the actual playback of this data might occur after this function
- * returns. This function is thread safe with respect to {@link #stop} calls,
- * in which case all of the specified data might not be written to the audio sink.
+ * Note that the actual playback of this data might occur after this function returns.
*
* @param audioData the array that holds the data to play.
* @param offsetInBytes the offset expressed in bytes in audioData where the data to play
* starts.
* @param sizeInBytes the number of bytes to read in audioData after the offset.
- * @return the number of bytes that were written or {@link #ERROR_INVALID_OPERATION}
- * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+ * @return zero or the positive number of bytes that were written, or
+ * {@link #ERROR_INVALID_OPERATION}
+ * if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes, or
* {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
* needs to be recreated.
+ * The dead object error code is not returned if some data was successfully transferred.
+ * In this case, the error is returned at the next write().
+ *
+ * This is equivalent to {@link #write(byte[], int, int, int)} with <code>writeMode</code>
+ * set to {@link #WRITE_BLOCKING}.
*/
public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes) {
return write(audioData, offsetInBytes, sizeInBytes, WRITE_BLOCKING);
@@ -1592,11 +1624,17 @@
* or copies audio data for later playback (static buffer mode).
* The format specified in the AudioTrack constructor should be
* {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
- * In streaming mode, will block until all data has been written to the audio sink.
- * In static buffer mode, copies the data to the buffer starting at offset 0.
- * Note that the actual playback of this data might occur after this function
- * returns. This function is thread safe with respect to {@link #stop} calls,
- * in which case all of the specified data might not be written to the audio sink.
+ * <p>
+ * In streaming mode, the blocking behavior depends on the write mode. If the write mode is
+ * {@link #WRITE_BLOCKING}, the write will normally block until all the data has been enqueued
+ * for playback, and will return a full transfer count. However, if the write mode is
+ * {@link #WRITE_NON_BLOCKING}, or the track is stopped or paused on entry, or another thread
+ * interrupts the write by calling stop or pause, or an I/O error
+ * occurs during the write, then the write may return a short transfer count.
+ * <p>
+ * In static buffer mode, copies the data to the buffer starting at offset 0,
+ * and the write mode is ignored.
+ * Note that the actual playback of this data might occur after this function returns.
*
* @param audioData the array that holds the data to play.
* @param offsetInBytes the offset expressed in bytes in audioData where the data to play
@@ -1608,11 +1646,14 @@
* to the audio sink.
* <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
* queuing as much audio data for playback as possible without blocking.
- * @return the number of bytes that were written or {@link #ERROR_INVALID_OPERATION}
- * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+ * @return zero or the positive number of bytes that were written, or
+ * {@link #ERROR_INVALID_OPERATION}
+ * if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes, or
* {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
* needs to be recreated.
+ * The dead object error code is not returned if some data was successfully transferred.
+ * In this case, the error is returned at the next write().
*/
public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
@WriteMode int writeMode) {
@@ -1650,21 +1691,30 @@
* or copies audio data for later playback (static buffer mode).
* The format specified in the AudioTrack constructor should be
* {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array.
- * In streaming mode, will block until all data has been written to the audio sink.
+ * <p>
+ * In streaming mode, the write will normally block until all the data has been enqueued for
+ * playback, and will return a full transfer count. However, if the track is stopped or paused
+ * on entry, or another thread interrupts the write by calling stop or pause, or an I/O error
+ * occurs during the write, then the write may return a short transfer count.
+ * <p>
* In static buffer mode, copies the data to the buffer starting at offset 0.
- * Note that the actual playback of this data might occur after this function
- * returns. This function is thread safe with respect to {@link #stop} calls,
- * in which case all of the specified data might not be written to the audio sink.
+ * Note that the actual playback of this data might occur after this function returns.
*
* @param audioData the array that holds the data to play.
* @param offsetInShorts the offset expressed in shorts in audioData where the data to play
* starts.
* @param sizeInShorts the number of shorts to read in audioData after the offset.
- * @return the number of shorts that were written or {@link #ERROR_INVALID_OPERATION}
- * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+ * @return zero or the positive number of shorts that were written, or
+ * {@link #ERROR_INVALID_OPERATION}
+ * if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes, or
* {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
* needs to be recreated.
+ * The dead object error code is not returned if some data was successfully transferred.
+ * In this case, the error is returned at the next write().
+ *
+ * This is equivalent to {@link #write(short[], int, int, int)} with <code>writeMode</code>
+ * set to {@link #WRITE_BLOCKING}.
*/
public int write(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) {
return write(audioData, offsetInShorts, sizeInShorts, WRITE_BLOCKING);
@@ -1675,11 +1725,16 @@
* or copies audio data for later playback (static buffer mode).
* The format specified in the AudioTrack constructor should be
* {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array.
- * In streaming mode, will block until all data has been written to the audio sink.
+ * <p>
+ * In streaming mode, the blocking behavior depends on the write mode. If the write mode is
+ * {@link #WRITE_BLOCKING}, the write will normally block until all the data has been enqueued
+ * for playback, and will return a full transfer count. However, if the write mode is
+ * {@link #WRITE_NON_BLOCKING}, or the track is stopped or paused on entry, or another thread
+ * interrupts the write by calling stop or pause, or an I/O error
+ * occurs during the write, then the write may return a short transfer count.
+ * <p>
* In static buffer mode, copies the data to the buffer starting at offset 0.
- * Note that the actual playback of this data might occur after this function
- * returns. This function is thread safe with respect to {@link #stop} calls,
- * in which case all of the specified data might not be written to the audio sink.
+ * Note that the actual playback of this data might occur after this function returns.
*
* @param audioData the array that holds the data to play.
* @param offsetInShorts the offset expressed in shorts in audioData where the data to play
@@ -1691,11 +1746,14 @@
* to the audio sink.
* <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
* queuing as much audio data for playback as possible without blocking.
- * @return the number of shorts that were written or {@link #ERROR_INVALID_OPERATION}
- * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+ * @return zero or the positive number of shorts that were written, or
+ * {@link #ERROR_INVALID_OPERATION}
+ * if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes, or
* {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
* needs to be recreated.
+ * The dead object error code is not returned if some data was successfully transferred.
+ * In this case, the error is returned at the next write().
*/
public int write(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
@WriteMode int writeMode) {
@@ -1733,14 +1791,18 @@
* or copies audio data for later playback (static buffer mode).
* The format specified in the AudioTrack constructor should be
* {@link AudioFormat#ENCODING_PCM_FLOAT} to correspond to the data in the array.
+ * <p>
+ * In streaming mode, the blocking behavior depends on the write mode. If the write mode is
+ * {@link #WRITE_BLOCKING}, the write will normally block until all the data has been enqueued
+ * for playback, and will return a full transfer count. However, if the write mode is
+ * {@link #WRITE_NON_BLOCKING}, or the track is stopped or paused on entry, or another thread
+ * interrupts the write by calling stop or pause, or an I/O error
+ * occurs during the write, then the write may return a short transfer count.
+ * <p>
* In static buffer mode, copies the data to the buffer starting at offset 0,
* and the write mode is ignored.
- * In streaming mode, the blocking behavior will depend on the write mode.
- * <p>
- * Note that the actual playback of this data might occur after this function
- * returns. This function is thread safe with respect to {@link #stop} calls,
- * in which case all of the specified data might not be written to the audio sink.
- * <p>
+ * Note that the actual playback of this data might occur after this function returns.
+ *
* @param audioData the array that holds the data to play.
* The implementation does not clip for sample values within the nominal range
* [-1.0f, 1.0f], provided that all gains in the audio pipeline are
@@ -1760,11 +1822,14 @@
* to the audio sink.
* <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
* queuing as much audio data for playback as possible without blocking.
- * @return the number of floats that were written, or {@link #ERROR_INVALID_OPERATION}
- * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+ * @return zero or the positive number of floats that were written, or
+ * {@link #ERROR_INVALID_OPERATION}
+ * if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes, or
* {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
* needs to be recreated.
+ * The dead object error code is not returned if some data was successfully transferred.
+ * In this case, the error is returned at the next write().
*/
public int write(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats,
@WriteMode int writeMode) {
@@ -1808,9 +1873,19 @@
/**
* Writes the audio data to the audio sink for playback (streaming mode),
* or copies audio data for later playback (static buffer mode).
- * In static buffer mode, copies the data to the buffer starting at its 0 offset, and the write
- * mode is ignored.
- * In streaming mode, the blocking behavior will depend on the write mode.
+ * The audioData in ByteBuffer should match the format specified in the AudioTrack constructor.
+ * <p>
+ * In streaming mode, the blocking behavior depends on the write mode. If the write mode is
+ * {@link #WRITE_BLOCKING}, the write will normally block until all the data has been enqueued
+ * for playback, and will return a full transfer count. However, if the write mode is
+ * {@link #WRITE_NON_BLOCKING}, or the track is stopped or paused on entry, or another thread
+ * interrupts the write by calling stop or pause, or an I/O error
+ * occurs during the write, then the write may return a short transfer count.
+ * <p>
+ * In static buffer mode, copies the data to the buffer starting at offset 0,
+ * and the write mode is ignored.
+ * Note that the actual playback of this data might occur after this function returns.
+ *
* @param audioData the buffer that holds the data to play, starting at the position reported
* by <code>audioData.position()</code>.
* <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will
@@ -1824,10 +1899,12 @@
* to the audio sink.
* <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
* queuing as much audio data for playback as possible without blocking.
- * @return 0 or a positive number of bytes that were written, or
+ * @return zero or the positive number of bytes that were written, or
* {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}, or
* {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
* needs to be recreated.
+ * The dead object error code is not returned if some data was successfully transferred.
+ * In this case, the error is returned at the next write().
*/
public int write(@NonNull ByteBuffer audioData, int sizeInBytes,
@WriteMode int writeMode) {
@@ -1874,8 +1951,8 @@
}
/**
- * Writes the audio data to the audio sink for playback (streaming mode) on a HW_AV_SYNC track.
- * In streaming mode, the blocking behavior will depend on the write mode.
+ * Writes the audio data to the audio sink for playback in streaming mode on a HW_AV_SYNC track.
+ * The blocking behavior will depend on the write mode.
* @param audioData the buffer that holds the data to play, starting at the position reported
* by <code>audioData.position()</code>.
* <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will
@@ -1889,10 +1966,12 @@
* <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
* queuing as much audio data for playback as possible without blocking.
* @param timestamp The timestamp of the first decodable audio frame in the provided audioData.
- * @return 0 or a positive number of bytes that were written, or
+ * @return zero or a positive number of bytes that were written, or
* {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}, or
* {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
* needs to be recreated.
+ * The dead object error code is not returned if some data was successfully transferred.
+ * In this case, the error is returned at the next write().
*/
public int write(ByteBuffer audioData, int sizeInBytes,
@WriteMode int writeMode, long timestamp) {
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 3ee1959..f148606 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -40,6 +40,7 @@
import android.system.ErrnoException;
import android.system.OsConstants;
import android.util.Log;
+import android.util.Pair;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.widget.VideoView;
@@ -70,6 +71,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.InetSocketAddress;
+import java.util.BitSet;
+import java.util.HashSet;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
@@ -639,9 +642,7 @@
}
mTimeProvider = new TimeProvider(this);
- mOutOfBandSubtitleTracks = new Vector<SubtitleTrack>();
mOpenSubtitleSources = new Vector<InputStream>();
- mInbandSubtitleTracks = new SubtitleTrack[0];
IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
mAppOps = IAppOpsService.Stub.asInterface(b);
@@ -1693,8 +1694,6 @@
}
mOpenSubtitleSources.clear();
}
- mOutOfBandSubtitleTracks.clear();
- mInbandSubtitleTracks = new SubtitleTrack[0];
if (mSubtitleController != null) {
mSubtitleController.reset();
}
@@ -1709,6 +1708,11 @@
if (mEventHandler != null) {
mEventHandler.removeCallbacksAndMessages(null);
}
+
+ synchronized (mIndexTrackPairs) {
+ mIndexTrackPairs.clear();
+ mInbandTrackIndices.clear();
+ };
}
private native void _reset();
@@ -2050,6 +2054,16 @@
};
+ // We would like domain specific classes with more informative names than the `first` and `second`
+ // in generic Pair, but we would also like to avoid creating new/trivial classes. As a compromise
+ // we document the meanings of `first` and `second` here:
+ //
+ // Pair.first - inband track index; non-null iff representing an inband track.
+ // Pair.second - a SubtitleTrack registered with mSubtitleController; non-null iff representing
+ // an inband subtitle track or any out-of-band track (subtitle or timedtext).
+ private Vector<Pair<Integer, SubtitleTrack>> mIndexTrackPairs = new Vector<>();
+ private BitSet mInbandTrackIndices = new BitSet();
+
/**
* Returns an array of track information.
*
@@ -2061,17 +2075,20 @@
public TrackInfo[] getTrackInfo() throws IllegalStateException {
TrackInfo trackInfo[] = getInbandTrackInfo();
// add out-of-band tracks
- TrackInfo allTrackInfo[] = new TrackInfo[trackInfo.length + mOutOfBandSubtitleTracks.size()];
- System.arraycopy(trackInfo, 0, allTrackInfo, 0, trackInfo.length);
- int i = trackInfo.length;
- for (SubtitleTrack track: mOutOfBandSubtitleTracks) {
- int type = track.isTimedText()
- ? TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT
- : TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE;
- allTrackInfo[i] = new TrackInfo(type, track.getFormat());
- ++i;
+ synchronized (mIndexTrackPairs) {
+ TrackInfo allTrackInfo[] = new TrackInfo[mIndexTrackPairs.size()];
+ for (int i = 0; i < allTrackInfo.length; i++) {
+ Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i);
+ if (p.first != null) {
+ // inband track
+ allTrackInfo[i] = trackInfo[p.first];
+ } else {
+ SubtitleTrack track = p.second;
+ allTrackInfo[i] = new TrackInfo(track.getTrackType(), track.getFormat());
+ }
+ }
+ return allTrackInfo;
}
- return allTrackInfo;
}
private TrackInfo[] getInbandTrackInfo() throws IllegalStateException {
@@ -2167,22 +2184,21 @@
}
}
- private final Object mInbandSubtitleLock = new Object();
- private SubtitleTrack[] mInbandSubtitleTracks;
private int mSelectedSubtitleTrackIndex = -1;
- private Vector<SubtitleTrack> mOutOfBandSubtitleTracks;
private Vector<InputStream> mOpenSubtitleSources;
private OnSubtitleDataListener mSubtitleDataListener = new OnSubtitleDataListener() {
@Override
public void onSubtitleData(MediaPlayer mp, SubtitleData data) {
int index = data.getTrackIndex();
- if (index >= mInbandSubtitleTracks.length) {
- return;
- }
- SubtitleTrack track = mInbandSubtitleTracks[index];
- if (track != null) {
- track.onData(data);
+ synchronized (mIndexTrackPairs) {
+ for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) {
+ if (p.first != null && p.first == index && p.second != null) {
+ // inband subtitle track that owns data
+ SubtitleTrack track = p.second;
+ track.onData(data);
+ }
+ }
}
}
};
@@ -2201,18 +2217,24 @@
if (track == null) {
return;
}
- for (int i = 0; i < mInbandSubtitleTracks.length; i++) {
- if (mInbandSubtitleTracks[i] == track) {
- Log.v(TAG, "Selecting subtitle track " + i);
- mSelectedSubtitleTrackIndex = i;
- try {
- selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, true);
- } catch (IllegalStateException e) {
+
+ synchronized (mIndexTrackPairs) {
+ for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) {
+ if (p.first != null && p.second == track) {
+ // inband subtitle track that is selected
+ mSelectedSubtitleTrackIndex = p.first;
+ break;
}
- setOnSubtitleDataListener(mSubtitleDataListener);
- break;
}
}
+
+ if (mSelectedSubtitleTrackIndex >= 0) {
+ try {
+ selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, true);
+ } catch (IllegalStateException e) {
+ }
+ setOnSubtitleDataListener(mSubtitleDataListener);
+ }
// no need to select out-of-band tracks
}
@@ -2252,7 +2274,9 @@
mOpenSubtitleSources.remove(fIs);
}
scanner.close();
- mOutOfBandSubtitleTracks.add(track);
+ synchronized (mIndexTrackPairs) {
+ mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track));
+ }
track.onData(contents.getBytes(), true /* eos */, ~0 /* runID: keep forever */);
return MEDIA_INFO_EXTERNAL_METADATA_UPDATE;
}
@@ -2274,23 +2298,33 @@
setSubtitleAnchor();
}
+ populateInbandTracks();
+
+ if (mSubtitleController != null) {
+ mSubtitleController.selectDefaultTrack();
+ }
+ }
+
+ private void populateInbandTracks() {
TrackInfo[] tracks = getInbandTrackInfo();
- synchronized (mInbandSubtitleLock) {
- SubtitleTrack[] inbandTracks = new SubtitleTrack[tracks.length];
- for (int i=0; i < tracks.length; i++) {
+ synchronized (mIndexTrackPairs) {
+ for (int i = 0; i < tracks.length; i++) {
+ if (mInbandTrackIndices.get(i)) {
+ continue;
+ } else {
+ mInbandTrackIndices.set(i);
+ }
+
+ // newly appeared inband track
if (tracks[i].getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
- if (i < mInbandSubtitleTracks.length) {
- inbandTracks[i] = mInbandSubtitleTracks[i];
- } else {
- SubtitleTrack track = mSubtitleController.addTrack(
- tracks[i].getFormat());
- inbandTracks[i] = track;
- }
+ SubtitleTrack track = mSubtitleController.addTrack(
+ tracks[i].getFormat());
+ mIndexTrackPairs.add(Pair.create(i, track));
+ } else {
+ mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(i, null));
}
}
- mInbandSubtitleTracks = inbandTracks;
}
- mSubtitleController.selectDefaultTrack();
}
/* TODO: Limit the total number of external timed text source to a reasonable number.
@@ -2438,7 +2472,9 @@
mSubtitleController.registerRenderer(new SRTRenderer(context, mEventHandler));
}
final SubtitleTrack track = mSubtitleController.addTrack(fFormat);
- mOutOfBandSubtitleTracks.add(track);
+ synchronized (mIndexTrackPairs) {
+ mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track));
+ }
final FileDescriptor fd3 = fd2;
final long offset2 = offset;
@@ -2510,12 +2546,18 @@
* @see #deselectTrack(int)
*/
public int getSelectedTrack(int trackType) throws IllegalStateException {
- if (trackType == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE && mSubtitleController != null) {
+ if (mSubtitleController != null
+ && (trackType == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE
+ || trackType == TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT)) {
SubtitleTrack subtitleTrack = mSubtitleController.getSelectedTrack();
if (subtitleTrack != null) {
- int index = mOutOfBandSubtitleTracks.indexOf(subtitleTrack);
- if (index >= 0) {
- return mInbandSubtitleTracks.length + index;
+ synchronized (mIndexTrackPairs) {
+ for (int i = 0; i < mIndexTrackPairs.size(); i++) {
+ Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i);
+ if (p.second == subtitleTrack && subtitleTrack.getTrackType() == trackType) {
+ return i;
+ }
+ }
}
}
}
@@ -2527,8 +2569,16 @@
request.writeInt(INVOKE_ID_GET_SELECTED_TRACK);
request.writeInt(trackType);
invoke(request, reply);
- int selectedTrack = reply.readInt();
- return selectedTrack;
+ int inbandTrackIndex = reply.readInt();
+ synchronized (mIndexTrackPairs) {
+ for (int i = 0; i < mIndexTrackPairs.size(); i++) {
+ Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i);
+ if (p.first != null && p.first == inbandTrackIndex) {
+ return i;
+ }
+ }
+ }
+ return -1;
} finally {
request.recycle();
reply.recycle();
@@ -2588,36 +2638,30 @@
private void selectOrDeselectTrack(int index, boolean select)
throws IllegalStateException {
// handle subtitle track through subtitle controller
- SubtitleTrack track = null;
- synchronized (mInbandSubtitleLock) {
- if (mInbandSubtitleTracks.length == 0) {
- TrackInfo[] tracks = getInbandTrackInfo();
- mInbandSubtitleTracks = new SubtitleTrack[tracks.length];
- for (int i=0; i < tracks.length; i++) {
- if (tracks[i].getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
- mInbandSubtitleTracks[i] = mSubtitleController.addTrack(tracks[i].getFormat());
- }
- }
- }
+ populateInbandTracks();
+
+ Pair<Integer,SubtitleTrack> p = null;
+ try {
+ p = mIndexTrackPairs.get(index);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // ignore bad index
+ return;
}
- if (index < mInbandSubtitleTracks.length) {
- track = mInbandSubtitleTracks[index];
- } else if (index < mInbandSubtitleTracks.length + mOutOfBandSubtitleTracks.size()) {
- track = mOutOfBandSubtitleTracks.get(index - mInbandSubtitleTracks.length);
+ SubtitleTrack track = p.second;
+ if (track == null) {
+ // inband (de)select
+ selectOrDeselectInbandTrack(p.first, select);
+ return;
}
- if (mSubtitleController != null && track != null) {
- if (select) {
- if (track.isTimedText()) {
- int ttIndex = getSelectedTrack(TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT);
- if (ttIndex >= 0 && ttIndex < mInbandSubtitleTracks.length) {
- // deselect inband counterpart
- selectOrDeselectInbandTrack(ttIndex, false);
- }
- }
- mSubtitleController.selectTrack(track);
- } else if (mSubtitleController.getSelectedTrack() == track) {
+ if (mSubtitleController == null) {
+ return;
+ }
+
+ if (!select) {
+ // out-of-band deselect
+ if (mSubtitleController.getSelectedTrack() == track) {
mSubtitleController.selectTrack(null);
} else {
Log.w(TAG, "trying to deselect track that was not selected");
@@ -2625,7 +2669,20 @@
return;
}
- selectOrDeselectInbandTrack(index, select);
+ // out-of-band select
+ if (track.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
+ int ttIndex = getSelectedTrack(TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT);
+ synchronized (mIndexTrackPairs) {
+ if (ttIndex >= 0 && ttIndex < mIndexTrackPairs.size()) {
+ Pair<Integer,SubtitleTrack> p2 = mIndexTrackPairs.get(ttIndex);
+ if (p2.first != null && p2.second == null) {
+ // deselect inband counterpart
+ selectOrDeselectInbandTrack(p2.first, false);
+ }
+ }
+ }
+ }
+ mSubtitleController.selectTrack(track);
}
private void selectOrDeselectInbandTrack(int index, boolean select)
diff --git a/media/java/android/media/PlaybackParams.aidl b/media/java/android/media/PlaybackParams.aidl
new file mode 100644
index 0000000..0356117
--- /dev/null
+++ b/media/java/android/media/PlaybackParams.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+parcelable PlaybackParams;
diff --git a/media/java/android/media/PlaybackParams.java b/media/java/android/media/PlaybackParams.java
index e41b2b6..021dbf2 100644
--- a/media/java/android/media/PlaybackParams.java
+++ b/media/java/android/media/PlaybackParams.java
@@ -16,11 +16,13 @@
package android.media;
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import android.annotation.IntDef;
-
/**
* Structure for common playback params.
*
@@ -52,7 +54,7 @@
* similar to {@link AudioTrack#setPlaybackRate(int)}.</li>
* </ul>
*/
-public final class PlaybackParams {
+public final class PlaybackParams implements Parcelable {
/** @hide */
@IntDef(
value = {
@@ -94,6 +96,20 @@
private float mPitch = 1.0f;
private float mSpeed = 1.0f;
+ public PlaybackParams() {
+ }
+
+ private PlaybackParams(Parcel in) {
+ mSet = in.readInt();
+ mAudioFallbackMode = in.readInt();
+ mAudioStretchMode = in.readInt();
+ mPitch = in.readFloat();
+ if (mPitch < 0.f) {
+ mPitch = 0.f;
+ }
+ mSpeed = in.readFloat();
+ }
+
/**
* Allows defaults to be returned for properties not set.
* Otherwise a {@link java.lang.IllegalArgumentException} exception
@@ -158,8 +174,12 @@
* Sets the pitch factor.
* @param pitch
* @return this <code>PlaybackParams</code> instance.
+ * @throws InvalidArgumentException if the pitch is negative
*/
public PlaybackParams setPitch(float pitch) {
+ if (pitch < 0.f) {
+ throw new IllegalArgumentException("pitch must not be negative");
+ }
mPitch = pitch;
mSet |= SET_PITCH;
return this;
@@ -199,4 +219,32 @@
}
return mSpeed;
}
+
+ public static final Parcelable.Creator<PlaybackParams> CREATOR =
+ new Parcelable.Creator<PlaybackParams>() {
+ @Override
+ public PlaybackParams createFromParcel(Parcel in) {
+ return new PlaybackParams(in);
+ }
+
+ @Override
+ public PlaybackParams[] newArray(int size) {
+ return new PlaybackParams[size];
+ }
+ };
+
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSet);
+ dest.writeInt(mAudioFallbackMode);
+ dest.writeInt(mAudioStretchMode);
+ dest.writeFloat(mPitch);
+ dest.writeFloat(mSpeed);
+ }
}
diff --git a/media/java/android/media/SubtitleController.java b/media/java/android/media/SubtitleController.java
index f82dbe0..fd72b39 100644
--- a/media/java/android/media/SubtitleController.java
+++ b/media/java/android/media/SubtitleController.java
@@ -20,6 +20,7 @@
import java.util.Vector;
import android.content.Context;
+import android.media.MediaPlayer.TrackInfo;
import android.media.SubtitleTrack.RenderingWidget;
import android.os.Handler;
import android.os.Looper;
@@ -275,7 +276,8 @@
mSelectedTrack.getFormat().getInteger(
MediaFormat.KEY_IS_FORCED_SUBTITLE, 0) != 0)) {
show();
- } else if (mSelectedTrack != null && !mSelectedTrack.isTimedText()) {
+ } else if (mSelectedTrack != null
+ && mSelectedTrack.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
hide();
}
mVisibilityIsExplicit = false;
diff --git a/media/java/android/media/SubtitleTrack.java b/media/java/android/media/SubtitleTrack.java
index c760810..6c8e323 100644
--- a/media/java/android/media/SubtitleTrack.java
+++ b/media/java/android/media/SubtitleTrack.java
@@ -17,6 +17,7 @@
package android.media;
import android.graphics.Canvas;
+import android.media.MediaPlayer.TrackInfo;
import android.os.Handler;
import android.util.Log;
import android.util.LongSparseArray;
@@ -609,8 +610,10 @@
}
/** @hide whether this is a text track who fires events instead getting rendered */
- public boolean isTimedText() {
- return getRenderingWidget() == null;
+ public int getTrackType() {
+ return getRenderingWidget() == null
+ ? TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT
+ : TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE;
}
diff --git a/media/java/android/media/SyncParams.java b/media/java/android/media/SyncParams.java
index 00a8dc0..319eacba 100644
--- a/media/java/android/media/SyncParams.java
+++ b/media/java/android/media/SyncParams.java
@@ -51,9 +51,9 @@
* </ul>
* <p> <strong>tolerance:</strong> specifies the amount of allowed playback rate
* change to keep media in sync with the sync source. The handling of this depends
- * on the sync source.
+ * on the sync source, but must not be negative, and must be less than one.
* <p> <strong>frameRate:</strong> initial hint for video frame rate. Used when
- * sync source is vsync.
+ * sync source is vsync. Negative values can be used to clear a previous hint.
*/
public final class SyncParams {
/** @hide */
@@ -163,7 +163,7 @@
private int mSet = 0;
// params
- private int mAudioAdjustMode = AUDIO_ADJUST_MODE_STRETCH;
+ private int mAudioAdjustMode = AUDIO_ADJUST_MODE_DEFAULT;
private int mSyncSource = SYNC_SOURCE_DEFAULT;
private float mTolerance = 0.f;
private float mFrameRate = 0.f;
@@ -227,13 +227,17 @@
}
/**
- * Sets the tolerance. The default tolerance is 0.
+ * Sets the tolerance. The default tolerance is platform specific, but is never more than 1/24.
* @param tolerance A non-negative number representing
* the maximum deviation of the playback rate from the playback rate
* set. ({@code abs(actual_rate - set_rate) / set_rate})
* @return this <code>SyncParams</code> instance.
+ * @throws InvalidArgumentException if the tolerance is negative, or not less than one
*/
public SyncParams setTolerance(float tolerance) {
+ if (tolerance < 0.f || tolerance >= 1.f) {
+ throw new IllegalArgumentException("tolerance must be less than one and non-negative");
+ }
mTolerance = tolerance;
mSet |= SET_TOLERANCE;
return this;
@@ -256,7 +260,8 @@
/**
* Sets the video frame rate hint to be used. By default the frame rate is unspecified.
* @param frameRate A non-negative number used as an initial hint on
- * the video frame rate to be used when using vsync as the sync source.
+ * the video frame rate to be used when using vsync as the sync source. A negative
+ * number is used to clear a previous hint.
* @return this <code>SyncParams</code> instance.
*/
public SyncParams setFrameRate(float frameRate) {
@@ -269,7 +274,8 @@
* Retrieves the video frame rate hint.
* @return frame rate factor. A non-negative number representing
* the maximum deviation of the playback rate from the playback rate
- * set. ({@code abs(actual_rate - set_rate) / set_rate})
+ * set. ({@code abs(actual_rate - set_rate) / set_rate}), or a negative
+ * number representing the desire to clear a previous hint using these params.
* @throws IllegalStateException if frame rate is not set.
*/
public float getFrameRate() {
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 6fe5dbb..f8057db 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -18,6 +18,7 @@
import android.content.ComponentName;
import android.graphics.Rect;
+import android.media.PlaybackParams;
import android.media.tv.DvbDeviceInfo;
import android.media.tv.ITvInputClient;
import android.media.tv.ITvInputHardware;
@@ -79,7 +80,7 @@
void timeShiftPause(in IBinder sessionToken, int userId);
void timeShiftResume(in IBinder sessionToken, int userId);
void timeShiftSeekTo(in IBinder sessionToken, long timeMs, int userId);
- void timeShiftSetPlaybackRate(in IBinder sessionToken, float rate, int audioMode, int userId);
+ void timeShiftSetPlaybackParams(in IBinder sessionToken, in PlaybackParams params, int userId);
void timeShiftEnablePositionTracking(in IBinder sessionToken, boolean enable, int userId);
// For TV input hardware binding
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 17f3984..6a06b8f 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -17,6 +17,7 @@
package android.media.tv;
import android.graphics.Rect;
+import android.media.PlaybackParams;
import android.media.tv.TvTrackInfo;
import android.net.Uri;
import android.os.Bundle;
@@ -50,6 +51,6 @@
void timeShiftPause();
void timeShiftResume();
void timeShiftSeekTo(long timeMs);
- void timeShiftSetPlaybackRate(float rate, int audioMode);
+ void timeShiftSetPlaybackParams(in PlaybackParams params);
void timeShiftEnablePositionTracking(boolean enable);
}
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 58954bd..66b904b 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -62,7 +62,7 @@
private static final int DO_TIME_SHIFT_PAUSE = 14;
private static final int DO_TIME_SHIFT_RESUME = 15;
private static final int DO_TIME_SHIFT_SEEK_TO = 16;
- private static final int DO_TIME_SHIFT_SET_PLAYBACK_RATE = 17;
+ private static final int DO_TIME_SHIFT_SET_PLAYBACK_PARAMS = 17;
private static final int DO_TIME_SHIFT_ENABLE_POSITION_TRACKING = 18;
private final HandlerCaller mCaller;
@@ -172,7 +172,7 @@
mTvInputSessionImpl.timeShiftSeekTo((Long) msg.obj);
break;
}
- case DO_TIME_SHIFT_SET_PLAYBACK_RATE: {
+ case DO_TIME_SHIFT_SET_PLAYBACK_PARAMS: {
PlaybackParams params = new PlaybackParams()
.setSpeed((Float) msg.obj)
.setAudioFallbackMode(msg.arg1);
@@ -293,9 +293,9 @@
}
@Override
- public void timeShiftSetPlaybackRate(float rate, int audioMode) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_TIME_SHIFT_SET_PLAYBACK_RATE,
- audioMode, Float.valueOf(rate)));
+ public void timeShiftSetPlaybackParams(PlaybackParams params) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_TIME_SHIFT_SET_PLAYBACK_PARAMS,
+ params));
}
@Override
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index caa8a09..7fc19f1 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.graphics.Rect;
+import android.media.PlaybackParams;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -1747,23 +1748,17 @@
}
/**
- * Sets playback rate and audio mode.
+ * Sets playback rate using {@link android.media.PlaybackParams}.
*
- * @param rate The ratio between desired playback rate and normal one.
- * @param audioMode Audio playback mode. Must be one of the supported audio modes:
- * <ul>
- * <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_DEFAULT}
- * <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_STRETCH}
- * <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_RESAMPLE}
- * </ul>
+ * @param params The playback params.
*/
- void timeShiftSetPlaybackRate(float rate, int audioMode) {
+ void timeShiftSetPlaybackParams(PlaybackParams params) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
- mService.timeShiftSetPlaybackRate(mToken, rate, audioMode, mUserId);
+ mService.timeShiftSetPlaybackParams(mToken, params, mUserId);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 6169eea..e7ce1dd 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -28,7 +28,6 @@
import android.media.tv.TvInputManager.Session;
import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
import android.media.tv.TvInputManager.SessionCallback;
-import android.media.tv.TvView.TimeShiftPositionCallback;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -470,13 +469,13 @@
}
/**
- * Sets playback rate using {@link android.media#PlaybackParams}.
+ * Sets playback rate using {@link android.media.PlaybackParams}.
*
* @param params The playback params.
*/
public void timeShiftSetPlaybackParams(@NonNull PlaybackParams params) {
if (mSession != null) {
- mSession.timeShiftSetPlaybackRate(params.getSpeed(), params.getAudioFallbackMode());
+ mSession.timeShiftSetPlaybackParams(params);
}
}
diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp
index 74cf80e..77237ae 100644
--- a/native/android/configuration.cpp
+++ b/native/android/configuration.cpp
@@ -101,6 +101,10 @@
>> ResTable_config::SHIFT_SCREENLONG;
}
+int32_t AConfiguration_getScreenRound(AConfiguration* config) {
+ return (config->screenLayout2&ResTable_config::MASK_SCREENROUND);
+}
+
int32_t AConfiguration_getUiModeType(AConfiguration* config) {
return config->uiMode&ResTable_config::MASK_UI_MODE_TYPE;
}
@@ -192,6 +196,11 @@
| ((screenLong<<ResTable_config::SHIFT_SCREENLONG)&ResTable_config::MASK_SCREENLONG);
}
+void AConfiguration_setScreenRound(AConfiguration* config, int32_t screenRound) {
+ config->screenLayout2 = (config->screenLayout2&~ResTable_config::MASK_SCREENROUND)
+ | (screenRound&ResTable_config::MASK_SCREENROUND);
+}
+
void AConfiguration_setUiModeType(AConfiguration* config, int32_t uiModeType) {
config->uiMode = (config->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
| (uiModeType&ResTable_config::MASK_UI_MODE_TYPE);
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 1019e6c..c7b7e6a 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -146,6 +146,7 @@
private void done(Result result) {
if (mNetworkCallback != null) {
mCm.unregisterNetworkCallback(mNetworkCallback);
+ mNetworkCallback = null;
}
switch (result) {
case DISMISSED:
@@ -191,6 +192,16 @@
return super.onOptionsItemSelected(item);
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ if (mNetworkCallback != null) {
+ mCm.unregisterNetworkCallback(mNetworkCallback);
+ mNetworkCallback = null;
+ }
+ }
+
private void testForCaptivePortal() {
new Thread(new Runnable() {
public void run() {
diff --git a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
index 1cabcdfc..e03f449 100644
--- a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
+import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
@@ -42,7 +43,7 @@
.setPackage("com.android.phone")
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@@ -127,6 +128,7 @@
KeyguardUpdateMonitor.getInstance(mContext).reportEmergencyCallAction(
true /* bypassHandler */);
getContext().startActivityAsUser(INTENT_EMERGENCY_DIAL,
+ ActivityOptions.makeCustomAnimation(getContext(), 0, 0).toBundle(),
new UserHandle(KeyguardUpdateMonitor.getCurrentUser()));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 2d1fab0..c702673 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -621,10 +621,11 @@
mHeaderBar.draw(c);
c.setBitmap(null);
}
+ Bitmap thumbnailImmutable = thumbnail.createAshmemBitmap();
mStartAnimationTriggered = false;
return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
- thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
+ thumbnailImmutable, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
toTaskRect.height(), mHandler, this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 1377975..b3e6221 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -490,6 +490,7 @@
// Notify the system to skip the thumbnail layer by using an ALPHA_8 bitmap
b = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
}
+ Bitmap bImmut = b.createAshmemBitmap();
ActivityOptions.OnAnimationStartedListener animStartedListener = null;
if (lockToTask) {
animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
@@ -515,7 +516,7 @@
sourceView.getHandler(), animStartedListener);
} else {
opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
- b, offsetX, offsetY, transform.rect.width(), transform.rect.height(),
+ bImmut, offsetX, offsetY, transform.rect.width(), transform.rect.height(),
sourceView.getHandler(), animStartedListener);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 17e2cb5..3feec9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -59,6 +59,12 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int ownMaxHeight = mMaxViewHeight;
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
+ if (hasFixedHeight) {
+ // We have a height set in our layout, so we want to be at most as big as given
+ ownMaxHeight = Math.min(MeasureSpec.getSize(heightMeasureSpec), ownMaxHeight);
+ }
int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
int maxChildHeight = 0;
int childCount = getChildCount();
@@ -85,7 +91,7 @@
mMatchParentViews.add(child);
}
}
- int ownHeight = Math.min(ownMaxHeight, maxChildHeight);
+ int ownHeight = hasFixedHeight ? ownMaxHeight : Math.min(ownMaxHeight, maxChildHeight);
newHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY);
for (View child : mMatchParentViews) {
child.measure(getChildMeasureSpec(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
index a2cd50a..bd36462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
@@ -20,6 +20,8 @@
import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import java.util.BitSet;
+
public class EthernetSignalController extends
SignalController<SignalController.State, SignalController.IconGroup> {
@@ -38,6 +40,12 @@
}
@Override
+ public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
+ mCurrentState.connected = connectedTransports.get(mTransportType);
+ super.updateConnectivity(connectedTransports, validatedTransports);
+ }
+
+ @Override
public void notifyListeners() {
boolean ethernetVisible = mCurrentState.connected;
String contentDescription = getStringIfExists(getContentDescription());
@@ -51,9 +59,4 @@
public SignalController.State cleanState() {
return new SignalController.State();
}
-
- public void setConnected(boolean connected) {
- mCurrentState.connected = connected;
- notifyListenersIfNecessary();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 5515873..0d59953 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -36,6 +36,7 @@
import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
import java.io.PrintWriter;
+import java.util.BitSet;
import java.util.Objects;
@@ -105,13 +106,13 @@
notifyListenersIfNecessary();
}
- public void setInetCondition(int inetCondition, int inetConditionForNetwork) {
- // For mobile data, use general inet condition for phone signal indexing,
- // and network specific for data indexing (I think this might be a bug, but
- // keeping for now).
- // TODO: Update with explanation of why.
- mCurrentState.inetForNetwork = inetConditionForNetwork;
- setInetCondition(inetCondition);
+ @Override
+ public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
+ boolean isValidated = validatedTransports.get(mTransportType);
+ mCurrentState.isDefault = connectedTransports.get(mTransportType);
+ // Only show this as not having connectivity if we are default.
+ mCurrentState.inetCondition = (isValidated || !mCurrentState.isDefault) ? 1 : 0;
+ notifyListenersIfNecessary();
}
public void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) {
@@ -195,9 +196,9 @@
String contentDescription = getStringIfExists(getContentDescription());
String dataContentDescription = getStringIfExists(icons.mDataContentDescription);
- boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0
+ // Show icon in QS when we are connected or need to show roaming.
+ boolean showDataIcon = mCurrentState.dataConnected
|| mCurrentState.iconGroup == TelephonyIcons.ROAMING;
-
IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode,
getCurrentIconId(), contentDescription);
@@ -206,7 +207,7 @@
String description = null;
// Only send data sim callbacks to QS.
if (mCurrentState.dataSim) {
- qsTypeIcon = showDataIcon ? icons.mQsDataType[mCurrentState.inetForNetwork] : 0;
+ qsTypeIcon = showDataIcon ? icons.mQsDataType : 0;
qsIcon = new IconState(mCurrentState.enabled
&& !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
@@ -217,6 +218,7 @@
boolean activityOut = mCurrentState.dataConnected
&& !mCurrentState.carrierNetworkChangeMode
&& mCurrentState.activityOut;
+ showDataIcon &= mCurrentState.isDefault;
int typeIcon = showDataIcon ? icons.mDataType : 0;
mCallbackHandler.setMobileDataIndicators(statusIcon, qsIcon, getCurrentDarkIconId(),
typeIcon, qsTypeIcon, activityIn, activityOut, dataContentDescription, description,
@@ -470,12 +472,12 @@
final int mDataContentDescription; // mContentDescriptionDataType
final int mDataType;
final boolean mIsWide;
- final int[] mQsDataType;
+ final int mQsDataType;
public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
int discContentDesc, int dataContentDesc, int dataType, boolean isWide,
- int[] qsDataType) {
+ int qsDataType) {
this(name, sbIcons, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState,
sbDiscState, sbDiscState, qsDiscState, discContentDesc, dataContentDesc,
dataType, isWide, qsDataType);
@@ -484,7 +486,7 @@
public MobileIconGroup(String name, int[][] sbIcons, int[][] sbDarkIcons, int[][] qsIcons,
int[] contentDesc, int sbNullState, int qsNullState, int sbDiscState,
int sbDarkDiscState, int qsDiscState, int discContentDesc, int dataContentDesc,
- int dataType, boolean isWide, int[] qsDataType) {
+ int dataType, boolean isWide, int qsDataType) {
super(name, sbIcons, sbDarkIcons, qsIcons, contentDesc, sbNullState, qsNullState,
sbDiscState, sbDarkDiscState, qsDiscState, discContentDesc);
mDataContentDescription = dataContentDesc;
@@ -502,7 +504,7 @@
boolean isEmergency;
boolean airplaneMode;
boolean carrierNetworkChangeMode;
- int inetForNetwork;
+ boolean isDefault;
@Override
public void copyFrom(State s) {
@@ -512,7 +514,7 @@
networkName = state.networkName;
networkNameData = state.networkNameData;
dataConnected = state.dataConnected;
- inetForNetwork = state.inetForNetwork;
+ isDefault = state.isDefault;
isEmergency = state.isEmergency;
airplaneMode = state.airplaneMode;
carrierNetworkChangeMode = state.carrierNetworkChangeMode;
@@ -526,7 +528,7 @@
builder.append("networkName=").append(networkName).append(',');
builder.append("networkNameData=").append(networkNameData).append(',');
builder.append("dataConnected=").append(dataConnected).append(',');
- builder.append("inetForNetwork=").append(inetForNetwork).append(',');
+ builder.append("isDefault=").append(isDefault).append(',');
builder.append("isEmergency=").append(isEmergency).append(',');
builder.append("airplaneMode=").append(airplaneMode).append(',');
builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode);
@@ -542,7 +544,7 @@
&& ((MobileState) o).isEmergency == isEmergency
&& ((MobileState) o).airplaneMode == airplaneMode
&& ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
- && ((MobileState) o).inetForNetwork == inetForNetwork;
+ && ((MobileState) o).isDefault == isDefault;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 484f66a..e8957f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -538,17 +538,10 @@
private void pushConnectivityToSignals() {
// We want to update all the icons, all at once, for any condition change
for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
- mobileSignalController.setInetCondition(
- mInetCondition ? 1 : 0,
- mValidatedTransports.get(mobileSignalController.getTransportType()) ? 1 : 0);
+ mobileSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
}
- mWifiSignalController.setInetCondition(
- mValidatedTransports.get(mWifiSignalController.getTransportType()) ? 1 : 0);
-
- mEthernetSignalController.setConnected(
- mConnectedTransports.get(mEthernetSignalController.getTransportType()));
- mEthernetSignalController.setInetCondition(
- mValidatedTransports.get(mEthernetSignalController.getTransportType()) ? 1 : 0);
+ mWifiSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
+ mEthernetSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -581,7 +574,7 @@
}
private boolean mDemoMode;
- private int mDemoInetCondition;
+ private boolean mDemoInetCondition;
private WifiSignalController.WifiState mDemoWifiState;
@Override
@@ -590,7 +583,7 @@
if (DEBUG) Log.d(TAG, "Entering demo mode");
unregisterListeners();
mDemoMode = true;
- mDemoInetCondition = mInetCondition ? 1 : 0;
+ mDemoInetCondition = mInetCondition;
mDemoWifiState = mWifiSignalController.getState();
} else if (mDemoMode && command.equals(COMMAND_EXIT)) {
if (DEBUG) Log.d(TAG, "Exiting demo mode");
@@ -614,10 +607,18 @@
}
String fully = args.getString("fully");
if (fully != null) {
- mDemoInetCondition = Boolean.parseBoolean(fully) ? 1 : 0;
- mWifiSignalController.setInetCondition(mDemoInetCondition);
+ mDemoInetCondition = Boolean.parseBoolean(fully);
+ BitSet connected = new BitSet();
+
+ if (mDemoInetCondition) {
+ connected.set(mWifiSignalController.mTransportType);
+ }
+ mWifiSignalController.updateConnectivity(connected, connected);
for (MobileSignalController controller : mMobileSignalControllers.values()) {
- controller.setInetCondition(mDemoInetCondition, mDemoInetCondition);
+ if (mDemoInetCondition) {
+ connected.set(controller.mTransportType);
+ }
+ controller.updateConnectivity(connected, connected);
}
}
String wifi = args.getString("wifi");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
index 97b530c..e6ca646 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
@@ -22,6 +22,7 @@
import android.util.Log;
import java.io.PrintWriter;
+import java.util.BitSet;
/**
@@ -75,12 +76,8 @@
return mCurrentState;
}
- public int getTransportType() {
- return mTransportType;
- }
-
- public void setInetCondition(int inetCondition) {
- mCurrentState.inetCondition = inetCondition;
+ public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
+ mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0;
notifyListenersIfNecessary();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index 053feb12..fa4d464 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -104,10 +104,7 @@
R.drawable.ic_qs_signal_carrier_network_change_animation }
};
- static final int[] QS_DATA_R = {
- R.drawable.ic_qs_signal_r,
- R.drawable.ic_qs_signal_r
- };
+ static final int QS_DATA_R = R.drawable.ic_qs_signal_r;
//***** Data connection icons
@@ -123,10 +120,7 @@
R.drawable.stat_sys_data_fully_connected_g }
};
- static final int[] QS_DATA_G = {
- R.drawable.ic_qs_signal_g,
- R.drawable.ic_qs_signal_g
- };
+ static final int QS_DATA_G = R.drawable.ic_qs_signal_g;
static final int[][] DATA_3G = {
{ R.drawable.stat_sys_data_fully_connected_3g,
@@ -139,10 +133,7 @@
R.drawable.stat_sys_data_fully_connected_3g }
};
- static final int[] QS_DATA_3G = {
- R.drawable.ic_qs_signal_3g,
- R.drawable.ic_qs_signal_3g
- };
+ static final int QS_DATA_3G = R.drawable.ic_qs_signal_3g;
static final int[][] DATA_E = {
{ R.drawable.stat_sys_data_fully_connected_e,
@@ -155,10 +146,7 @@
R.drawable.stat_sys_data_fully_connected_e }
};
- static final int[] QS_DATA_E = {
- R.drawable.ic_qs_signal_e,
- R.drawable.ic_qs_signal_e
- };
+ static final int QS_DATA_E = R.drawable.ic_qs_signal_e;
//3.5G
static final int[][] DATA_H = {
@@ -172,10 +160,7 @@
R.drawable.stat_sys_data_fully_connected_h }
};
- static final int[] QS_DATA_H = {
- R.drawable.ic_qs_signal_h,
- R.drawable.ic_qs_signal_h
- };
+ static final int QS_DATA_H = R.drawable.ic_qs_signal_h;
//CDMA
// Use 3G icons for EVDO data and 1x icons for 1XRTT data
@@ -190,10 +175,7 @@
R.drawable.stat_sys_data_fully_connected_1x }
};
- static final int[] QS_DATA_1X = {
- R.drawable.ic_qs_signal_1x,
- R.drawable.ic_qs_signal_1x
- };
+ static final int QS_DATA_1X = R.drawable.ic_qs_signal_1x;
// LTE and eHRPD
static final int[][] DATA_4G = {
@@ -207,10 +189,7 @@
R.drawable.stat_sys_data_fully_connected_4g }
};
- static final int[] QS_DATA_4G = {
- R.drawable.ic_qs_signal_4g,
- R.drawable.ic_qs_signal_4g
- };
+ static final int QS_DATA_4G = R.drawable.ic_qs_signal_4g;
// LTE branded "LTE"
static final int[][] DATA_LTE = {
@@ -224,10 +203,7 @@
R.drawable.stat_sys_data_fully_connected_lte }
};
- static final int[] QS_DATA_LTE = {
- R.drawable.ic_qs_signal_lte,
- R.drawable.ic_qs_signal_lte
- };
+ static final int QS_DATA_LTE = R.drawable.ic_qs_signal_lte;
static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_airplane_mode;
static final int ROAMING_ICON = R.drawable.stat_sys_data_fully_connected_roam;
@@ -264,7 +240,7 @@
R.string.accessibility_carrier_network_change_mode,
0,
false,
- null
+ 0
);
static final MobileIconGroup THREE_G = new MobileIconGroup(
@@ -291,7 +267,7 @@
TelephonyIcons.TELEPHONY_NO_NETWORK,
TelephonyIcons.QS_TELEPHONY_NO_NETWORK,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
- 0, 0, false, new int[2]
+ 0, 0, false, 0
);
static final MobileIconGroup E = new MobileIconGroup(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index 1cc6fa8..c14d06f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -20,9 +20,9 @@
import android.test.AndroidTestCase;
import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.NetworkControllerImpl.EmergencyListener;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl.IconState;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 3d5f045..2d6bb68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -34,8 +34,8 @@
import com.android.internal.telephony.cdma.EriInfo;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl.IconState;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
@@ -145,10 +145,10 @@
setLevel(DEFAULT_LEVEL);
updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
TelephonyManager.NETWORK_TYPE_UMTS);
- setConnectivity(100, ConnectivityManager.TYPE_MOBILE, true);
+ setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, true, true);
}
- public void setConnectivity(int inetCondition, int networkType, boolean isConnected) {
+ public void setConnectivity(int networkType, boolean inetCondition, boolean isConnected) {
Intent i = new Intent(ConnectivityManager.INET_CONDITION_ACTION);
// TODO: Separate out into several NetworkCapabilities.
if (isConnected) {
@@ -156,7 +156,7 @@
} else {
mNetCapabilities.removeTransportType(networkType);
}
- if (inetCondition != 0) {
+ if (inetCondition) {
mNetCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
} else {
mNetCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 015127d..15752e1de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -1,17 +1,17 @@
package com.android.systemui.statusbar.policy;
-import org.mockito.Mockito;
-
import android.os.Looper;
import android.telephony.TelephonyManager;
+import org.mockito.Mockito;
+
public class NetworkControllerDataTest extends NetworkControllerBaseTest {
public void test3gDataIcon() {
setupDefaultSignal();
verifyDataIndicators(TelephonyIcons.DATA_3G[1][0 /* No direction */],
- TelephonyIcons.QS_DATA_3G[1]);
+ TelephonyIcons.QS_DATA_3G);
}
public void testRoamingDataIcon() {
@@ -23,7 +23,7 @@
TelephonyIcons.ROAMING_ICON);
verifyLastQsMobileDataIndicators(true,
TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[1][DEFAULT_LEVEL],
- TelephonyIcons.QS_DATA_R[1], false, false);
+ TelephonyIcons.QS_DATA_R, false, false);
}
public void test2gDataIcon() {
@@ -32,7 +32,7 @@
TelephonyManager.NETWORK_TYPE_GSM);
verifyDataIndicators(TelephonyIcons.DATA_G[1][0 /* No direction */],
- TelephonyIcons.QS_DATA_G[1]);
+ TelephonyIcons.QS_DATA_G);
}
public void testCdmaDataIcon() {
@@ -41,7 +41,7 @@
TelephonyManager.NETWORK_TYPE_CDMA);
verifyDataIndicators(TelephonyIcons.DATA_1X[1][0 /* No direction */],
- TelephonyIcons.QS_DATA_1X[1]);
+ TelephonyIcons.QS_DATA_1X);
}
public void testEdgeDataIcon() {
@@ -50,7 +50,7 @@
TelephonyManager.NETWORK_TYPE_EDGE);
verifyDataIndicators(TelephonyIcons.DATA_E[1][0 /* No direction */],
- TelephonyIcons.QS_DATA_E[1]);
+ TelephonyIcons.QS_DATA_E);
}
public void testLteDataIcon() {
@@ -59,7 +59,7 @@
TelephonyManager.NETWORK_TYPE_LTE);
verifyDataIndicators(TelephonyIcons.DATA_LTE[1][0 /* No direction */],
- TelephonyIcons.QS_DATA_LTE[1]);
+ TelephonyIcons.QS_DATA_LTE);
}
public void testHspaDataIcon() {
@@ -68,7 +68,7 @@
TelephonyManager.NETWORK_TYPE_HSPA);
verifyDataIndicators(TelephonyIcons.DATA_H[1][0 /* No direction */],
- TelephonyIcons.QS_DATA_H[1]);
+ TelephonyIcons.QS_DATA_H);
}
public void test4gDataIcon() {
@@ -85,7 +85,7 @@
TelephonyManager.NETWORK_TYPE_LTE);
verifyDataIndicators(TelephonyIcons.DATA_4G[1][0 /* No direction */],
- TelephonyIcons.QS_DATA_4G[1]);
+ TelephonyIcons.QS_DATA_4G);
}
public void test4gDataIconConfigChange() {
@@ -101,7 +101,7 @@
mNetworkController.handleConfigurationChanged();
verifyDataIndicators(TelephonyIcons.DATA_4G[1][0 /* No direction */],
- TelephonyIcons.QS_DATA_4G[1]);
+ TelephonyIcons.QS_DATA_4G);
}
public void testDataActivity() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java
index f1d2acb..5d63d8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java
@@ -2,7 +2,7 @@
import android.net.NetworkCapabilities;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.IconState;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
@@ -26,7 +26,7 @@
}
protected void setEthernetState(boolean connected, boolean validated) {
- setConnectivity(validated ? 100 : 0, NetworkCapabilities.TRANSPORT_ETHERNET, connected);
+ setConnectivity(NetworkCapabilities.TRANSPORT_ETHERNET, validated, connected);
}
protected void verifyLastEthernetIcon(boolean visible, int icon) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index a85bca2..874fdf9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -19,6 +19,7 @@
import android.content.Intent;
import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
import android.os.Looper;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -79,13 +80,12 @@
setLevel(testStrength);
verifyLastMobileDataIndicators(true,
- TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[1][testStrength],
- DEFAULT_ICON);
+ TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[1][testStrength], DEFAULT_ICON);
// Verify low inet number indexing.
- setConnectivity(0, ConnectivityManager.TYPE_MOBILE, true);
+ setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, false, true);
verifyLastMobileDataIndicators(true,
- TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[0][testStrength], 0);
+ TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[0][testStrength], DEFAULT_ICON);
}
}
@@ -154,18 +154,12 @@
}
}
- public void testNoRoamingWithoutSignal() {
+ public void testNoBangWithWifi() {
setupDefaultSignal();
- setCdma();
- setCdmaRoaming(true);
- setVoiceRegState(ServiceState.STATE_OUT_OF_SERVICE);
- setDataRegState(ServiceState.STATE_OUT_OF_SERVICE);
+ setConnectivity(mMobileSignalController.mTransportType, false, false);
+ setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, true, true);
- // This exposes the bug in b/18034542, and should be switched to the commented out
- // verification below (and pass), once the bug is fixed.
- verifyLastMobileDataIndicators(true, R.drawable.stat_sys_signal_null,
- TelephonyIcons.ROAMING_ICON);
- //verifyLastMobileDataIndicators(true, R.drawable.stat_sys_signal_null, 0 /* No Icon */);
+ verifyLastMobileDataIndicators(true, TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[1][2], 0);
}
// Some tests of actual NetworkController code, just internals not display stuff
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index d0c95b9..cecf2fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -1,12 +1,12 @@
package com.android.systemui.statusbar.policy;
import android.content.Intent;
-import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.IconState;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
@@ -27,9 +27,9 @@
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
setWifiLevel(testLevel);
- setConnectivity(100, ConnectivityManager.TYPE_WIFI, true);
+ setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, true, true);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
- setConnectivity(0, ConnectivityManager.TYPE_WIFI, true);
+ setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, false, true);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
}
}
@@ -47,10 +47,10 @@
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
setWifiLevel(testLevel);
- setConnectivity(100, ConnectivityManager.TYPE_WIFI, true);
+ setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, true, true);
verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel],
testSsid);
- setConnectivity(0, ConnectivityManager.TYPE_WIFI, true);
+ setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, false, true);
verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[0][testLevel],
testSsid);
}
@@ -63,7 +63,7 @@
setWifiEnabled(true);
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivity(100, ConnectivityManager.TYPE_WIFI, true);
+ setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, true, true);
verifyLastQsWifiIcon(true, true,
WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel], testSsid);
@@ -84,13 +84,13 @@
setWifiEnabled(true);
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivity(100, ConnectivityManager.TYPE_WIFI, true);
+ setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, true, true);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
setupDefaultSignal();
setGsmRoaming(true);
// Still be on wifi though.
- setConnectivity(100, ConnectivityManager.TYPE_WIFI, true);
+ setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, true, true);
verifyLastMobileDataIndicators(true,
TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[1][DEFAULT_LEVEL],
TelephonyIcons.ROAMING_ICON);
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index ea8b2ec..48e0582 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -21,6 +21,7 @@
import android.graphics.drawable.Drawable;
import android.net.IConnectivityManager;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.text.Html;
import android.text.Html.ImageGetter;
import android.util.Log;
@@ -50,7 +51,7 @@
mService = IConnectivityManager.Stub.asInterface(
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
- if (mService.prepareVpn(mPackage, null)) {
+ if (mService.prepareVpn(mPackage, null, UserHandle.myUserId())) {
setResult(RESULT_OK);
finish();
return;
@@ -94,10 +95,10 @@
@Override
public void onClick(DialogInterface dialog, int which) {
try {
- if (mService.prepareVpn(null, mPackage)) {
+ if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) {
// Authorize this app to initiate VPN connections in the future without user
// intervention.
- mService.setVpnPackageAuthorization(true);
+ mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), true);
setResult(RESULT_OK);
}
} catch (Exception e) {
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index cc8500a..76b2346 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -23,6 +23,7 @@
import android.os.Message;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
@@ -63,7 +64,7 @@
mService = IConnectivityManager.Stub.asInterface(
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
- mConfig = mService.getVpnConfig();
+ mConfig = mService.getVpnConfig(UserHandle.myUserId());
// mConfig can be null if we are a restricted user, in that case don't show this dialog
if (mConfig == null) {
@@ -120,10 +121,11 @@
if (which == DialogInterface.BUTTON_POSITIVE) {
mConfig.configureIntent.send();
} else if (which == DialogInterface.BUTTON_NEUTRAL) {
+ final int myUserId = UserHandle.myUserId();
if (mConfig.legacy) {
- mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+ mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
} else {
- mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN);
+ mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
}
}
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index f5d27f9..c46fa76 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.Manifest;
import android.app.ActivityManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothProfile;
@@ -909,16 +910,22 @@
mCallbacks.finishBroadcast();
}
}
+
public String getAddress() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
- "Need BLUETOOTH permission");
+ "Need BLUETOOTH permission");
if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
- (!checkIfCallerIsForegroundUser())) {
+ (!checkIfCallerIsForegroundUser())) {
Log.w(TAG,"getAddress(): not allowed for non-active and non system user");
return null;
}
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.LOCAL_MAC_ADDRESS)
+ != PackageManager.PERMISSION_GRANTED) {
+ return BluetoothAdapter.DEFAULT_MAC_ADDRESS;
+ }
+
synchronized(mConnection) {
if (mBluetooth != null) {
try {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 02bffc4..1919c37 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1071,23 +1071,6 @@
}
}
- private NetworkCapabilities getNetworkCapabilitiesAndValidation(NetworkAgentInfo nai) {
- if (nai != null) {
- synchronized (nai) {
- if (nai.created) {
- NetworkCapabilities nc = new NetworkCapabilities(nai.networkCapabilities);
- if (nai.lastValidated) {
- nc.addCapability(NET_CAPABILITY_VALIDATED);
- } else {
- nc.removeCapability(NET_CAPABILITY_VALIDATED);
- }
- return nc;
- }
- }
- }
- return null;
- }
-
@Override
public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
// The basic principle is: if an app's traffic could possibly go over a
@@ -1109,7 +1092,7 @@
HashMap<Network, NetworkCapabilities> result = new HashMap<Network, NetworkCapabilities>();
NetworkAgentInfo nai = getDefaultNetwork();
- NetworkCapabilities nc = getNetworkCapabilitiesAndValidation(getDefaultNetwork());
+ NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
if (nc != null) {
result.put(nai.network, nc);
}
@@ -1122,9 +1105,9 @@
if (networks != null) {
for (Network network : networks) {
nai = getNetworkAgentInfoForNetwork(network);
- nc = getNetworkCapabilitiesAndValidation(nai);
+ nc = getNetworkCapabilitiesInternal(nai);
if (nc != null) {
- result.put(nai.network, nc);
+ result.put(network, nc);
}
}
}
@@ -1184,25 +1167,24 @@
return null;
}
- @Override
- public NetworkCapabilities getNetworkCapabilities(Network network) {
- enforceAccessPermission();
- NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) {
if (nai != null) {
synchronized (nai) {
- NetworkCapabilities nc = new NetworkCapabilities(nai.networkCapabilities);
- if (nai.lastValidated) {
- nc.addCapability(NET_CAPABILITY_VALIDATED);
- } else {
- nc.removeCapability(NET_CAPABILITY_VALIDATED);
+ if (nai.networkCapabilities != null) {
+ return new NetworkCapabilities(nai.networkCapabilities);
}
- return nc;
}
}
return null;
}
@Override
+ public NetworkCapabilities getNetworkCapabilities(Network network) {
+ enforceAccessPermission();
+ return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
+ }
+
+ @Override
public NetworkState[] getAllNetworkState() {
// Require internal since we're handing out IMSI details
enforceConnectivityInternalPermission();
@@ -1410,6 +1392,22 @@
}
};
+ /**
+ * Require that the caller is either in the same user or has appropriate permission to interact
+ * across users.
+ *
+ * @param userId Target user for whatever operation the current IPC is supposed to perform.
+ */
+ private void enforceCrossUserPermission(int userId) {
+ if (userId == UserHandle.getCallingUserId()) {
+ // Not a cross-user call.
+ return;
+ }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "ConnectivityService");
+ }
+
private void enforceInternetPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERNET,
@@ -1950,11 +1948,14 @@
}
case NetworkMonitor.EVENT_NETWORK_TESTED: {
NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
- if (isLiveNetworkAgent(nai, "EVENT_NETWORK_VALIDATED")) {
- boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
+ if (isLiveNetworkAgent(nai, "EVENT_NETWORK_TESTED")) {
+ final boolean valid =
+ (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
+ final boolean validationChanged = (valid != nai.lastValidated);
nai.lastValidated = valid;
if (valid) {
if (DBG) log("Validated " + nai.name());
+ nai.networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
if (!nai.everValidated) {
nai.everValidated = true;
rematchNetworkAndRequests(nai, NascentState.JUST_VALIDATED,
@@ -1962,6 +1963,8 @@
// If score has changed, rebroadcast to NetworkFactories. b/17726566
sendUpdatedScoreToFactories(nai);
}
+ } else {
+ nai.networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED);
}
updateInetCondition(nai);
// Let the NetworkAgent know the state of its network
@@ -1970,8 +1973,9 @@
(valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
0, null);
- // TODO: trigger a NetworkCapabilities update so that the dialog can know
- // that the network is now validated and close itself.
+ if (validationChanged) {
+ notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
+ }
}
break;
}
@@ -2945,29 +2949,48 @@
/**
* Prepare for a VPN application.
- * Permissions are checked in Vpn class.
+ * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
+ * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ *
+ * @param oldPackage Package name of the application which currently controls VPN, which will
+ * be replaced. If there is no such application, this should should either be
+ * {@code null} or {@link VpnConfig.LEGACY_VPN}.
+ * @param newPackage Package name of the application which should gain control of VPN, or
+ * {@code null} to disable.
+ * @param userId User for whom to prepare the new VPN.
+ *
* @hide
*/
@Override
- public boolean prepareVpn(String oldPackage, String newPackage) {
+ public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
+ int userId) {
+ enforceCrossUserPermission(userId);
throwIfLockdownEnabled();
- int user = UserHandle.getUserId(Binder.getCallingUid());
+
synchronized(mVpns) {
- return mVpns.get(user).prepare(oldPackage, newPackage);
+ return mVpns.get(userId).prepare(oldPackage, newPackage);
}
}
/**
- * Set whether the current VPN package has the ability to launch VPNs without
- * user intervention. This method is used by system-privileged apps.
- * Permissions are checked in Vpn class.
+ * Set whether the VPN package has the ability to launch VPNs without user intervention.
+ * This method is used by system-privileged apps.
+ * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
+ * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ *
+ * @param packageName The package for which authorization state should change.
+ * @param userId User for whom {@code packageName} is installed.
+ * @param authorized {@code true} if this app should be able to start a VPN connection without
+ * explicit user approval, {@code false} if not.
+ *
* @hide
*/
@Override
- public void setVpnPackageAuthorization(boolean authorized) {
- int user = UserHandle.getUserId(Binder.getCallingUid());
+ public void setVpnPackageAuthorization(String packageName, int userId, boolean authorized) {
+ enforceCrossUserPermission(userId);
+
synchronized(mVpns) {
- mVpns.get(user).setPackageAuthorization(authorized);
+ mVpns.get(userId).setPackageAuthorization(packageName, authorized);
}
}
@@ -3069,16 +3092,16 @@
}
/**
- * Returns the information of the ongoing VPN. This method is used by VpnDialogs and
- * not available in ConnectivityManager.
+ * Returns the information of the ongoing VPN for {@code userId}. This method is used by
+ * VpnDialogs and not available in ConnectivityManager.
* Permissions are checked in Vpn class.
* @hide
*/
@Override
- public VpnConfig getVpnConfig() {
- int user = UserHandle.getUserId(Binder.getCallingUid());
+ public VpnConfig getVpnConfig(int userId) {
+ enforceCrossUserPermission(userId);
synchronized(mVpns) {
- return mVpns.get(user).getVpnConfig();
+ return mVpns.get(userId).getVpnConfig();
}
}
@@ -3534,8 +3557,7 @@
}
private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities) {
- if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
- == false) {
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) == false) {
enforceConnectivityInternalPermission();
} else {
enforceChangePermission();
@@ -3562,8 +3584,7 @@
private void enforceMeteredApnPolicy(NetworkCapabilities networkCapabilities) {
// if UID is restricted, don't allow them to bring up metered APNs
- if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
- == false) {
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) == false) {
final int uidRules;
final int uid = Binder.getCallingUid();
synchronized(mRulesLock) {
@@ -3934,6 +3955,11 @@
synchronized (networkAgent) {
networkAgent.networkCapabilities = networkCapabilities;
}
+ if (networkAgent.lastValidated) {
+ networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
+ // There's no need to remove the capability if we think the network is unvalidated,
+ // because NetworkAgents don't set the validated capability.
+ }
rematchAllNetworksAndRequests(networkAgent, networkAgent.getCurrentScore());
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
@@ -4560,6 +4586,8 @@
@Override
public void factoryReset() {
enforceConnectivityInternalPermission();
+ final int userId = UserHandle.getCallingUserId();
+
// Turn airplane mode off
setAirplaneMode(false);
@@ -4569,16 +4597,16 @@
}
// Turn VPN off
- VpnConfig vpnConfig = getVpnConfig();
+ VpnConfig vpnConfig = getVpnConfig(userId);
if (vpnConfig != null) {
if (vpnConfig.legacy) {
- prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+ prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
} else {
- // Prevent this app from initiating VPN connections in the future without
- // user intervention.
- setVpnPackageAuthorization(false);
+ // Prevent this app (packagename = vpnConfig.user) from initiating VPN connections
+ // in the future without user intervention.
+ setVpnPackageAuthorization(vpnConfig.user, userId, false);
- prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN);
+ prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN, userId);
}
}
}
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index ef9d0c3..4b65dec 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -72,7 +72,9 @@
# when a notification action button has been clicked
27521 notification_action_clicked (key|3),(action_index|1)
# when a notification has been canceled
-27530 notification_canceled (key|3),(reason|1),(lifespan|1)
+27530 notification_canceled (key|3),(reason|1),(lifespan|1),(exposure|1)
+# replaces 27510 with a row per notification
+27531 notification_visibility (key|3),(visibile|1),(lifespan|1),(freshness|1)
# ---------------------------
# Watchdog.java
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a8ab667..2d74618 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6164,17 +6164,6 @@
}
}
- @Override
- public void systemBackupRestored() {
- synchronized (this) {
- if (mSystemReady) {
- mTaskPersister.restoreTasksFromOtherDeviceLocked();
- } else {
- Slog.w(TAG, "System backup restored before system is ready");
- }
- }
- }
-
final void ensureBootCompleted() {
boolean booting;
boolean enableScreen;
@@ -11327,7 +11316,6 @@
mRecentTasks.clear();
mRecentTasks.addAll(mTaskPersister.restoreTasksLocked());
- mTaskPersister.restoreTasksFromOtherDeviceLocked();
mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
mTaskPersister.startPersisting();
@@ -16191,18 +16179,12 @@
removeUriPermissionsForPackageLocked(ssp, userId, true);
removeTasksByPackageNameLocked(ssp, userId);
- if (userId == UserHandle.USER_OWNER) {
- mTaskPersister.removeFromPackageCache(ssp);
- }
mBatteryStatsService.notePackageUninstalled(ssp);
}
} else {
cleanupDisabledPackageComponentsLocked(ssp, userId,
intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST));
- if (userId == UserHandle.USER_OWNER) {
- mTaskPersister.addOtherDeviceTasksToRecentsLocked(ssp);
- }
}
}
break;
@@ -16217,9 +16199,6 @@
intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
mCompatModePackages.handlePackageAddedLocked(ssp, replacing);
- if (userId == UserHandle.USER_OWNER) {
- mTaskPersister.addOtherDeviceTasksToRecentsLocked(ssp);
- }
try {
ApplicationInfo ai = AppGlobals.getPackageManager().
getApplicationInfo(ssp, 0, 0);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index f3b18f5..ca1fd6a 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,8 +17,6 @@
package com.android.server.am;
import static com.android.server.am.ActivityManagerDebugConfig.*;
-import static com.android.server.am.TaskPersister.DEBUG_PERSISTER;
-import static com.android.server.am.TaskPersister.DEBUG_RESTORER;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import android.app.ActivityManager.TaskDescription;
@@ -85,7 +83,7 @@
private static final String ATTR_USERID = "user_id";
private static final String TAG_PERSISTABLEBUNDLE = "persistable_bundle";
private static final String ATTR_LAUNCHEDFROMUID = "launched_from_uid";
- static final String ATTR_LAUNCHEDFROMPACKAGE = "launched_from_package";
+ private static final String ATTR_LAUNCHEDFROMPACKAGE = "launched_from_package";
private static final String ATTR_RESOLVEDTYPE = "resolved_type";
private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
@@ -94,7 +92,7 @@
final IApplicationToken.Stub appToken; // window manager token
final ActivityInfo info; // all about me
final ApplicationInfo appInfo; // information about activity's app
- int launchedFromUid; // always the uid who started the activity.
+ final int launchedFromUid; // always the uid who started the activity.
final String launchedFromPackage; // always the package who started the activity.
final int userId; // Which user is this running for?
final Intent intent; // the original intent that generated us
@@ -1178,8 +1176,8 @@
}
}
- static ActivityRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
- throws IOException, XmlPullParserException {
+ static ActivityRecord restoreFromXml(XmlPullParser in,
+ ActivityStackSupervisor stackSupervisor) throws IOException, XmlPullParserException {
Intent intent = null;
PersistableBundle persistentState = null;
int launchedFromUid = 0;
@@ -1194,7 +1192,7 @@
for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
final String attrName = in.getAttributeName(attrNdx);
final String attrValue = in.getAttributeValue(attrNdx);
- if (DEBUG_PERSISTER || DEBUG_RESTORER) Slog.d(TaskPersister.TAG,
+ if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
"ActivityRecord: attribute name=" + attrName + " value=" + attrValue);
if (ATTR_ID.equals(attrName)) {
createTime = Long.valueOf(attrValue);
@@ -1220,15 +1218,15 @@
(event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
if (event == XmlPullParser.START_TAG) {
final String name = in.getName();
- if (DEBUG_PERSISTER || DEBUG_RESTORER)
+ if (TaskPersister.DEBUG)
Slog.d(TaskPersister.TAG, "ActivityRecord: START_TAG name=" + name);
if (TAG_INTENT.equals(name)) {
intent = Intent.restoreFromXml(in);
- if (DEBUG_PERSISTER || DEBUG_RESTORER)
+ if (TaskPersister.DEBUG)
Slog.d(TaskPersister.TAG, "ActivityRecord: intent=" + intent);
} else if (TAG_PERSISTABLEBUNDLE.equals(name)) {
persistentState = PersistableBundle.restoreFromXml(in);
- if (DEBUG_PERSISTER || DEBUG_RESTORER) Slog.d(TaskPersister.TAG,
+ if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
"ActivityRecord: persistentState=" + persistentState);
} else {
Slog.w(TAG, "restoreActivity: unexpected name=" + name);
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 318cd45..ef1559a 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -59,8 +59,7 @@
public class TaskPersister {
static final String TAG = "TaskPersister";
- static final boolean DEBUG_PERSISTER = false;
- static final boolean DEBUG_RESTORER = false;
+ static final boolean DEBUG = false;
/** When not flushing don't write out files faster than this */
private static final long INTER_WRITE_DELAY_MS = 500;
@@ -81,21 +80,10 @@
private static final String IMAGES_DIRNAME = "recent_images";
static final String IMAGE_EXTENSION = ".png";
- // Directory where restored historical task XML/PNG files are placed. This directory
- // contains subdirs named after TASKS_DIRNAME and IMAGES_DIRNAME mirroring the
- // ancestral device's dataset. This needs to match the RECENTS_TASK_RESTORE_DIR
- // value in RecentsBackupHelper.
- private static final String RESTORED_TASKS_DIRNAME = "restored_" + TASKS_DIRNAME;
-
- // Max time to wait for the application/package of a restored task to be installed
- // before giving up.
- private static final long MAX_INSTALL_WAIT_TIME = DateUtils.DAY_IN_MILLIS;
-
private static final String TAG_TASK = "task";
static File sImagesDir;
static File sTasksDir;
- static File sRestoredTasksDir;
private final ActivityManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
@@ -129,23 +117,11 @@
ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<WriteQueueItem>();
- // Map of tasks that were backed-up on a different device that can be restored on this device.
- // Data organization: <packageNameOfAffiliateTask, listOfAffiliatedTasksChains>
- private ArrayMap<String, List<List<OtherDeviceTask>>> mOtherDeviceTasksMap =
- new ArrayMap<>(10);
- // Local cache of package names to uid used when restoring a task from another device.
- private ArrayMap<String, Integer> mPackageUidMap;
-
- // The next time in milliseconds we will remove expired task from
- // {@link #mOtherDeviceTasksMap} and disk. Set to {@link Long.MAX_VALUE} to never clean-up
- // tasks.
- private long mExpiredTasksCleanupTime = Long.MAX_VALUE;
-
TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
RecentTasks recentTasks) {
sTasksDir = new File(systemDir, TASKS_DIRNAME);
if (!sTasksDir.exists()) {
- if (DEBUG_PERSISTER) Slog.d(TAG, "Creating tasks directory " + sTasksDir);
+ if (DEBUG) Slog.d(TAG, "Creating tasks directory " + sTasksDir);
if (!sTasksDir.mkdir()) {
Slog.e(TAG, "Failure creating tasks directory " + sTasksDir);
}
@@ -153,14 +129,12 @@
sImagesDir = new File(systemDir, IMAGES_DIRNAME);
if (!sImagesDir.exists()) {
- if (DEBUG_PERSISTER) Slog.d(TAG, "Creating images directory " + sTasksDir);
+ if (DEBUG) Slog.d(TAG, "Creating images directory " + sTasksDir);
if (!sImagesDir.mkdir()) {
Slog.e(TAG, "Failure creating images directory " + sImagesDir);
}
}
- sRestoredTasksDir = new File(systemDir, RESTORED_TASKS_DIRNAME);
-
mStackSupervisor = stackSupervisor;
mService = stackSupervisor.mService;
mRecentTasks = recentTasks;
@@ -179,8 +153,8 @@
final WriteQueueItem item = mWriteQueue.get(queueNdx);
if (item instanceof ImageWriteQueueItem &&
((ImageWriteQueueItem) item).mFilename.startsWith(taskString)) {
- if (DEBUG_PERSISTER) Slog.d(TAG, "Removing "
- + ((ImageWriteQueueItem) item).mFilename + " from write queue");
+ if (DEBUG) Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilename +
+ " from write queue");
mWriteQueue.remove(queueNdx);
}
}
@@ -225,9 +199,9 @@
} else if (mNextWriteTime == 0) {
mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
}
- if (DEBUG_PERSISTER) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush
- + " mNextWriteTime=" + mNextWriteTime + " mWriteQueue.size="
- + mWriteQueue.size() + " Callers=" + Debug.getCallers(4));
+ if (DEBUG) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " mNextWriteTime="
+ + mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size()
+ + " Callers=" + Debug.getCallers(4));
notifyAll();
}
@@ -269,7 +243,7 @@
} else if (mNextWriteTime == 0) {
mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
}
- if (DEBUG_PERSISTER) Slog.d(TAG, "saveImage: filename=" + filename + " now=" +
+ if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename + " now=" +
SystemClock.uptimeMillis() + " mNextWriteTime=" +
mNextWriteTime + " Callers=" + Debug.getCallers(4));
notifyAll();
@@ -303,12 +277,12 @@
}
private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
- if (DEBUG_PERSISTER) Slog.d(TAG, "saveToXml: task=" + task);
+ if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
final XmlSerializer xmlSerializer = new FastXmlSerializer();
StringWriter stringWriter = new StringWriter();
xmlSerializer.setOutput(stringWriter);
- if (DEBUG_PERSISTER) xmlSerializer.setFeature(
+ if (DEBUG) xmlSerializer.setFeature(
"http://xmlpull.org/v1/doc/features.html#indent-output", true);
// save task
@@ -367,7 +341,7 @@
for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
File taskFile = recentFiles[taskNdx];
- if (DEBUG_PERSISTER) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName());
+ if (DEBUG) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName());
BufferedReader reader = null;
boolean deleteFile = false;
try {
@@ -380,12 +354,11 @@
event != XmlPullParser.END_TAG) {
final String name = in.getName();
if (event == XmlPullParser.START_TAG) {
- if (DEBUG_PERSISTER)
- Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name);
+ if (DEBUG) Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name);
if (TAG_TASK.equals(name)) {
final TaskRecord task =
TaskRecord.restoreFromXml(in, mStackSupervisor);
- if (DEBUG_PERSISTER) Slog.d(TAG, "restoreTasksLocked: restored task=" +
+ if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" +
task);
if (task != null) {
task.isPersistable = true;
@@ -414,15 +387,14 @@
deleteFile = true;
} finally {
IoUtils.closeQuietly(reader);
- if (!DEBUG_PERSISTER && deleteFile) {
- if (true || DEBUG_PERSISTER)
- Slog.d(TAG, "Deleting file=" + taskFile.getName());
+ if (deleteFile) {
+ if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
taskFile.delete();
}
}
}
- if (!DEBUG_PERSISTER) {
+ if (!DEBUG) {
removeObsoleteFiles(recoveredTaskIds);
}
@@ -453,8 +425,8 @@
}
private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
- if (DEBUG_PERSISTER) Slog.d(TAG, "removeObsoleteFile: persistentTaskIds="
- + persistentTaskIds + " files=" + files);
+ if (DEBUG) Slog.d(TAG, "removeObsoleteFile: persistentTaskIds=" + persistentTaskIds +
+ " files=" + files);
if (files == null) {
Slog.e(TAG, "File error accessing recents directory (too many files open?).");
return;
@@ -467,15 +439,14 @@
final int taskId;
try {
taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
- if (DEBUG_PERSISTER) Slog.d(TAG, "removeObsoleteFile: Found taskId=" + taskId);
+ if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Found taskId=" + taskId);
} catch (Exception e) {
Slog.wtf(TAG, "removeObsoleteFile: Can't parse file=" + file.getName());
file.delete();
continue;
}
if (!persistentTaskIds.contains(taskId)) {
- if (true || DEBUG_PERSISTER) Slog.d(TAG, "removeObsoleteFile: deleting file=" +
- file.getName());
+ if (DEBUG) Slog.d(TAG, "removeObsoleteFile: deleting file=" + file.getName());
file.delete();
}
}
@@ -488,441 +459,10 @@
}
static Bitmap restoreImage(String filename) {
- if (DEBUG_PERSISTER) Slog.d(TAG, "restoreImage: restoring " + filename);
+ if (DEBUG) Slog.d(TAG, "restoreImage: restoring " + filename);
return BitmapFactory.decodeFile(sImagesDir + File.separator + filename);
}
- /**
- * Tries to restore task that were backed-up on a different device onto this device.
- */
- void restoreTasksFromOtherDeviceLocked() {
- readOtherDeviceTasksFromDisk();
- addOtherDeviceTasksToRecentsLocked();
- }
-
- /**
- * Read the tasks that were backed-up on a different device and can be restored to this device
- * from disk and populated {@link #mOtherDeviceTasksMap} with the information. Also sets up
- * time to clear out other device tasks that have not been restored on this device
- * within the allotted time.
- */
- private void readOtherDeviceTasksFromDisk() {
- synchronized (mOtherDeviceTasksMap) {
- // Clear out current map and expiration time.
- mOtherDeviceTasksMap.clear();
- mExpiredTasksCleanupTime = Long.MAX_VALUE;
-
- final File[] taskFiles;
- if (!sRestoredTasksDir.exists()
- || (taskFiles = sRestoredTasksDir.listFiles()) == null) {
- // Nothing to do if there are no tasks to restore.
- return;
- }
-
- long earliestMtime = System.currentTimeMillis();
- SparseArray<List<OtherDeviceTask>> tasksByAffiliateIds =
- new SparseArray<>(taskFiles.length);
-
- // Read new tasks from disk
- for (int i = 0; i < taskFiles.length; ++i) {
- final File taskFile = taskFiles[i];
- if (DEBUG_RESTORER) Slog.d(TAG, "readOtherDeviceTasksFromDisk: taskFile="
- + taskFile.getName());
-
- final OtherDeviceTask task = OtherDeviceTask.createFromFile(taskFile);
-
- if (task == null) {
- // Go ahead and remove the file on disk if we are unable to create a task from
- // it.
- if (DEBUG_RESTORER) Slog.e(TAG, "Unable to create task for file="
- + taskFile.getName() + "...deleting file.");
- taskFile.delete();
- continue;
- }
-
- List<OtherDeviceTask> tasks = tasksByAffiliateIds.get(task.mAffiliatedTaskId);
- if (tasks == null) {
- tasks = new ArrayList<>();
- tasksByAffiliateIds.put(task.mAffiliatedTaskId, tasks);
- }
- tasks.add(task);
- final long taskMtime = taskFile.lastModified();
- if (earliestMtime > taskMtime) {
- earliestMtime = taskMtime;
- }
- }
-
- if (tasksByAffiliateIds.size() > 0) {
- // Sort each affiliated tasks chain by taskId which is the order they were created
- // that should always be correct...Then add to task map.
- for (int i = 0; i < tasksByAffiliateIds.size(); i++) {
- List<OtherDeviceTask> chain = tasksByAffiliateIds.valueAt(i);
- Collections.sort(chain);
- // Package name of the root task in the affiliate chain.
- final String packageName =
- chain.get(chain.size()-1).mComponentName.getPackageName();
- List<List<OtherDeviceTask>> chains = mOtherDeviceTasksMap.get(packageName);
- if (chains == null) {
- chains = new ArrayList<>();
- mOtherDeviceTasksMap.put(packageName, chains);
- }
- chains.add(chain);
- }
-
- // Set expiration time.
- mExpiredTasksCleanupTime = earliestMtime + MAX_INSTALL_WAIT_TIME;
- if (DEBUG_RESTORER) Slog.d(TAG, "Set Expiration time to "
- + DateUtils.formatDateTime(mService.mContext, mExpiredTasksCleanupTime,
- DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME));
- }
- }
- }
-
- /**
- * Removed any expired tasks from {@link #mOtherDeviceTasksMap} and disk if their expiration
- * time is less than or equal to {@link #mExpiredTasksCleanupTime}.
- */
- private void removeExpiredTasksIfNeeded() {
- synchronized (mOtherDeviceTasksMap) {
- final long now = System.currentTimeMillis();
- final boolean noMoreTasks = mOtherDeviceTasksMap.isEmpty();
- if (noMoreTasks || now < mExpiredTasksCleanupTime) {
- if (noMoreTasks && mPackageUidMap != null) {
- // All done! package->uid map no longer needed.
- mPackageUidMap = null;
- }
- return;
- }
-
- long earliestNonExpiredMtime = now;
- mExpiredTasksCleanupTime = Long.MAX_VALUE;
-
- // Remove expired backed-up tasks that have not been restored. We only want to
- // remove task if it is safe to remove all tasks in the affiliation chain.
- for (int i = mOtherDeviceTasksMap.size() - 1; i >= 0 ; i--) {
-
- List<List<OtherDeviceTask>> chains = mOtherDeviceTasksMap.valueAt(i);
- for (int j = chains.size() - 1; j >= 0 ; j--) {
-
- List<OtherDeviceTask> chain = chains.get(j);
- boolean removeChain = true;
- for (int k = chain.size() - 1; k >= 0 ; k--) {
- OtherDeviceTask task = chain.get(k);
- final long taskLastModified = task.mFile.lastModified();
- if ((taskLastModified + MAX_INSTALL_WAIT_TIME) > now) {
- // File has not expired yet...but we keep looping to get the earliest
- // mtime.
- if (earliestNonExpiredMtime > taskLastModified) {
- earliestNonExpiredMtime = taskLastModified;
- }
- removeChain = false;
- }
- }
- if (removeChain) {
- for (int k = chain.size() - 1; k >= 0; k--) {
- final File file = chain.get(k).mFile;
- if (DEBUG_RESTORER) Slog.d(TAG, "Deleting expired file="
- + file.getName() + " mapped to not installed component="
- + chain.get(k).mComponentName);
- file.delete();
- }
- chains.remove(j);
- }
- }
- if (chains.isEmpty()) {
- final String packageName = mOtherDeviceTasksMap.keyAt(i);
- mOtherDeviceTasksMap.removeAt(i);
- if (DEBUG_RESTORER) Slog.d(TAG, "Removed package=" + packageName
- + " from task map");
- }
- }
-
- // Reset expiration time if there is any task remaining.
- if (!mOtherDeviceTasksMap.isEmpty()) {
- mExpiredTasksCleanupTime = earliestNonExpiredMtime + MAX_INSTALL_WAIT_TIME;
- if (DEBUG_RESTORER) Slog.d(TAG, "Reset expiration time to "
- + DateUtils.formatDateTime(mService.mContext, mExpiredTasksCleanupTime,
- DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME));
- } else {
- // All done! package->uid map no longer needed.
- mPackageUidMap = null;
- }
- }
- }
-
- /**
- * Removes the input package name from the local package->uid map.
- */
- void removeFromPackageCache(String packageName) {
- synchronized (mOtherDeviceTasksMap) {
- if (mPackageUidMap != null) {
- mPackageUidMap.remove(packageName);
- }
- }
- }
-
- /**
- * Tries to add all backed-up tasks from another device to this device recent's list.
- */
- private void addOtherDeviceTasksToRecentsLocked() {
- synchronized (mOtherDeviceTasksMap) {
- for (int i = mOtherDeviceTasksMap.size() - 1; i >= 0; i--) {
- addOtherDeviceTasksToRecentsLocked(mOtherDeviceTasksMap.keyAt(i));
- }
- }
- }
-
- /**
- * Tries to add backed-up tasks that are associated with the input package from
- * another device to this device recent's list.
- */
- void addOtherDeviceTasksToRecentsLocked(String packageName) {
- synchronized (mOtherDeviceTasksMap) {
- List<List<OtherDeviceTask>> chains = mOtherDeviceTasksMap.get(packageName);
- if (chains == null) {
- return;
- }
-
- for (int i = chains.size() - 1; i >= 0; i--) {
- List<OtherDeviceTask> chain = chains.get(i);
- if (!canAddOtherDeviceTaskChain(chain)) {
- if (DEBUG_RESTORER) Slog.d(TAG, "Can't add task chain at index=" + i
- + " for package=" + packageName);
- continue;
- }
-
- // Generate task records for this chain.
- List<TaskRecord> tasks = new ArrayList<>();
- TaskRecord prev = null;
- for (int j = chain.size() - 1; j >= 0; j--) {
- TaskRecord task = createTaskRecordLocked(chain.get(j));
- if (task == null) {
- // There was a problem in creating one of this task records in this chain.
- // There is no way we can continue...
- if (DEBUG_RESTORER) Slog.d(TAG, "Can't create task record for file="
- + chain.get(j).mFile + " for package=" + packageName);
- break;
- }
-
- // Wire-up affiliation chain.
- if (prev == null) {
- task.mPrevAffiliate = null;
- task.mPrevAffiliateTaskId = INVALID_TASK_ID;
- task.mAffiliatedTaskId = task.taskId;
- } else {
- prev.mNextAffiliate = task;
- prev.mNextAffiliateTaskId = task.taskId;
- task.mAffiliatedTaskId = prev.mAffiliatedTaskId;
- task.mPrevAffiliate = prev;
- task.mPrevAffiliateTaskId = prev.taskId;
- }
- prev = task;
- tasks.add(0, task);
- }
-
- // Add tasks to recent's if we were able to create task records for all the tasks
- // in the chain.
- if (tasks.size() == chain.size()) {
- // Make sure there is space in recent's to add the new task. If there is space
- // to the to the back.
- // TODO: Would be more fancy to interleave the new tasks into recent's based on
- // {@link TaskRecord.mLastTimeMoved} and drop the oldest recent's vs. just
- // adding to the back of the list.
- int spaceLeft =
- ActivityManager.getMaxRecentTasksStatic() - mRecentTasks.size();
- if (spaceLeft >= tasks.size()) {
- mRecentTasks.addAll(mRecentTasks.size(), tasks);
- for (int k = tasks.size() - 1; k >= 0; k--) {
- // Persist new tasks.
- wakeup(tasks.get(k), false);
- }
-
- if (DEBUG_RESTORER) Slog.d(TAG, "Added " + tasks.size()
- + " tasks to recent's for" + " package=" + packageName);
- } else {
- if (DEBUG_RESTORER) Slog.d(TAG, "Didn't add to recents. tasks.size("
- + tasks.size() + ") != chain.size(" + chain.size()
- + ") for package=" + packageName);
- }
- } else {
- if (DEBUG_RESTORER) Slog.v(TAG, "Unable to add restored tasks to recents "
- + tasks.size() + " tasks for package=" + packageName);
- }
-
- // Clean-up structures
- for (int j = chain.size() - 1; j >= 0; j--) {
- chain.get(j).mFile.delete();
- }
- chains.remove(i);
- if (chains.isEmpty()) {
- // The fate of all backed-up tasks associated with this package has been
- // determine. Go ahead and remove it from the to-process list.
- mOtherDeviceTasksMap.remove(packageName);
- if (DEBUG_RESTORER)
- Slog.d(TAG, "Removed package=" + packageName + " from restore map");
- }
- }
- }
- }
-
- /**
- * Creates and returns {@link TaskRecord} for the task from another device that can be used on
- * this device. Returns null if the operation failed.
- */
- private TaskRecord createTaskRecordLocked(OtherDeviceTask other) {
- File file = other.mFile;
- BufferedReader reader = null;
- TaskRecord task = null;
- if (DEBUG_RESTORER) Slog.d(TAG, "createTaskRecordLocked: file=" + file.getName());
-
- try {
- reader = new BufferedReader(new FileReader(file));
- final XmlPullParser in = Xml.newPullParser();
- in.setInput(reader);
-
- int event;
- while (((event = in.next()) != XmlPullParser.END_DOCUMENT)
- && event != XmlPullParser.END_TAG) {
- final String name = in.getName();
- if (event == XmlPullParser.START_TAG) {
-
- if (TAG_TASK.equals(name)) {
- // Create a task record using a task id that is valid for this device.
- task = TaskRecord.restoreFromXml(
- in, mStackSupervisor, mStackSupervisor.getNextTaskId());
- if (DEBUG_RESTORER)
- Slog.d(TAG, "createTaskRecordLocked: restored task=" + task);
-
- if (task != null) {
- task.isPersistable = true;
- task.inRecents = true;
- // Task can/should only be backed-up/restored for device owner.
- task.userId = UserHandle.USER_OWNER;
- // Clear out affiliated ids that are no longer valid on this device.
- task.mAffiliatedTaskId = INVALID_TASK_ID;
- task.mPrevAffiliateTaskId = INVALID_TASK_ID;
- task.mNextAffiliateTaskId = INVALID_TASK_ID;
- // Set up uids valid for this device.
- Integer uid = mPackageUidMap.get(task.realActivity.getPackageName());
- if (uid == null) {
- // How did this happen???
- Slog.wtf(TAG, "Can't find uid for task=" + task
- + " in mPackageUidMap=" + mPackageUidMap);
- return null;
- }
- task.effectiveUid = task.mCallingUid = uid;
- for (int i = task.mActivities.size() - 1; i >= 0; --i) {
- final ActivityRecord activity = task.mActivities.get(i);
- uid = mPackageUidMap.get(activity.launchedFromPackage);
- if (uid == null) {
- // How did this happen??
- Slog.wtf(TAG, "Can't find uid for activity=" + activity
- + " in mPackageUidMap=" + mPackageUidMap);
- return null;
- }
- activity.launchedFromUid = uid;
- }
-
- } else {
- Slog.e(TAG, "Unable to create task for backed-up file=" + file + ": "
- + fileToString(file));
- }
- } else {
- Slog.wtf(TAG, "createTaskRecordLocked Unknown xml event=" + event
- + " name=" + name);
- }
- }
- XmlUtils.skipCurrentTag(in);
- }
- } catch (Exception e) {
- Slog.wtf(TAG, "Unable to parse " + file + ". Error ", e);
- Slog.e(TAG, "Failing file: " + fileToString(file));
- } finally {
- IoUtils.closeQuietly(reader);
- }
-
- return task;
- }
-
- /**
- * Returns true if the input task chain backed-up from another device can be restored on this
- * device. Also, sets the {@link OtherDeviceTask#mUid} on the input tasks if they can be
- * restored.
- */
- private boolean canAddOtherDeviceTaskChain(List<OtherDeviceTask> chain) {
-
- final ArraySet<ComponentName> validComponents = new ArraySet<>();
- final IPackageManager pm = AppGlobals.getPackageManager();
- for (int i = 0; i < chain.size(); i++) {
-
- OtherDeviceTask task = chain.get(i);
- // Quick check, we can't add the task chain if any of its task files don't exist.
- if (!task.mFile.exists()) {
- if (DEBUG_RESTORER) Slog.d(TAG,
- "Can't add chain due to missing file=" + task.mFile);
- return false;
- }
-
- // Verify task package is installed.
- if (!isPackageInstalled(task.mComponentName.getPackageName())) {
- return false;
- }
- // Verify that all the launch packages are installed.
- if (task.mLaunchPackages != null) {
- for (int j = task.mLaunchPackages.size() - 1; j >= 0; --j) {
- if (!isPackageInstalled(task.mLaunchPackages.valueAt(j))) {
- return false;
- }
- }
- }
-
- if (validComponents.contains(task.mComponentName)) {
- // Existance of component has already been verified.
- continue;
- }
-
- // Check to see if the specific component is installed.
- try {
- if (pm.getActivityInfo(task.mComponentName, 0, UserHandle.USER_OWNER) == null) {
- // Component isn't installed...
- return false;
- }
- validComponents.add(task.mComponentName);
- } catch (RemoteException e) {
- // Should not happen???
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Returns true if the input package name is installed. If the package is installed, an entry
- * for the package is added to {@link #mPackageUidMap}.
- */
- private boolean isPackageInstalled(final String packageName) {
- if (mPackageUidMap != null && mPackageUidMap.containsKey(packageName)) {
- return true;
- }
- try {
- int uid = AppGlobals.getPackageManager().getPackageUid(
- packageName, UserHandle.USER_OWNER);
- if (uid == -1) {
- // package doesn't exist...
- return false;
- }
- if (mPackageUidMap == null) {
- mPackageUidMap = new ArrayMap<>();
- }
- mPackageUidMap.put(packageName, uid);
- return true;
- } catch (RemoteException e) {
- // Should not happen???
- return false;
- }
- }
-
private class LazyTaskWriterThread extends Thread {
LazyTaskWriterThread(String name) {
@@ -941,21 +481,20 @@
probablyDone = mWriteQueue.isEmpty();
}
if (probablyDone) {
- if (DEBUG_PERSISTER) Slog.d(TAG, "Looking for obsolete files.");
+ if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
persistentTaskIds.clear();
synchronized (mService) {
- if (DEBUG_PERSISTER) Slog.d(TAG, "mRecents=" + mRecentTasks);
+ if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
for (int taskNdx = mRecentTasks.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mRecentTasks.get(taskNdx);
- if (DEBUG_PERSISTER) Slog.d(TAG, "LazyTaskWriter: task=" + task +
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task +
" persistable=" + task.isPersistable);
if ((task.isPersistable || task.inRecents)
&& (task.stack == null || !task.stack.isHomeStack())) {
- if (DEBUG_PERSISTER)
- Slog.d(TAG, "adding to persistentTaskIds task=" + task);
+ if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
persistentTaskIds.add(task.taskId);
} else {
- if (DEBUG_PERSISTER) Slog.d(TAG,
+ if (DEBUG) Slog.d(TAG,
"omitting from persistentTaskIds task=" + task);
}
}
@@ -969,7 +508,7 @@
if (mNextWriteTime != FLUSH_QUEUE) {
// The next write we don't have to wait so long.
mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
- if (DEBUG_PERSISTER) Slog.d(TAG, "Next write time may be in " +
+ if (DEBUG) Slog.d(TAG, "Next write time may be in " +
INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
}
@@ -979,13 +518,8 @@
mNextWriteTime = 0; // idle.
TaskPersister.this.notifyAll(); // wake up flush() if needed.
}
-
- // See if we need to remove any expired back-up tasks before waiting.
- removeExpiredTasksIfNeeded();
-
try {
- if (DEBUG_PERSISTER)
- Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
TaskPersister.this.wait();
} catch (InterruptedException e) {
}
@@ -995,12 +529,11 @@
item = mWriteQueue.remove(0);
long now = SystemClock.uptimeMillis();
- if (DEBUG_PERSISTER) Slog.d(TAG, "LazyTaskWriter: now=" + now
- + " mNextWriteTime=" + mNextWriteTime + " mWriteQueue.size="
- + mWriteQueue.size());
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
+ mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
while (now < mNextWriteTime) {
try {
- if (DEBUG_PERSISTER) Slog.d(TAG, "LazyTaskWriter: waiting " +
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
(mNextWriteTime - now));
TaskPersister.this.wait(mNextWriteTime - now);
} catch (InterruptedException e) {
@@ -1015,7 +548,7 @@
ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
final String filename = imageWriteQueueItem.mFilename;
final Bitmap bitmap = imageWriteQueueItem.mImage;
- if (DEBUG_PERSISTER) Slog.d(TAG, "writing bitmap: filename=" + filename);
+ if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filename);
FileOutputStream imageFile = null;
try {
imageFile = new FileOutputStream(new File(sImagesDir, filename));
@@ -1029,12 +562,12 @@
// Write out one task.
StringWriter stringWriter = null;
TaskRecord task = ((TaskWriteQueueItem) item).mTask;
- if (DEBUG_PERSISTER) Slog.d(TAG, "Writing task=" + task);
+ if (DEBUG) Slog.d(TAG, "Writing task=" + task);
synchronized (mService) {
if (task.inRecents) {
// Still there.
try {
- if (DEBUG_PERSISTER) Slog.d(TAG, "Saving task=" + task);
+ if (DEBUG) Slog.d(TAG, "Saving task=" + task);
stringWriter = saveToXml(task);
} catch (IOException e) {
} catch (XmlPullParserException e) {
@@ -1064,127 +597,4 @@
}
}
}
-
- /**
- * Helper class for holding essential information about task that were backed-up on a different
- * device that can be restored on this device.
- */
- private static class OtherDeviceTask implements Comparable<OtherDeviceTask> {
- final File mFile;
- // See {@link TaskRecord} for information on the fields below.
- final ComponentName mComponentName;
- final int mTaskId;
- final int mAffiliatedTaskId;
-
- // Names of packages that launched activities in this task. All packages listed here need
- // to be installed on the current device in order for the task to be restored successfully.
- final ArraySet<String> mLaunchPackages;
-
- private OtherDeviceTask(File file, ComponentName componentName, int taskId,
- int affiliatedTaskId, ArraySet<String> launchPackages) {
- mFile = file;
- mComponentName = componentName;
- mTaskId = taskId;
- mAffiliatedTaskId = (affiliatedTaskId == INVALID_TASK_ID) ? taskId: affiliatedTaskId;
- mLaunchPackages = launchPackages;
- }
-
- @Override
- public int compareTo(OtherDeviceTask another) {
- return mTaskId - another.mTaskId;
- }
-
- /**
- * Creates a new {@link OtherDeviceTask} object based on the contents of the input file.
- *
- * @param file input file that contains the complete task information.
- * @return new {@link OtherDeviceTask} object or null if we failed to create the object.
- */
- static OtherDeviceTask createFromFile(File file) {
- if (file == null || !file.exists()) {
- if (DEBUG_RESTORER)
- Slog.d(TAG, "createFromFile: file=" + file + " doesn't exist.");
- return null;
- }
-
- BufferedReader reader = null;
-
- try {
- reader = new BufferedReader(new FileReader(file));
- final XmlPullParser in = Xml.newPullParser();
- in.setInput(reader);
-
- int event;
- while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
- event != XmlPullParser.START_TAG) {
- // Skip to the start tag or end of document
- }
-
- if (event == XmlPullParser.START_TAG) {
- final String name = in.getName();
-
- if (TAG_TASK.equals(name)) {
- final int outerDepth = in.getDepth();
- ComponentName componentName = null;
- int taskId = INVALID_TASK_ID;
- int taskAffiliation = INVALID_TASK_ID;
- for (int j = in.getAttributeCount() - 1; j >= 0; --j) {
- final String attrName = in.getAttributeName(j);
- final String attrValue = in.getAttributeValue(j);
- if (TaskRecord.ATTR_REALACTIVITY.equals(attrName)) {
- componentName = ComponentName.unflattenFromString(attrValue);
- } else if (TaskRecord.ATTR_TASKID.equals(attrName)) {
- taskId = Integer.valueOf(attrValue);
- } else if (TaskRecord.ATTR_TASK_AFFILIATION.equals(attrName)) {
- taskAffiliation = Integer.valueOf(attrValue);
- }
- }
- if (componentName == null || taskId == INVALID_TASK_ID) {
- if (DEBUG_RESTORER) Slog.e(TAG,
- "createFromFile: FAILED componentName=" + componentName
- + " taskId=" + taskId + " file=" + file);
- return null;
- }
-
- ArraySet<String> launchPackages = null;
- while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
- (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
- if (event == XmlPullParser.START_TAG) {
- if (TaskRecord.TAG_ACTIVITY.equals(in.getName())) {
- for (int j = in.getAttributeCount() - 1; j >= 0; --j) {
- if (ActivityRecord.ATTR_LAUNCHEDFROMPACKAGE.equals(
- in.getAttributeName(j))) {
- if (launchPackages == null) {
- launchPackages = new ArraySet();
- }
- launchPackages.add(in.getAttributeValue(j));
- }
- }
- } else {
- XmlUtils.skipCurrentTag(in);
- }
- }
- }
- if (DEBUG_RESTORER) Slog.d(TAG, "creating OtherDeviceTask from file="
- + file.getName() + " componentName=" + componentName
- + " taskId=" + taskId + " launchPackages=" + launchPackages);
- return new OtherDeviceTask(file, componentName, taskId,
- taskAffiliation, launchPackages);
- } else {
- Slog.wtf(TAG,
- "createFromFile: Unknown xml event=" + event + " name=" + name);
- }
- } else {
- Slog.wtf(TAG, "createFromFile: Unable to find start tag in file=" + file);
- }
- } catch (IOException | XmlPullParserException e) {
- Slog.wtf(TAG, "Unable to parse " + file + ". Error ", e);
- } finally {
- IoUtils.closeQuietly(reader);
- }
-
- // Something went wrong...
- return null;
- }
- }
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index f653e9e..417c7c3 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -28,8 +28,6 @@
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
-import static com.android.server.am.TaskPersister.DEBUG_PERSISTER;
-import static com.android.server.am.TaskPersister.DEBUG_RESTORER;
import android.app.Activity;
import android.app.ActivityManager;
@@ -71,7 +69,7 @@
private static final String TAG_AFFINITYINTENT = "affinity_intent";
static final String ATTR_REALACTIVITY = "real_activity";
private static final String ATTR_ORIGACTIVITY = "orig_activity";
- static final String TAG_ACTIVITY = "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";
@@ -970,10 +968,6 @@
static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
throws IOException, XmlPullParserException {
- return restoreFromXml(in, stackSupervisor, INVALID_TASK_ID);
- }
- static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor,
- int inTaskId) throws IOException, XmlPullParserException {
Intent intent = null;
Intent affinityIntent = null;
ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
@@ -993,7 +987,7 @@
long lastActiveTime = -1;
long lastTimeOnTop = 0;
boolean neverRelinquishIdentity = true;
- int taskId = inTaskId;
+ int taskId = INVALID_TASK_ID;
final int outerDepth = in.getDepth();
TaskDescription taskDescription = new TaskDescription();
int taskAffiliation = INVALID_TASK_ID;
@@ -1008,8 +1002,8 @@
for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
final String attrName = in.getAttributeName(attrNdx);
final String attrValue = in.getAttributeValue(attrNdx);
- if (DEBUG_PERSISTER || DEBUG_RESTORER) Slog.d(TaskPersister.TAG,
- "TaskRecord: attribute name=" + attrName + " value=" + attrValue);
+ if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
+ attrName + " value=" + attrValue);
if (ATTR_TASKID.equals(attrName)) {
if (taskId == INVALID_TASK_ID) taskId = Integer.valueOf(attrValue);
} else if (ATTR_REALACTIVITY.equals(attrName)) {
@@ -1071,16 +1065,16 @@
(event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
if (event == XmlPullParser.START_TAG) {
final String name = in.getName();
- if (DEBUG_PERSISTER || DEBUG_RESTORER)
- Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" + name);
+ if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" +
+ name);
if (TAG_AFFINITYINTENT.equals(name)) {
affinityIntent = Intent.restoreFromXml(in);
} else if (TAG_INTENT.equals(name)) {
intent = Intent.restoreFromXml(in);
} else if (TAG_ACTIVITY.equals(name)) {
ActivityRecord activity = ActivityRecord.restoreFromXml(in, stackSupervisor);
- if (DEBUG_PERSISTER || DEBUG_RESTORER)
- Slog.d(TaskPersister.TAG, "TaskRecord: activity=" + activity);
+ if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
+ activity);
if (activity != null) {
activities.add(activity);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 6c83192..9f35843 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2862,8 +2862,11 @@
}
}
if (toRemove != null) {
+ int delay = checkSendBecomingNoisyIntent(
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+ 0);
for (int i = 0; i < toRemove.size(); i++) {
- makeA2dpDeviceUnavailableNow(toRemove.valueAt(i));
+ makeA2dpDeviceUnavailableLater(toRemove.valueAt(i), delay);
}
}
}
@@ -4426,7 +4429,7 @@
}
// must be called synchronized on mConnectedDevices
- private void makeA2dpDeviceUnavailableLater(String address) {
+ private void makeA2dpDeviceUnavailableLater(String address, int delayMs) {
// prevent any activity on the A2DP audio output to avoid unwanted
// reconnection of the sink.
AudioSystem.setParameters("A2dpSuspended=true");
@@ -4435,7 +4438,7 @@
makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
// send the delayed message to make the device unavailable later
Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
- mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
+ mAudioHandler.sendMessageDelayed(msg, delayMs);
}
@@ -4492,7 +4495,7 @@
// introduction of a delay for transient disconnections of docks when
// power is rapidly turned off/on, this message will be canceled if
// we reconnect the dock under a preset delay
- makeA2dpDeviceUnavailableLater(address);
+ makeA2dpDeviceUnavailableLater(address, BTA2DP_DOCK_TIMEOUT_MILLIS);
// the next time isConnected is evaluated, it will be false for the dock
}
} else {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index aeecdf3..e1ec8a6 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -298,13 +298,15 @@
}
/**
- * Set whether the current package has the ability to launch VPNs without user intervention.
+ * Set whether a package has the ability to launch VPNs without user intervention.
*/
- public void setPackageAuthorization(boolean authorized) {
+ public void setPackageAuthorization(String packageName, boolean authorized) {
// Check if the caller is authorized.
enforceControlPermission();
- if (mPackage == null || VpnConfig.LEGACY_VPN.equals(mPackage)) {
+ int uid = getAppUid(packageName, mUserHandle);
+ if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
+ // Authorization for nonexistent packages (or fake ones) can't be updated.
return;
}
@@ -312,10 +314,10 @@
try {
AppOpsManager appOps =
(AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, mOwnerUID, mPackage,
+ appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, uid, packageName,
authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
} catch (Exception e) {
- Log.wtf(TAG, "Failed to set app ops for package " + mPackage, e);
+ Log.wtf(TAG, "Failed to set app ops for package " + packageName + ", uid " + uid, e);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 0db3e3f..97ada15 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -88,6 +88,11 @@
public static final int FLAG_OWN_CONTENT_ONLY = 1 << 7;
/**
+ * Flag: This display device has a round shape.
+ */
+ public static final int FLAG_ROUND = 1 << 8;
+
+ /**
* Touch attachment: Display does not receive touch.
*/
public static final int TOUCH_NONE = 0;
@@ -385,6 +390,9 @@
if ((flags & FLAG_OWN_CONTENT_ONLY) != 0) {
msg.append(", FLAG_OWN_CONTENT_ONLY");
}
+ if ((flags & FLAG_ROUND) != 0) {
+ msg.append(", FLAG_ROUND");
+ }
return msg.toString();
}
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index cc7d848..517a825 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import android.content.res.Resources;
+import android.os.Build;
import com.android.server.LocalServices;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
@@ -49,6 +51,8 @@
private static final String UNIQUE_ID_PREFIX = "local:";
+ private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
+
private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN,
SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI,
@@ -267,10 +271,16 @@
}
if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
- mInfo.name = getContext().getResources().getString(
+ final Resources res = getContext().getResources();
+ mInfo.name = res.getString(
com.android.internal.R.string.display_manager_built_in_display_name);
mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
| DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
+ if (res.getBoolean(com.android.internal.R.bool.config_mainBuiltInDisplayIsRound)
+ || (Build.HARDWARE.contains("goldfish")
+ && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
+ }
mInfo.type = Display.TYPE_BUILT_IN;
mInfo.densityDpi = (int)(phys.density * 160 + 0.5f);
mInfo.xDpi = phys.xDpi;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 424b4a0..4823769 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -217,6 +217,9 @@
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_PRESENTATION) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_PRESENTATION;
}
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_ROUND) != 0) {
+ mBaseDisplayInfo.flags |= Display.FLAG_ROUND;
+ }
mBaseDisplayInfo.type = deviceInfo.type;
mBaseDisplayInfo.address = deviceInfo.address;
mBaseDisplayInfo.name = deviceInfo.name;
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index f16fcb0..d725d94 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -626,16 +626,16 @@
public void onStart() {
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
mHalDeviceId = nativeOpenHal();
- if (mHalDeviceId != 0) {
- updateActiveGroup(ActivityManager.getCurrentUser());
- }
+ updateActiveGroup(ActivityManager.getCurrentUser());
if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
listenForUserSwitches();
}
private void updateActiveGroup(int userId) {
- File path = Environment.getUserSystemDirectory(userId);
- nativeSetActiveGroup(userId, path.getAbsolutePath().getBytes());
+ if (mHalDeviceId != 0) {
+ File path = Environment.getUserSystemDirectory(userId);
+ nativeSetActiveGroup(userId, path.getAbsolutePath().getBytes());
+ }
}
private void listenForUserSwitches() {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 311ca65..ef4da02 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -660,6 +660,7 @@
String[] newlyVisibleKeys, String[] noLongerVisibleKeys) {
// Using ';' as separator since eventlogs uses ',' to separate
// args.
+ // TODO remove this: b/21248682
EventLogTags.writeNotificationVisibilityChanged(
TextUtils.join(";", newlyVisibleKeys),
TextUtils.join(";", noLongerVisibleKeys));
@@ -667,7 +668,7 @@
for (String key : newlyVisibleKeys) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r == null) continue;
- r.stats.onVisibilityChanged(true);
+ r.setVisibility(true);
}
// Note that we might receive this event after notifications
// have already left the system, e.g. after dismissing from the
@@ -676,7 +677,7 @@
for (String key : noLongerVisibleKeys) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r == null) continue;
- r.stats.onVisibilityChanged(false);
+ r.setVisibility(false);
}
}
}
@@ -2784,8 +2785,11 @@
// Save it for users of getHistoricalNotifications()
mArchive.record(r.sbn);
- int lifespan = (int) (System.currentTimeMillis() - r.getCreationTimeMs());
- EventLogTags.writeNotificationCanceled(canceledKey, reason, lifespan);
+ final long now = System.currentTimeMillis();
+ final int lifespan = (int) (now - r.getCreationTimeMs());
+ final long visibleSinceMs = r.getVisibleSinceMs();
+ final int exposure = visibleSinceMs == 0L ? 0 : (int) (now - visibleSinceMs);
+ EventLogTags.writeNotificationCanceled(canceledKey, reason, lifespan, exposure);
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 02cc840..b8478c1 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -26,6 +26,7 @@
import android.service.notification.StatusBarNotification;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.EventLogTags;
import java.io.PrintWriter;
import java.lang.reflect.Array;
@@ -68,6 +69,12 @@
// The first post time, stable across updates.
private long mCreationTimeMs;
+ // The most recent visibility event.
+ private long mVisibleSinceMs;
+
+ // The most recent update time, or the creation time if no updates.
+ private long mUpdateTimeMs;
+
// Is this record an update of an old record?
public boolean isUpdate;
private int mPackagePriority;
@@ -84,6 +91,7 @@
mOriginalFlags = sbn.getNotification().flags;
mRankingTimeMs = calculateRankingTimeMs(0L);
mCreationTimeMs = sbn.getPostTime();
+ mUpdateTimeMs = mCreationTimeMs;
}
// copy any notes that the ranking system may have made before the update
@@ -95,6 +103,7 @@
mIntercept = previous.mIntercept;
mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
mCreationTimeMs = previous.mCreationTimeMs;
+ mVisibleSinceMs = previous.mVisibleSinceMs;
// Don't copy mGlobalSortKey, recompute it.
}
@@ -181,6 +190,8 @@
pw.println(prefix + " mGlobalSortKey=" + mGlobalSortKey);
pw.println(prefix + " mRankingTimeMs=" + mRankingTimeMs);
pw.println(prefix + " mCreationTimeMs=" + mCreationTimeMs);
+ pw.println(prefix + " mVisibleSinceMs=" + mVisibleSinceMs);
+ pw.println(prefix + " mUpdateTimeMs=" + mUpdateTimeMs);
}
@@ -277,6 +288,13 @@
}
/**
+ * Returns the timestamp of the most recent updates, or the post time if none.
+ */
+ public long getUpdateTimeMs() {
+ return mUpdateTimeMs;
+ }
+
+ /**
* Returns the timestamp of the first post, ignoring updates.
*/
public long getCreationTimeMs() {
@@ -284,6 +302,25 @@
}
/**
+ * Returns the timestamp of the most recent visibility event, or 0L if hidden.
+ */
+ public long getVisibleSinceMs() {
+ return mVisibleSinceMs;
+ }
+
+ /**
+ * Set the visibility of the notification.
+ */
+ public void setVisibility(boolean visible) {
+ final long now = System.currentTimeMillis();
+ mVisibleSinceMs = visible ? now : 0L;
+ stats.onVisibilityChanged(visible);
+ EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0,
+ (int) (now - mCreationTimeMs),
+ (int) (now - mUpdateTimeMs));
+ }
+
+ /**
* @param previousRankingTimeMs for updated notifications, {@link #getRankingTimeMs()}
* of the previous notification record, 0 otherwise
*/
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 185ef03..a4e9c68 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1220,9 +1220,7 @@
};
private boolean isRoundWindow() {
- return mContext.getResources().getBoolean(com.android.internal.R.bool.config_windowIsRound)
- || (Build.HARDWARE.contains("goldfish")
- && SystemProperties.getBoolean(ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false));
+ return mContext.getResources().getConfiguration().isScreenRound();
}
/** {@inheritDoc} */
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index a869c20..0fffa0f 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -39,6 +39,7 @@
import android.graphics.Rect;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
+import android.media.PlaybackParams;
import android.media.tv.DvbDeviceInfo;
import android.media.tv.ITvInputClient;
import android.media.tv.ITvInputHardware;
@@ -87,8 +88,6 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.IllegalArgumentException;
-import java.lang.Integer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -1422,19 +1421,19 @@
}
@Override
- public void timeShiftSetPlaybackRate(IBinder sessionToken, float rate, int audioMode,
+ public void timeShiftSetPlaybackParams(IBinder sessionToken, PlaybackParams params,
int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
- userId, "timeShiftSetPlaybackRate");
+ userId, "timeShiftSetPlaybackParams");
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId)
- .timeShiftSetPlaybackRate(rate, audioMode);
+ .timeShiftSetPlaybackParams(params);
} catch (RemoteException | SessionNotFoundException e) {
- Slog.e(TAG, "error in timeShiftSetPlaybackRate", e);
+ Slog.e(TAG, "error in timeShiftSetPlaybackParams", e);
}
}
} finally {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 91ce739..482ae24 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -432,8 +432,7 @@
mDrawBorderInset = (int) mBorderWidth / 2;
mWindow = new ViewportWindow(mContext);
- if (mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_windowIsRound)) {
+ if (mContext.getResources().getConfiguration().isScreenRound()) {
mCircularPath = new Path();
mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
final int centerXY = mTempPoint.x / 2;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4972ce4..d94f5d9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5923,8 +5923,7 @@
public void updateCircularDisplayMaskIfNeeded() {
// we're fullscreen and not hosted in an ActivityView
- if (mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_windowIsRound)
+ if (mContext.getResources().getConfiguration().isScreenRound()
&& mContext.getResources().getBoolean(
com.android.internal.R.bool.config_windowShowCircularMask)) {
// Device configuration calls for a circular display mask, but we only enable the mask
@@ -6380,10 +6379,9 @@
}
}
- // Copy the screenshot bitmap to another buffer so that the gralloc backed
- // bitmap will not have a long lifetime. Gralloc memory can be pinned or
- // duplicated and might have a higher cost than a skia backed buffer.
- Bitmap ret = bm.copy(bm.getConfig(),true);
+ // Create a copy of the screenshot that is immutable and backed in ashmem.
+ // This greatly reduces the overhead of passing the bitmap between processes.
+ Bitmap ret = bm.createAshmemBitmap();
bm.recycle();
return ret;
}
@@ -7342,6 +7340,11 @@
computeSizeRangesAndScreenLayout(displayInfo, rotated, dw, dh, mDisplayMetrics.density,
config);
+ config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
+ | ((displayInfo.flags & Display.FLAG_ROUND) != 0
+ ? Configuration.SCREENLAYOUT_ROUND_YES
+ : Configuration.SCREENLAYOUT_ROUND_NO);
+
config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated,
@@ -9973,6 +9976,7 @@
winAnimator.setAnimation(a);
winAnimator.mAnimDw = w.mLastFrame.left - left;
winAnimator.mAnimDh = w.mLastFrame.top - top;
+ winAnimator.mAnimateMove = true;
}
//TODO (multidisplay): Accessibility supported only for the default display.
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index ec89b37..424e2e2 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -122,6 +122,7 @@
// used.
int mAnimDw;
int mAnimDh;
+ boolean mAnimateMove = false;
float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
@@ -290,9 +291,15 @@
" wh=" + mWin.mFrame.height() +
" dw=" + mAnimDw + " dh=" + mAnimDh +
" scale=" + mService.getWindowAnimationScaleLocked());
- mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
- mAnimDw, mAnimDh);
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ if (mAnimateMove) {
+ mAnimateMove = false;
+ mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
+ mAnimDw, mAnimDh);
+ } else {
+ mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
+ displayInfo.appWidth, displayInfo.appHeight);
+ }
mAnimDw = displayInfo.appWidth;
mAnimDh = displayInfo.appHeight;
mAnimation.setStartTime(mAnimationStartTime != -1
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 42eb6c3..03abfba 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -35,6 +35,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.IVoiceInteractionSessionService;
import android.service.voice.VoiceInteractionService;
@@ -177,6 +178,8 @@
public boolean showLocked(Bundle args, int flags,
IVoiceInteractionSessionShowCallback showCallback) {
+ // For now we never allow screenshots.
+ flags &= ~VoiceInteractionService.START_WITH_SCREENSHOT;
if (mBound) {
if (!mFullyBound) {
mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
@@ -190,7 +193,8 @@
mHaveAssistData = false;
if ((flags&VoiceInteractionService.START_WITH_ASSIST) != 0) {
if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_STRUCTURE, mCallingUid,
- mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED) {
+ mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED
+ && isStructureEnabled()) {
try {
mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL,
mAssistReceiver);
@@ -455,6 +459,11 @@
}
}
+ private boolean isStructureEnabled() {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, mUser) != 0;
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("mToken="); pw.println(mToken);
pw.print(prefix); pw.print("mShown="); pw.println(mShown);
diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp
index ede9e99..b12867a 100644
--- a/tools/aapt/AaptConfig.cpp
+++ b/tools/aapt/AaptConfig.cpp
@@ -123,6 +123,14 @@
part = parts[index].string();
}
+ if (parseScreenRound(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
if (parseOrientation(part, &config)) {
index++;
if (index == N) {
@@ -241,7 +249,9 @@
}
uint16_t minSdk = 0;
- if (config->density == ResTable_config::DENSITY_ANY) {
+ if (config->screenLayout2 & ResTable_config::MASK_SCREENROUND) {
+ minSdk = SDK_MNC;
+ } else if (config->density == ResTable_config::DENSITY_ANY) {
minSdk = SDK_LOLLIPOP;
} else if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
|| config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
@@ -395,7 +405,26 @@
| ResTable_config::SCREENLONG_NO;
return true;
}
+ return false;
+}
+bool parseScreenRound(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->screenLayout2 =
+ (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
+ | ResTable_config::SCREENROUND_ANY;
+ return true;
+ } else if (strcmp(name, "round") == 0) {
+ if (out) out->screenLayout2 =
+ (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
+ | ResTable_config::SCREENROUND_YES;
+ return true;
+ } else if (strcmp(name, "notround") == 0) {
+ if (out) out->screenLayout2 =
+ (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
+ | ResTable_config::SCREENROUND_NO;
+ return true;
+ }
return false;
}
diff --git a/tools/aapt/AaptConfig.h b/tools/aapt/AaptConfig.h
index f73a5081..04c763f 100644
--- a/tools/aapt/AaptConfig.h
+++ b/tools/aapt/AaptConfig.h
@@ -60,6 +60,7 @@
bool parseScreenHeightDp(const char* str, android::ResTable_config* out = NULL);
bool parseScreenLayoutSize(const char* str, android::ResTable_config* out = NULL);
bool parseScreenLayoutLong(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenRound(const char* name, android::ResTable_config* out = NULL);
bool parseOrientation(const char* str, android::ResTable_config* out = NULL);
bool parseUiModeType(const char* str, android::ResTable_config* out = NULL);
bool parseUiModeNight(const char* str, android::ResTable_config* out = NULL);
diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
index 4e0fe10..16e622a 100644
--- a/tools/aapt/SdkConstants.h
+++ b/tools/aapt/SdkConstants.h
@@ -38,6 +38,7 @@
SDK_KITKAT_WATCH = 20,
SDK_LOLLIPOP = 21,
SDK_LOLLIPOP_MR1 = 22,
+ SDK_MNC = 23,
};
#endif // H_AAPT_SDK_CONSTANTS
diff --git a/tools/aapt/tests/AaptConfig_test.cpp b/tools/aapt/tests/AaptConfig_test.cpp
index 7618974..8bb38ba 100644
--- a/tools/aapt/tests/AaptConfig_test.cpp
+++ b/tools/aapt/tests/AaptConfig_test.cpp
@@ -19,6 +19,7 @@
#include "AaptConfig.h"
#include "ConfigDescription.h"
+#include "SdkConstants.h"
#include "TestHelper.h"
using android::String8;
@@ -82,3 +83,18 @@
EXPECT_TRUE(TestParse("car", &config));
EXPECT_EQ(android::ResTable_config::UI_MODE_TYPE_CAR, config.uiMode);
}
+
+TEST(AaptConfigTest, TestParsingRoundQualifier) {
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("round", &config));
+ EXPECT_EQ(android::ResTable_config::SCREENROUND_YES,
+ config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
+ EXPECT_EQ(SDK_MNC, config.sdkVersion);
+ EXPECT_EQ(String8("round-v23"), config.toString());
+
+ EXPECT_TRUE(TestParse("notround", &config));
+ EXPECT_EQ(android::ResTable_config::SCREENROUND_NO,
+ config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
+ EXPECT_EQ(SDK_MNC, config.sdkVersion);
+ EXPECT_EQ(String8("notround-v23"), config.toString());
+}
diff --git a/tools/layoutlib/bridge/resources/bars/navigation_bar.xml b/tools/layoutlib/bridge/resources/bars/navigation_bar.xml
index 79920a1..55bd1d2 100644
--- a/tools/layoutlib/bridge/resources/bars/navigation_bar.xml
+++ b/tools/layoutlib/bridge/resources/bars/navigation_bar.xml
@@ -1,8 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<View
android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content"
+ android:visibility="invisible"/>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -10,20 +27,23 @@
<View
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:layout_weight="1"/>
+ android:layout_weight="1"
+ android:visibility="invisible"/>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:scaleType="centerInside"/>
<View
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_weight="1"/>
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:visibility="invisible"/>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:scaleType="centerInside"/>
<View
android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content"
+ android:visibility="invisible"/>
</merge>
diff --git a/tools/layoutlib/bridge/resources/bars/navigation_bar600dp.xml b/tools/layoutlib/bridge/resources/bars/navigation_bar600dp.xml
new file mode 100644
index 0000000..e208a0d
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/navigation_bar600dp.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <View
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:visibility="invisible"/>
+ <ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:scaleType="centerInside"/>
+ <View
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:visibility="invisible"/>
+ <ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:scaleType="centerInside"/>
+ <View
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:visibility="invisible"/>
+ <ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:scaleType="centerInside"/>
+ <View
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:visibility="invisible"/>
+</merge>
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index 16f477b..dcf82a3 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -41,6 +41,9 @@
private static final int WIDTH_DEFAULT = 36;
private static final int WIDTH_SW360 = 40;
private static final int WIDTH_SW600 = 48;
+ private static final String LAYOUT_XML = "/bars/navigation_bar.xml";
+ private static final String LAYOUT_600DP_XML = "/bars/navigation_bar600dp.xml";
+
/**
* Constructor to be used when creating the {@link NavigationBar} as a regular control.
@@ -59,8 +62,8 @@
public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
boolean rtlEnabled, int simulatedPlatformVersion) throws XmlPullParserException {
- super(context, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml",
- simulatedPlatformVersion);
+ super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML,
+ "navigation_bar.xml", simulatedPlatformVersion);
int color = getThemeAttrColor(ATTR_COLOR, true);
setBackgroundColor(color == 0 ? 0xFF000000 : color);
@@ -87,13 +90,19 @@
}
private void setupNavBar(BridgeContext context, int orientation) {
+ float sw = getShortestWidth(context);
View leftPadding = getChildAt(0);
View rightPadding = getChildAt(6);
- setSize(context, leftPadding, orientation, getSidePadding(context));
- setSize(context, rightPadding, orientation, getSidePadding(context));
+ setSize(context, leftPadding, orientation, getSidePadding(sw));
+ setSize(context, rightPadding, orientation, getSidePadding(sw));
+ int navButtonWidth = getWidth(sw);
for (int i = 1; i < 6; i += 2) {
View navButton = getChildAt(i);
- setSize(context, navButton, orientation, getWidth(context));
+ setSize(context, navButton, orientation, navButtonWidth);
+ }
+ if (sw >= 600) {
+ setSize(context, getChildAt(2), orientation, 128);
+ setSize(context, getChildAt(4), orientation, 128);
}
}
@@ -108,11 +117,7 @@
view.setLayoutParams(layoutParams);
}
- private static int getSidePadding(BridgeContext context) {
- DisplayMetrics metrics = context.getMetrics();
- float sw = metrics.widthPixels > metrics.heightPixels
- ? metrics.heightPixels : metrics.widthPixels;
- sw /= metrics.density;
+ private static int getSidePadding(float sw) {
if (sw >= 400) {
return PADDING_WIDTH_SW400;
}
@@ -122,11 +127,7 @@
return PADDING_WIDTH_DEFAULT;
}
- private static int getWidth(BridgeContext context) {
- DisplayMetrics metrics = context.getMetrics();
- float sw = metrics.widthPixels > metrics.heightPixels
- ? metrics.heightPixels : metrics.widthPixels;
- sw /= metrics.density;
+ private static int getWidth(float sw) {
if (sw >= 600) {
return WIDTH_SW600;
}
@@ -136,6 +137,14 @@
return WIDTH_DEFAULT;
}
+ private static float getShortestWidth(BridgeContext context) {
+ DisplayMetrics metrics = context.getMetrics();
+ float sw = metrics.widthPixels < metrics.heightPixels ?
+ metrics.widthPixels : metrics.heightPixels;
+ sw /= metrics.density;
+ return sw;
+ }
+
@Override
protected TextView getStyleableTextView() {
return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 677c744..a3fde866 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -16,6 +16,7 @@
package com.android.layoutlib.bridge.impl;
+import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.ide.common.rendering.api.DensityBasedResourceValue;
import com.android.ide.common.rendering.api.LayoutLog;
@@ -70,6 +71,10 @@
public static int getColor(String value) {
if (value != null) {
if (!value.startsWith("#")) {
+ if (value.startsWith(SdkConstants.PREFIX_THEME_REF)) {
+ throw new NumberFormatException(String.format(
+ "Attribute '%s' not found. Are you using the right theme?", value));
+ }
throw new NumberFormatException(
String.format("Color value '%s' must start with #", value));
}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 00cdc71..b2af044 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -152,17 +152,20 @@
int getVerboseLoggingLevel();
+ void enableAggressiveHandover(int enabled);
int getAggressiveHandover();
- void enableAggressiveHandover(int enabled);
-
+ void setAllowScansWithTraffic(int enabled);
int getAllowScansWithTraffic();
- void setAllowScansWithTraffic(int enabled);
+ void setAllowScansWhileAssociated(int enabled);
+ int getAllowScansWhileAssociated();
- boolean getAllowScansWhileAssociated();
+ void setAllowNetworkSwitchingWhileAssociated(int enabled);
+ int getAllowNetworkSwitchingWhileAssociated();
- void setAllowScansWhileAssociated(boolean enabled);
+ void setHalBasedAutojoinOffload(int enabled);
+ int getHalBasedAutojoinOffload();
WifiConnectionStatistics getConnectionStatistics();
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 75198e5..dbfd4ef 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -42,6 +42,14 @@
private static final EnumMap<SupplicantState, DetailedState> stateMap =
new EnumMap<SupplicantState, DetailedState>(SupplicantState.class);
+ /**
+ * Default MAC address reported to a client that does not have the
+ * android.permission.LOCAL_MAC_ADDRESS permission.
+ *
+ * @hide
+ */
+ public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00";
+
static {
stateMap.put(SupplicantState.DISCONNECTED, DetailedState.DISCONNECTED);
stateMap.put(SupplicantState.INTERFACE_DISABLED, DetailedState.DISCONNECTED);
@@ -91,7 +99,7 @@
private int mFrequency;
private InetAddress mIpAddress;
- private String mMacAddress;
+ private String mMacAddress = DEFAULT_MAC_ADDRESS;
/**
* @hide
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index a2b1858..f2c2a28 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2773,7 +2773,7 @@
* Set setting for allowing Scans when infrastructure is associated
* @hide
*/
- public void setAllowScansWhileAssociated(boolean enabled) {
+ public void setAllowScansWhileAssociated(int enabled) {
try {
mService.setAllowScansWhileAssociated(enabled);
} catch (RemoteException e) {
@@ -2785,12 +2785,12 @@
* Get setting for allowing Scans when infrastructure is associated
* @hide
*/
- public boolean getAllowScansWhileAssociated() {
+ public int getAllowScansWhileAssociated() {
try {
return mService.getAllowScansWhileAssociated();
} catch (RemoteException e) {
}
- return false;
+ return 0;
}
/**
@@ -2817,4 +2817,52 @@
return null;
}
}
+
+ /**
+ * Set setting for enabling autojoin Offload thru Wifi HAL layer
+ * @hide
+ */
+ public void setHalBasedAutojoinOffload(int enabled) {
+ try {
+ mService.setHalBasedAutojoinOffload(enabled);
+ } catch (RemoteException e) {
+
+ }
+ }
+
+ /**
+ * Get setting for enabling autojoin Offload thru Wifi HAL layer
+ * @hide
+ */
+ public int getHalBasedAutojoinOffload() {
+ try {
+ return mService.getHalBasedAutojoinOffload();
+ } catch (RemoteException e) {
+ }
+ return 0;
+ }
+
+ /**
+ * Set setting for enabling network switching while wifi is associated
+ * @hide
+ */
+ public void setAllowNetworkSwitchingWhileAssociated(int enabled) {
+ try {
+ mService.setAllowNetworkSwitchingWhileAssociated(enabled);
+ } catch (RemoteException e) {
+
+ }
+ }
+
+ /**
+ * Get setting for enabling network switching while wifi is associated
+ * @hide
+ */
+ public int getAllowNetworkSwitchingWhileAssociated() {
+ try {
+ return mService.getAllowNetworkSwitchingWhileAssociated();
+ } catch (RemoteException e) {
+ }
+ return 0;
+ }
}