summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--api/current.txt29
-rw-r--r--api/system-current.txt30
-rw-r--r--core/java/android/app/ActivityManagerNative.java56
-rw-r--r--core/java/android/app/ActivityThread.java2
-rw-r--r--core/java/android/app/AssistStructure.java9
-rw-r--r--core/java/android/app/IActivityManager.java7
-rw-r--r--core/java/android/app/IUidObserver.aidl23
-rw-r--r--core/java/android/app/backup/RecentsBackupHelper.java130
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java8
-rw-r--r--core/java/android/bluetooth/BluetoothPan.java22
-rw-r--r--core/java/android/content/res/Configuration.java65
-rw-r--r--core/java/android/hardware/camera2/CameraAccessException.java26
-rw-r--r--core/java/android/hardware/camera2/CameraCaptureSession.java57
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java7
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java56
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java39
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java2
-rw-r--r--core/java/android/hardware/camera2/CaptureFailure.java19
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java16
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java6
-rw-r--r--core/java/android/hardware/camera2/DngCreator.java29
-rw-r--r--core/java/android/hardware/camera2/TotalCaptureResult.java2
-rw-r--r--core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java1
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java2
-rw-r--r--core/java/android/net/ConnectivityManager.java39
-rw-r--r--core/java/android/net/IConnectivityManager.aidl8
-rw-r--r--core/java/android/net/IpReachabilityMonitor.java54
-rw-r--r--core/java/android/net/Network.java7
-rw-r--r--core/java/android/net/NetworkCapabilities.java1
-rw-r--r--core/java/android/net/NetworkUtils.java6
-rw-r--r--core/java/android/net/VpnService.java9
-rw-r--r--core/java/android/net/netlink/RtNetlinkNeighborMessage.java65
-rw-r--r--core/java/android/net/netlink/StructNdMsg.java5
-rw-r--r--core/java/android/net/netlink/StructNlAttr.java8
-rw-r--r--core/java/android/net/netlink/StructNlMsgErr.java5
-rw-r--r--core/java/android/net/netlink/StructNlMsgHdr.java11
-rw-r--r--core/java/android/os/IUserManager.aidl1
-rw-r--r--core/java/android/os/MessageQueue.java115
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java12
-rw-r--r--core/java/android/os/PowerManagerInternal.java4
-rw-r--r--core/java/android/os/UserManager.java16
-rw-r--r--core/java/android/provider/Settings.java8
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java29
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java1
-rw-r--r--core/java/android/service/voice/VoiceInteractionSession.java1
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java6
-rw-r--r--core/java/android/util/SparseArray.java18
-rw-r--r--core/java/android/view/ActionMode.java19
-rw-r--r--core/java/android/view/Display.java11
-rw-r--r--core/java/android/view/DisplayInfo.java3
-rw-r--r--core/java/android/view/ViewConfiguration.java12
-rw-r--r--core/java/android/view/ViewPropertyAnimator.java15
-rw-r--r--core/java/android/view/ViewRootImpl.java20
-rw-r--r--core/java/android/view/ViewStructure.java155
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java27
-rw-r--r--core/java/android/webkit/FindActionModeCallback.java6
-rw-r--r--core/java/android/webkit/ViewAssistStructure.java5
-rw-r--r--core/java/android/widget/Editor.java56
-rw-r--r--core/java/android/widget/TextView.java24
-rw-r--r--core/java/com/android/internal/alsa/AlsaCardsParser.java2
-rw-r--r--core/java/com/android/internal/app/ProcessMap.java6
-rw-r--r--core/java/com/android/internal/logging/MetricsConstants.java1
-rw-r--r--core/java/com/android/internal/util/ScreenShapeHelper.java12
-rw-r--r--core/java/com/android/internal/view/FloatingActionMode.java134
-rw-r--r--core/java/com/android/internal/widget/FloatingToolbar.java4
-rw-r--r--core/java/com/android/internal/widget/TextViewInputDisabler.java49
-rw-r--r--core/java/com/android/server/backup/SystemBackupAgent.java14
-rw-r--r--core/jni/android/graphics/Region.cpp10
-rw-r--r--core/jni/android_net_NetUtils.cpp6
-rw-r--r--core/jni/android_util_AssetManager.cpp11
-rw-r--r--core/res/AndroidManifest.xml8
-rw-r--r--core/res/res/values/attrs.xml3
-rwxr-xr-xcore/res/res/values/config.xml20
-rw-r--r--core/res/res/values/styles_material.xml16
-rwxr-xr-xcore/res/res/values/symbols.xml8
-rw-r--r--core/res/res/values/themes_material.xml4
-rw-r--r--core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java52
-rw-r--r--core/tests/coretests/src/android/widget/TextViewTest.java66
-rw-r--r--data/fonts/fallback_fonts.xml13
-rw-r--r--data/fonts/fonts.xml11
-rw-r--r--docs/html/about/about_toc.cs14
-rw-r--r--docs/html/about/android.jd111
-rw-r--r--docs/html/about/versions/lollipop.jd147
-rw-r--r--docs/html/develop/index.jd7
-rw-r--r--docs/html/distribute/analyze/index.jd2
-rw-r--r--docs/html/distribute/analyze/start.jd2
-rw-r--r--docs/html/distribute/engage/ads.jd13
-rw-r--r--docs/html/distribute/engage/appindexing.jd61
-rw-r--r--docs/html/distribute/engage/deep-linking.jd13
-rw-r--r--docs/html/distribute/engage/easy-signin.jd137
-rw-r--r--docs/html/distribute/engage/engage_toc.cs3
-rw-r--r--docs/html/distribute/engage/index.jd22
-rw-r--r--docs/html/distribute/essentials/best-practices/apps.jd260
-rw-r--r--docs/html/distribute/essentials/best-practices/games.jd259
-rw-r--r--docs/html/distribute/essentials/essentials_toc.cs13
-rw-r--r--docs/html/distribute/essentials/index.jd18
-rw-r--r--docs/html/distribute/googleplay/auto.jd2
-rw-r--r--docs/html/distribute/googleplay/cardboard.jd3
-rw-r--r--docs/html/distribute/googleplay/cast.jd9
-rw-r--r--docs/html/distribute/googleplay/edu/about.jd51
-rw-r--r--docs/html/distribute/googleplay/families/faq.jd2
-rw-r--r--docs/html/distribute/googleplay/googleplay_toc.cs20
-rw-r--r--docs/html/distribute/googleplay/index.jd25
-rw-r--r--docs/html/distribute/googleplay/tv.jd2
-rw-r--r--docs/html/distribute/googleplay/wear.jd2
-rw-r--r--docs/html/distribute/googleplay/work/about.jd95
-rw-r--r--docs/html/distribute/index.jd12
-rw-r--r--docs/html/distribute/monetize/ads.jd119
-rw-r--r--docs/html/distribute/monetize/index.jd15
-rw-r--r--docs/html/distribute/monetize/payments.jd2
-rw-r--r--docs/html/distribute/tools/index.jd20
-rw-r--r--docs/html/distribute/users/house-ads.jd61
-rw-r--r--docs/html/distribute/users/index.jd22
-rw-r--r--docs/html/distribute/users/ota-installs.jd51
-rw-r--r--docs/html/distribute/users/promote-with-ads.jd81
-rw-r--r--docs/html/distribute/users/users_toc.cs18
-rw-r--r--docs/html/distribute/users/youtube.jd34
-rw-r--r--docs/html/google/index.jd48
-rw-r--r--docs/html/google/play/billing/billing_subscriptions.jd2
-rw-r--r--docs/html/google/play/billing/index.jd8
-rw-r--r--docs/html/images/cards/card-analytics_2x.pngbin0 -> 7780 bytes
-rw-r--r--docs/html/images/cards/card-android-work_2x.pngbin0 -> 9880 bytes
-rw-r--r--docs/html/images/cards/card-youtube_2x.pngbin0 -> 5525 bytes
-rw-r--r--docs/html/images/develop/hero_image_studio5.pngbin0 -> 103243 bytes
-rw-r--r--docs/html/images/develop/hero_image_studio5_2x.pngbin0 -> 401643 bytes
-rw-r--r--docs/html/images/develop/studio-open.pngbin518726 -> 0 bytes
-rw-r--r--docs/html/images/distribute/android-work.jpgbin0 -> 155972 bytes
-rw-r--r--docs/html/images/distribute/gpfw_business.pngbin0 -> 15177 bytes
-rw-r--r--docs/html/images/distribute/gpfw_developer.pngbin0 -> 17469 bytes
-rw-r--r--docs/html/images/distribute/house-ads.pngbin0 -> 229247 bytes
-rw-r--r--docs/html/images/distribute/ota-installs.gifbin0 -> 635143 bytes
-rw-r--r--docs/html/images/distribute/signin-apps.pngbin0 -> 215251 bytes
-rw-r--r--docs/html/images/distribute/signin-seamless.pngbin0 -> 524858 bytes
-rw-r--r--docs/html/images/distribute/signin-secure.pngbin0 -> 330929 bytes
-rw-r--r--docs/html/images/distribute/youtube-card-example.pngbin0 -> 203228 bytes
-rw-r--r--docs/html/images/play_dev.jpgbin47072 -> 14049 bytes
-rw-r--r--docs/html/images/play_dev_old.jpgbin0 -> 47072 bytes
-rw-r--r--docs/html/images/versions/notification-headsup.pngbin142431 -> 58967 bytes
-rw-r--r--docs/html/images/versions/rivalknights.pngbin1081508 -> 309561 bytes
-rw-r--r--docs/html/index.jd11
-rw-r--r--docs/html/jd_collections.js73
-rw-r--r--docs/html/jd_extras.js290
-rw-r--r--docs/html/preview/api-overview.jd3
-rw-r--r--docs/html/preview/index.jd31
-rw-r--r--docs/html/preview/overview.jd1
-rw-r--r--docs/html/preview/setup-sdk.jd3
-rw-r--r--docs/html/preview/support.jd1
-rw-r--r--docs/html/tools/building/building-cmdline.jd8
-rw-r--r--docs/html/tools/devices/index.jd16
-rw-r--r--docs/html/tools/devices/managing-avds-cmdline.jd4
-rw-r--r--docs/html/training/articles/keystore.jd67
-rw-r--r--graphics/java/android/graphics/drawable/RippleComponent.java17
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java9
-rw-r--r--include/androidfw/ResourceTypes.h19
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java (renamed from keystore/java/android/security/keystore/AndroidKeyPairGeneratorSpi.java)15
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreProvider.java15
-rw-r--r--libs/androidfw/ResourceTypes.cpp43
-rw-r--r--libs/androidfw/tests/Config_test.cpp78
-rw-r--r--libs/hwui/utils/RingBuffer.h2
-rw-r--r--media/java/android/media/AudioTrack.java201
-rw-r--r--media/java/android/media/midi/MidiReceiver.java25
-rw-r--r--native/android/configuration.cpp9
-rw-r--r--packages/Keyguard/src/com/android/keyguard/EmergencyButton.java4
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java9
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java40
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java7
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java8
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java11
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java164
-rw-r--r--services/core/java/com/android/server/EventLogTags.logtags4
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerDebugConfig.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java295
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityRecord.java18
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java24
-rw-r--r--services/core/java/com/android/server/am/TaskPersister.java660
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java22
-rw-r--r--services/core/java/com/android/server/am/UidRecord.java62
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkMonitor.java118
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java12
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceInfo.java8
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java12
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java3
-rw-r--r--services/core/java/com/android/server/fingerprint/FingerprintService.java10
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java104
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java12
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java37
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java295
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java43
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java4
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java44
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java3
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java3
-rw-r--r--services/core/java/com/android/server/wm/AppWindowAnimator.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java81
-rw-r--r--services/usb/java/com/android/server/usb/UsbAlsaManager.java2
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java11
-rw-r--r--tools/aapt/AaptConfig.cpp31
-rw-r--r--tools/aapt/AaptConfig.h1
-rw-r--r--tools/aapt/SdkConstants.h1
-rw-r--r--tools/aapt/tests/AaptConfig_test.cpp16
-rw-r--r--tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java14
-rw-r--r--tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java3
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java5
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl13
-rw-r--r--wifi/java/android/net/wifi/WifiInfo.java10
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java54
217 files changed, 4062 insertions, 2975 deletions
diff --git a/Android.mk b/Android.mk
index eb6e7b5..146afe0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -84,6 +84,7 @@ LOCAL_SRC_FILES += \
core/java/android/app/job/IJobScheduler.aidl \
core/java/android/app/job/IJobService.aidl \
core/java/android/app/ITransientNotification.aidl \
+ core/java/android/app/IUidObserver.aidl \
core/java/android/app/IUiAutomationConnection.aidl \
core/java/android/app/IUiModeManager.aidl \
core/java/android/app/IUserSwitchObserver.aidl \
diff --git a/api/current.txt b/api/current.txt
index 2cf8969..d3b8724 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9630,6 +9630,7 @@ package android.content.res {
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 @@ package android.content.res {
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
@@ -23094,24 +23099,23 @@ package android.os {
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);
+ method public void removeOnFileDescriptorEventListener(java.io.FileDescriptor);
}
- public static abstract class MessageQueue.FileDescriptorCallback {
- ctor public MessageQueue.FileDescriptorCallback();
- method public int onFileDescriptorEvents(java.io.FileDescriptor, int);
+ 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 static abstract interface MessageQueue.IdleHandler {
- method public abstract boolean queueIdle();
- }
-
public final class Messenger implements android.os.Parcelable {
ctor public Messenger(android.os.Handler);
ctor public Messenger(android.os.IBinder);
@@ -28887,7 +28891,6 @@ package android.service.voice {
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 {
@@ -28913,7 +28916,6 @@ package android.service.voice {
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);
@@ -34441,6 +34443,8 @@ package android.view {
method public abstract void setTitle(int);
method public void setTitleOptionalHint(boolean);
method public void setType(int);
+ method public void snooze(int);
+ field public static final int SNOOZE_TIME_DEFAULT;
field public static final int TYPE_FLOATING = 1; // 0x1
field public static final int TYPE_PRIMARY = 0; // 0x0
}
@@ -34536,6 +34540,7 @@ package android.view {
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
@@ -36504,6 +36509,7 @@ package android.view {
public class ViewConfiguration {
ctor public deprecated ViewConfiguration();
method public static android.view.ViewConfiguration get(android.content.Context);
+ method public static int getDefaultActionModeSnoozeTime();
method public static int getDoubleTapTimeout();
method public static deprecated int getEdgeSlop();
method public static deprecated int getFadingEdgeLength();
@@ -36907,6 +36913,7 @@ package android.view {
method public abstract void setText(java.lang.CharSequence);
method public abstract void setText(java.lang.CharSequence, int, int);
method public abstract void setTextPaint(android.text.TextPaint);
+ method public abstract void setTextStyle(int, int, int, int);
method public abstract void setVisibility(int);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 263d17a..1dc5325 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -111,6 +111,7 @@ package android {
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 @@ package android.content.res {
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 @@ package android.content.res {
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
@@ -25008,24 +25014,23 @@ package android.os {
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);
+ method public void removeOnFileDescriptorEventListener(java.io.FileDescriptor);
}
- public static abstract class MessageQueue.FileDescriptorCallback {
- ctor public MessageQueue.FileDescriptorCallback();
- method public int onFileDescriptorEvents(java.io.FileDescriptor, int);
+ 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 static abstract interface MessageQueue.IdleHandler {
- method public abstract boolean queueIdle();
- }
-
public final class Messenger implements android.os.Parcelable {
ctor public Messenger(android.os.Handler);
ctor public Messenger(android.os.IBinder);
@@ -31016,7 +31021,6 @@ package android.service.voice {
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 {
@@ -31042,7 +31046,6 @@ package android.service.voice {
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);
@@ -36703,6 +36706,8 @@ package android.view {
method public abstract void setTitle(int);
method public void setTitleOptionalHint(boolean);
method public void setType(int);
+ method public void snooze(int);
+ field public static final int SNOOZE_TIME_DEFAULT;
field public static final int TYPE_FLOATING = 1; // 0x1
field public static final int TYPE_PRIMARY = 0; // 0x0
}
@@ -36798,6 +36803,7 @@ package android.view {
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
@@ -38766,6 +38772,7 @@ package android.view {
public class ViewConfiguration {
ctor public deprecated ViewConfiguration();
method public static android.view.ViewConfiguration get(android.content.Context);
+ method public static int getDefaultActionModeSnoozeTime();
method public static int getDoubleTapTimeout();
method public static deprecated int getEdgeSlop();
method public static deprecated int getFadingEdgeLength();
@@ -39169,6 +39176,7 @@ package android.view {
method public abstract void setText(java.lang.CharSequence);
method public abstract void setText(java.lang.CharSequence, int, int);
method public abstract void setTextPaint(android.text.TextPaint);
+ method public abstract void setTextStyle(int, int, int, int);
method public abstract void setVisibility(int);
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1e9bc54..cdf15e1 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1970,6 +1970,22 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case REGISTER_UID_OBSERVER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IUidObserver observer = IUidObserver.Stub.asInterface(
+ data.readStrongBinder());
+ registerUidObserver(observer);
+ return true;
+ }
+
+ case UNREGISTER_UID_OBSERVER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IUidObserver observer = IUidObserver.Stub.asInterface(
+ data.readStrongBinder());
+ unregisterUidObserver(observer);
+ return true;
+ }
+
case GET_PACKAGE_ASK_SCREEN_COMPAT_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
@@ -2466,13 +2482,6 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
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();
@@ -5084,6 +5093,28 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
+ public void registerUidObserver(IUidObserver observer) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ mRemote.transact(REGISTER_UID_OBSERVER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public void unregisterUidObserver(IUidObserver observer) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ mRemote.transact(UNREGISTER_UID_OBSERVER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -5759,17 +5790,6 @@ class ActivityManagerProxy implements IActivityManager
}
@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/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5dcbe37..cb436b5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4585,7 +4585,7 @@ public final class ActivityThread {
// crash if we can't get it.
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
try {
- final ProxyInfo proxyInfo = service.getDefaultProxy();
+ final ProxyInfo proxyInfo = service.getProxyForNetwork(null);
Proxy.setHttpProxySystemProperty(proxyInfo);
} catch (RemoteException e) {}
}
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java
index a06bc31..b703b0e 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -635,6 +635,15 @@ final public class AssistStructure implements Parcelable {
}
@Override
+ public void setTextStyle(int size, int fgColor, int bgColor, int style) {
+ ViewNodeText t = getNodeText();
+ t.mTextColor = fgColor;
+ t.mTextBackgroundColor = bgColor;
+ t.mTextSize = size;
+ t.mTextStyle = style;
+ }
+
+ @Override
public void setHint(CharSequence hint) {
getNodeText().mHint = hint != null ? hint.toString() : null;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 05a936c..310c5ef 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -387,6 +387,9 @@ public interface IActivityManager extends IInterface {
public void registerProcessObserver(IProcessObserver observer) throws RemoteException;
public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException;
+ public void registerUidObserver(IUidObserver observer) throws RemoteException;
+ public void unregisterUidObserver(IUidObserver observer) throws RemoteException;
+
public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException;
public boolean isIntentSenderAnActivity(IIntentSender sender) throws RemoteException;
@@ -488,7 +491,6 @@ public interface IActivityManager extends IInterface {
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 +827,6 @@ public interface IActivityManager extends IInterface {
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;
@@ -846,4 +847,6 @@ public interface IActivityManager extends IInterface {
int UPDATE_DEVICE_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+295;
int UPDATE_PREFERRED_SETUP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+296;
int KEYGUARD_GOING_AWAY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+297;
+ int REGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+298;
+ int UNREGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+299;
}
diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl
new file mode 100644
index 0000000..308cb94
--- /dev/null
+++ b/core/java/android/app/IUidObserver.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package android.app;
+
+/** {@hide} */
+oneway interface IUidObserver {
+ void onUidStateChanged(int uid, int procState);
+ void onUidGone(int uid);
+}
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 @@ public final class BluetoothAdapter {
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/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 4f81f98..eb6166a 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -333,19 +333,25 @@ public final class BluetoothPan implements BluetoothProfile {
public void setBluetoothTethering(boolean value) {
if (DBG) log("setBluetoothTethering(" + value + ")");
- try {
- mPanService.setBluetoothTethering(value);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+
+ if (mPanService != null && isEnabled()) {
+ try {
+ mPanService.setBluetoothTethering(value);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ }
}
}
public boolean isTetheringOn() {
if (VDBG) log("isTetheringOn()");
- try {
- return mPanService.isTetheringOn();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+
+ if (mPanService != null && isEnabled()) {
+ try {
+ return mPanService.isTetheringOn();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ }
}
return false;
}
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 @@ public final class Configuration implements Parcelable, Comparable<Configuration
* 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration
* <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 @@ public final class Configuration implements Parcelable, Comparable<Configuration
}
/**
+ * 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration
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 @@ public final class Configuration implements Parcelable, Comparable<Configuration
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/CameraAccessException.java b/core/java/android/hardware/camera2/CameraAccessException.java
index 84e9912..933ce0d 100644
--- a/core/java/android/hardware/camera2/CameraAccessException.java
+++ b/core/java/android/hardware/camera2/CameraAccessException.java
@@ -16,8 +16,13 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
+import android.annotation.IntDef;
import android.util.AndroidException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* <p><code>CameraAccessException</code> is thrown if a camera device could not
* be queried or opened by the {@link CameraManager}, or if the connection to an
@@ -76,6 +81,16 @@ public class CameraAccessException extends AndroidException {
*/
public static final int CAMERA_DEPRECATED_HAL = 1000;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ {CAMERA_IN_USE,
+ MAX_CAMERAS_IN_USE,
+ CAMERA_DISABLED,
+ CAMERA_DISCONNECTED,
+ CAMERA_ERROR})
+ public @interface AccessError {};
+
// Make the eclipse warning about serializable exceptions go away
private static final long serialVersionUID = 5630338637471475675L; // randomly generated
@@ -88,26 +103,27 @@ public class CameraAccessException extends AndroidException {
* @see #CAMERA_DISCONNECTED
* @see #CAMERA_ERROR
*/
+ @AccessError
public final int getReason() {
return mReason;
}
- public CameraAccessException(int problem) {
+ public CameraAccessException(@AccessError int problem) {
super(getDefaultMessage(problem));
mReason = problem;
}
- public CameraAccessException(int problem, String message) {
+ public CameraAccessException(@AccessError int problem, String message) {
super(message);
mReason = problem;
}
- public CameraAccessException(int problem, String message, Throwable cause) {
+ public CameraAccessException(@AccessError int problem, String message, Throwable cause) {
super(message, cause);
mReason = problem;
}
- public CameraAccessException(int problem, Throwable cause) {
+ public CameraAccessException(@AccessError int problem, Throwable cause) {
super(getDefaultMessage(problem), cause);
mReason = problem;
}
@@ -115,7 +131,7 @@ public class CameraAccessException extends AndroidException {
/**
* @hide
*/
- public static String getDefaultMessage(int problem) {
+ public static String getDefaultMessage(@AccessError int problem) {
switch (problem) {
case CAMERA_IN_USE:
return "The camera device is in use already";
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index c6f622c..b3e7cfc 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -16,6 +16,8 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Handler;
import android.view.Surface;
@@ -73,6 +75,7 @@ public abstract class CameraCaptureSession implements AutoCloseable {
/**
* Get the camera device that this session is created for.
*/
+ @NonNull
public abstract CameraDevice getDevice();
/**
@@ -133,7 +136,7 @@ public abstract class CameraCaptureSession implements AutoCloseable {
*
* @see StateCallback#onSurfacePrepared
*/
- public abstract void prepare(Surface surface) throws CameraAccessException;
+ public abstract void prepare(@NonNull Surface surface) throws CameraAccessException;
/**
* <p>Submit a request for an image to be captured by the camera device.</p>
@@ -194,7 +197,8 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @see #abortCaptures
* @see CameraDevice#createReprocessableCaptureSession
*/
- public abstract int capture(CaptureRequest request, CaptureCallback listener, Handler handler)
+ public abstract int capture(@NonNull CaptureRequest request,
+ @Nullable CaptureCallback listener, @Nullable Handler handler)
throws CameraAccessException;
/**
@@ -252,8 +256,9 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @see #setRepeatingBurst
* @see #abortCaptures
*/
- public abstract int captureBurst(List<CaptureRequest> requests, CaptureCallback listener,
- Handler handler) throws CameraAccessException;
+ public abstract int captureBurst(@NonNull List<CaptureRequest> requests,
+ @Nullable CaptureCallback listener, @Nullable Handler handler)
+ throws CameraAccessException;
/**
* Request endlessly repeating capture of images by this capture session.
@@ -318,8 +323,9 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @see #stopRepeating
* @see #abortCaptures
*/
- public abstract int setRepeatingRequest(CaptureRequest request, CaptureCallback listener,
- Handler handler) throws CameraAccessException;
+ public abstract int setRepeatingRequest(@NonNull CaptureRequest request,
+ @Nullable CaptureCallback listener, @Nullable Handler handler)
+ throws CameraAccessException;
/**
* <p>Request endlessly repeating capture of a sequence of images by this
@@ -389,8 +395,9 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @see #stopRepeating
* @see #abortCaptures
*/
- public abstract int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener,
- Handler handler) throws CameraAccessException;
+ public abstract int setRepeatingBurst(@NonNull List<CaptureRequest> requests,
+ @Nullable CaptureCallback listener, @Nullable Handler handler)
+ throws CameraAccessException;
/**
* <p>Cancel any ongoing repeating capture set by either
@@ -478,6 +485,7 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @see android.media.ImageWriter
* @see android.media.ImageReader
*/
+ @Nullable
public abstract Surface getInputSurface();
/**
@@ -525,7 +533,7 @@ public abstract class CameraCaptureSession implements AutoCloseable {
*
* @param session the session returned by {@link CameraDevice#createCaptureSession}
*/
- public abstract void onConfigured(CameraCaptureSession session);
+ public abstract void onConfigured(@NonNull CameraCaptureSession session);
/**
* This method is called if the session cannot be configured as requested.
@@ -540,7 +548,7 @@ public abstract class CameraCaptureSession implements AutoCloseable {
*
* @param session the session returned by {@link CameraDevice#createCaptureSession}
*/
- public abstract void onConfigureFailed(CameraCaptureSession session);
+ public abstract void onConfigureFailed(@NonNull CameraCaptureSession session);
/**
* This method is called every time the session has no more capture requests to process.
@@ -555,7 +563,7 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @param session the session returned by {@link CameraDevice#createCaptureSession}
*
*/
- public void onReady(CameraCaptureSession session) {
+ public void onReady(@NonNull CameraCaptureSession session) {
// default empty implementation
}
@@ -571,7 +579,7 @@ public abstract class CameraCaptureSession implements AutoCloseable {
*
* @param session the session returned by {@link CameraDevice#createCaptureSession}
*/
- public void onActive(CameraCaptureSession session) {
+ public void onActive(@NonNull CameraCaptureSession session) {
// default empty implementation
}
@@ -589,7 +597,7 @@ public abstract class CameraCaptureSession implements AutoCloseable {
*
* @param session the session returned by {@link CameraDevice#createCaptureSession}
*/
- public void onClosed(CameraCaptureSession session) {
+ public void onClosed(@NonNull CameraCaptureSession session) {
// default empty implementation
}
@@ -608,7 +616,8 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @param session the session returned by {@link CameraDevice#createCaptureSession}
* @param surface the Surface that was used with the {@link #prepare} call.
*/
- public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
+ public void onSurfacePrepared(@NonNull CameraCaptureSession session,
+ @NonNull Surface surface) {
// default empty implementation
}
}
@@ -675,8 +684,8 @@ public abstract class CameraCaptureSession implements AutoCloseable {
*
* @see android.media.MediaActionSound
*/
- public void onCaptureStarted(CameraCaptureSession session,
- CaptureRequest request, long timestamp, long frameNumber) {
+ public void onCaptureStarted(@NonNull CameraCaptureSession session,
+ @NonNull CaptureRequest request, long timestamp, long frameNumber) {
// Temporary trampoline for API change transition
onCaptureStarted(session, request, timestamp);
}
@@ -756,8 +765,8 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @see #setRepeatingRequest
* @see #setRepeatingBurst
*/
- public void onCaptureProgressed(CameraCaptureSession session,
- CaptureRequest request, CaptureResult partialResult) {
+ public void onCaptureProgressed(@NonNull CameraCaptureSession session,
+ @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
// default empty implementation
}
@@ -785,8 +794,8 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @see #setRepeatingRequest
* @see #setRepeatingBurst
*/
- public void onCaptureCompleted(CameraCaptureSession session,
- CaptureRequest request, TotalCaptureResult result) {
+ public void onCaptureCompleted(@NonNull CameraCaptureSession session,
+ @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
// default empty implementation
}
@@ -814,8 +823,8 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @see #setRepeatingRequest
* @see #setRepeatingBurst
*/
- public void onCaptureFailed(CameraCaptureSession session,
- CaptureRequest request, CaptureFailure failure) {
+ public void onCaptureFailed(@NonNull CameraCaptureSession session,
+ @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
// default empty implementation
}
@@ -844,7 +853,7 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @see CaptureFailure#getSequenceId()
* @see #onCaptureSequenceAborted
*/
- public void onCaptureSequenceCompleted(CameraCaptureSession session,
+ public void onCaptureSequenceCompleted(@NonNull CameraCaptureSession session,
int sequenceId, long frameNumber) {
// default empty implementation
}
@@ -873,7 +882,7 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @see CaptureFailure#getSequenceId()
* @see #onCaptureSequenceCompleted
*/
- public void onCaptureSequenceAborted(CameraCaptureSession session,
+ public void onCaptureSequenceAborted(@NonNull CameraCaptureSession session,
int sequenceId) {
// default empty implementation
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index d3b63f9..c2a6a44 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -16,6 +16,8 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
@@ -91,6 +93,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
*
* @return String representation of the key name
*/
+ @NonNull
public String getName() {
return mKey.getName();
}
@@ -166,6 +169,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* @param key The characteristics field to read.
* @return The value of that key, or {@code null} if the field is not set.
*/
+ @Nullable
public <T> T get(Key<T> key) {
return mProperties.get(key);
}
@@ -194,6 +198,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
/**
* {@inheritDoc}
*/
+ @NonNull
@Override
public List<Key<?>> getKeys() {
// List of keys is immutable; cache the results after we calculate them
@@ -227,6 +232,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* @return List of keys supported by this CameraDevice for CaptureRequests.
*/
@SuppressWarnings({"unchecked"})
+ @NonNull
public List<CaptureRequest.Key<?>> getAvailableCaptureRequestKeys() {
if (mAvailableRequestKeys == null) {
Object crKey = CaptureRequest.Key.class;
@@ -258,6 +264,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* @return List of keys supported by this CameraDevice for CaptureResults.
*/
@SuppressWarnings({"unchecked"})
+ @NonNull
public List<CaptureResult.Key<?>> getAvailableCaptureResultKeys() {
if (mAvailableResultKeys == null) {
Object crKey = CaptureResult.Key.class;
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index dad4fb6..d02f349 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -16,6 +16,9 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.IntDef;
import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.params.OutputConfiguration;
@@ -23,6 +26,8 @@ import android.os.Handler;
import android.view.Surface;
import java.util.List;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* <p>The CameraDevice class is a representation of a single camera connected to an
@@ -124,6 +129,17 @@ public abstract class CameraDevice implements AutoCloseable {
*/
public static final int TEMPLATE_MANUAL = 6;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ {TEMPLATE_PREVIEW,
+ TEMPLATE_STILL_CAPTURE,
+ TEMPLATE_RECORD,
+ TEMPLATE_VIDEO_SNAPSHOT,
+ TEMPLATE_ZERO_SHUTTER_LAG,
+ TEMPLATE_MANUAL })
+ public @interface RequestTemplate {};
+
/**
* Get the ID of this camera device.
*
@@ -142,6 +158,7 @@ public abstract class CameraDevice implements AutoCloseable {
* @see CameraManager#getCameraCharacteristics
* @see CameraManager#getCameraIdList
*/
+ @NonNull
public abstract String getId();
/**
@@ -391,8 +408,8 @@ public abstract class CameraDevice implements AutoCloseable {
* @see StreamConfigurationMap#getOutputSizes(int)
* @see StreamConfigurationMap#getOutputSizes(Class)
*/
- public abstract void createCaptureSession(List<Surface> outputs,
- CameraCaptureSession.StateCallback callback, Handler handler)
+ public abstract void createCaptureSession(@NonNull List<Surface> outputs,
+ @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
throws CameraAccessException;
/**
@@ -560,8 +577,9 @@ public abstract class CameraDevice implements AutoCloseable {
* @see android.media.ImageWriter
* @see android.media.ImageReader
*/
- public abstract void createReprocessableCaptureSession(InputConfiguration inputConfig,
- List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)
+ public abstract void createReprocessableCaptureSession(@NonNull InputConfiguration inputConfig,
+ @NonNull List<Surface> outputs, @NonNull CameraCaptureSession.StateCallback callback,
+ @Nullable Handler handler)
throws CameraAccessException;
/**
@@ -591,7 +609,8 @@ public abstract class CameraDevice implements AutoCloseable {
* @see #TEMPLATE_VIDEO_SNAPSHOT
* @see #TEMPLATE_MANUAL
*/
- public abstract CaptureRequest.Builder createCaptureRequest(int templateType)
+ @NonNull
+ public abstract CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType)
throws CameraAccessException;
/**
@@ -620,8 +639,9 @@ public abstract class CameraDevice implements AutoCloseable {
* @see CameraDevice#createReprocessableCaptureSession
* @see android.media.ImageWriter
*/
+ @NonNull
public abstract CaptureRequest.Builder createReprocessCaptureRequest(
- TotalCaptureResult inputResult) throws CameraAccessException;
+ @NonNull TotalCaptureResult inputResult) throws CameraAccessException;
/**
* Close the connection to this camera device as quickly as possible.
@@ -727,6 +747,16 @@ public abstract class CameraDevice implements AutoCloseable {
*/
public static final int ERROR_CAMERA_SERVICE = 5;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ {ERROR_CAMERA_IN_USE,
+ ERROR_MAX_CAMERAS_IN_USE,
+ ERROR_CAMERA_DISABLED,
+ ERROR_CAMERA_DEVICE,
+ ERROR_CAMERA_SERVICE })
+ public @interface ErrorCode {};
+
/**
* The method called when a camera device has finished opening.
*
@@ -736,7 +766,7 @@ public abstract class CameraDevice implements AutoCloseable {
*
* @param camera the camera device that has become opened
*/
- public abstract void onOpened(CameraDevice camera); // Must implement
+ public abstract void onOpened(@NonNull CameraDevice camera); // Must implement
/**
* The method called when a camera device has been closed with
@@ -749,7 +779,7 @@ public abstract class CameraDevice implements AutoCloseable {
*
* @param camera the camera device that has become closed
*/
- public void onClosed(CameraDevice camera) {
+ public void onClosed(@NonNull CameraDevice camera) {
// Default empty implementation
}
@@ -781,7 +811,7 @@ public abstract class CameraDevice implements AutoCloseable {
*
* @param camera the device that has been disconnected
*/
- public abstract void onDisconnected(CameraDevice camera); // Must implement
+ public abstract void onDisconnected(@NonNull CameraDevice camera); // Must implement
/**
* The method called when a camera device has encountered a serious error.
@@ -805,12 +835,14 @@ public abstract class CameraDevice implements AutoCloseable {
* @param error The error code, one of the
* {@code StateCallback.ERROR_*} values.
*
+ * @see #ERROR_CAMERA_IN_USE
+ * @see #ERROR_MAX_CAMERAS_IN_USE
+ * @see #ERROR_CAMERA_DISABLED
* @see #ERROR_CAMERA_DEVICE
* @see #ERROR_CAMERA_SERVICE
- * @see #ERROR_CAMERA_DISABLED
- * @see #ERROR_CAMERA_IN_USE
*/
- public abstract void onError(CameraDevice camera, int error); // Must implement
+ public abstract void onError(@NonNull CameraDevice camera,
+ @ErrorCode int error); // Must implement
}
/**
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index d99cce7..20e1610 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -16,6 +16,9 @@
package android.hardware.camera2;
+import android.annotation.RequiresPermission;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceListener;
@@ -86,6 +89,7 @@ public final class CameraManager {
*
* @return The list of currently connected camera devices.
*/
+ @NonNull
public String[] getCameraIdList() throws CameraAccessException {
synchronized (mLock) {
// ID list creation handles various known failures in device enumeration, so only
@@ -121,7 +125,8 @@ public final class CameraManager {
* @throws IllegalArgumentException if the handler is {@code null} but the current thread has
* no looper.
*/
- public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
+ public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback,
+ @Nullable Handler handler) {
if (handler == null) {
Looper looper = Looper.myLooper();
if (looper == null) {
@@ -142,7 +147,7 @@ public final class CameraManager {
*
* @param callback The callback to remove from the notification list
*/
- public void unregisterAvailabilityCallback(AvailabilityCallback callback) {
+ public void unregisterAvailabilityCallback(@NonNull AvailabilityCallback callback) {
CameraManagerGlobal.get().unregisterAvailabilityCallback(callback);
}
@@ -168,7 +173,7 @@ public final class CameraManager {
* @throws IllegalArgumentException if the handler is {@code null} but the current thread has
* no looper.
*/
- public void registerTorchCallback(TorchCallback callback, Handler handler) {
+ public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) {
if (handler == null) {
Looper looper = Looper.myLooper();
if (looper == null) {
@@ -188,7 +193,7 @@ public final class CameraManager {
*
* @param callback The callback to remove from the notification list
*/
- public void unregisterTorchCallback(TorchCallback callback) {
+ public void unregisterTorchCallback(@NonNull TorchCallback callback) {
CameraManagerGlobal.get().unregisterTorchCallback(callback);
}
@@ -201,15 +206,13 @@ public final class CameraManager {
*
* @throws IllegalArgumentException if the cameraId does not match any
* known camera device.
- * @throws CameraAccessException if the camera is disabled by device policy, or
- * the camera device has been disconnected.
- * @throws SecurityException if the application does not have permission to
- * access the camera
+ * @throws CameraAccessException if the camera device has been disconnected.
*
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
- public CameraCharacteristics getCameraCharacteristics(String cameraId)
+ @NonNull
+ public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId)
throws CameraAccessException {
CameraCharacteristics characteristics = null;
@@ -431,8 +434,9 @@ public final class CameraManager {
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
- public void openCamera(String cameraId, final CameraDevice.StateCallback callback,
- Handler handler)
+ @RequiresPermission(android.Manifest.permission.CAMERA)
+ public void openCamera(@NonNull String cameraId,
+ @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
throws CameraAccessException {
if (cameraId == null) {
@@ -444,7 +448,7 @@ public final class CameraManager {
handler = new Handler();
} else {
throw new IllegalArgumentException(
- "Looper doesn't exist in the calling thread");
+ "Handler argument is null, but no looper exists in the calling thread");
}
}
@@ -490,7 +494,8 @@ public final class CameraManager {
* or previously available camera device, or the camera device doesn't have a
* flash unit.
*/
- public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
+ public void setTorchMode(@NonNull String cameraId, boolean enabled)
+ throws CameraAccessException {
CameraManagerGlobal.get().setTorchMode(cameraId, enabled);
}
@@ -517,7 +522,7 @@ public final class CameraManager {
*
* @param cameraId The unique identifier of the new camera.
*/
- public void onCameraAvailable(String cameraId) {
+ public void onCameraAvailable(@NonNull String cameraId) {
// default empty implementation
}
@@ -532,7 +537,7 @@ public final class CameraManager {
*
* @param cameraId The unique identifier of the disconnected camera.
*/
- public void onCameraUnavailable(String cameraId) {
+ public void onCameraUnavailable(@NonNull String cameraId) {
// default empty implementation
}
}
@@ -572,7 +577,7 @@ public final class CameraManager {
* @param cameraId The unique identifier of the camera whose torch mode has become
* unavailable.
*/
- public void onTorchModeUnavailable(String cameraId) {
+ public void onTorchModeUnavailable(@NonNull String cameraId) {
// default empty implementation
}
@@ -589,7 +594,7 @@ public final class CameraManager {
* off. {@code false} when the torch mode has becomes off and available to
* be turned on.
*/
- public void onTorchModeChanged(String cameraId, boolean enabled) {
+ public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) {
// default empty implementation
}
}
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 6baa660..cf091a9 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -16,6 +16,7 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
@@ -103,6 +104,7 @@ public abstract class CameraMetadata<TKey> {
* @return List of the keys contained in this map.
*/
@SuppressWarnings("unchecked")
+ @NonNull
public List<TKey> getKeys() {
Class<CameraMetadata<TKey>> thisClass = (Class<CameraMetadata<TKey>>) getClass();
return Collections.unmodifiableList(
diff --git a/core/java/android/hardware/camera2/CaptureFailure.java b/core/java/android/hardware/camera2/CaptureFailure.java
index c168ff1..8bb33f1 100644
--- a/core/java/android/hardware/camera2/CaptureFailure.java
+++ b/core/java/android/hardware/camera2/CaptureFailure.java
@@ -15,6 +15,12 @@
*/
package android.hardware.camera2;
+import android.annotation.NonNull;
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A report of failed capture for a single image capture from the image sensor.
*
@@ -43,6 +49,13 @@ public class CaptureFailure {
*/
public static final int REASON_FLUSHED = 1;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ {REASON_ERROR,
+ REASON_FLUSHED })
+ public @interface FailureReason {};
+
private final CaptureRequest mRequest;
private final int mReason;
private final boolean mDropped;
@@ -52,8 +65,8 @@ public class CaptureFailure {
/**
* @hide
*/
- public CaptureFailure(CaptureRequest request, int reason, boolean dropped, int sequenceId,
- long frameNumber) {
+ public CaptureFailure(CaptureRequest request, int reason,
+ boolean dropped, int sequenceId, long frameNumber) {
mRequest = request;
mReason = reason;
mDropped = dropped;
@@ -81,6 +94,7 @@ public class CaptureFailure {
*
* @return The request associated with this failed capture. Never {@code null}.
*/
+ @NonNull
public CaptureRequest getRequest() {
return mRequest;
}
@@ -110,6 +124,7 @@ public class CaptureFailure {
* @see #REASON_ERROR
* @see #REASON_FLUSHED
*/
+ @FailureReason
public int getReason() {
return mReason;
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 5fb9abc..8f7b983 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -16,6 +16,8 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
@@ -117,6 +119,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
*
* @return String representation of the key name
*/
+ @NonNull
public String getName() {
return mKey.getName();
}
@@ -239,6 +242,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @param key The result field to read.
* @return The value of that key, or {@code null} if the field is not set.
*/
+ @Nullable
public <T> T get(Key<T> key) {
return mSettings.get(key);
}
@@ -268,6 +272,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* {@inheritDoc}
*/
@Override
+ @NonNull
public List<Key<?>> getKeys() {
// Force the javadoc for this function to show up on the CaptureRequest page
return super.getKeys();
@@ -286,6 +291,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* no tag has been set.
* @see Builder#setTag
*/
+ @Nullable
public Object getTag() {
return mUserTag;
}
@@ -476,7 +482,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
*
* @param outputTarget Surface to use as an output target for this request
*/
- public void addTarget(Surface outputTarget) {
+ public void addTarget(@NonNull Surface outputTarget) {
mRequest.mSurfaceSet.add(outputTarget);
}
@@ -487,7 +493,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
*
* @param outputTarget Surface to use as an output target for this request
*/
- public void removeTarget(Surface outputTarget) {
+ public void removeTarget(@NonNull Surface outputTarget) {
mRequest.mSurfaceSet.remove(outputTarget);
}
@@ -499,7 +505,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @param value The value to set the field to, which must be of a matching
* type to the key.
*/
- public <T> void set(Key<T> key, T value) {
+ public <T> void set(@NonNull Key<T> key, T value) {
mRequest.mSettings.set(key, value);
}
@@ -512,6 +518,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @param key The metadata field to read.
* @return The value of that key, or {@code null} if the field is not set.
*/
+ @Nullable
public <T> T get(Key<T> key) {
return mRequest.mSettings.get(key);
}
@@ -527,7 +534,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @param tag an arbitrary Object to store with this request
* @see CaptureRequest#getTag
*/
- public void setTag(Object tag) {
+ public void setTag(@Nullable Object tag) {
mRequest.mUserTag = tag;
}
@@ -543,6 +550,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @return A new capture request instance, ready for submission to the
* camera device.
*/
+ @NonNull
public CaptureRequest build() {
return new CaptureRequest(mRequest);
}
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 0277c5b..f4017d0 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -16,6 +16,8 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CaptureResultExtras;
import android.hardware.camera2.impl.PublicKey;
@@ -102,6 +104,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
*
* @return String representation of the key name
*/
+ @NonNull
public String getName() {
return mKey.getName();
}
@@ -216,6 +219,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* @param key The result field to read.
* @return The value of that key, or {@code null} if the field is not set.
*/
+ @Nullable
public <T> T get(Key<T> key) {
T value = mResults.get(key);
if (VERBOSE) Log.v(TAG, "#get for Key = " + key.getName() + ", returned value = " + value);
@@ -259,6 +263,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* {@inheritDoc}
*/
@Override
+ @NonNull
public List<Key<?>> getKeys() {
// Force the javadoc for this function to show up on the CaptureResult page
return super.getKeys();
@@ -285,6 +290,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
*
* @return The request associated with this result. Never {@code null}.
*/
+ @NonNull
public CaptureRequest getRequest() {
return mRequest;
}
diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
index f16d650..70afe5b 100644
--- a/core/java/android/hardware/camera2/DngCreator.java
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -16,6 +16,9 @@
package android.hardware.camera2;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.ImageFormat;
@@ -80,7 +83,8 @@ public final class DngCreator implements AutoCloseable {
* {@link android.hardware.camera2.CameraCharacteristics}.
* @param metadata a metadata object to generate tags from.
*/
- public DngCreator(CameraCharacteristics characteristics, CaptureResult metadata) {
+ public DngCreator(@NonNull CameraCharacteristics characteristics,
+ @NonNull CaptureResult metadata) {
if (characteristics == null || metadata == null) {
throw new IllegalArgumentException("Null argument to DngCreator constructor");
}
@@ -126,6 +130,7 @@ public final class DngCreator implements AutoCloseable {
* </ul>
* @return this {@link #DngCreator} object.
*/
+ @NonNull
public DngCreator setOrientation(int orientation) {
if (orientation < ExifInterface.ORIENTATION_UNDEFINED ||
orientation > ExifInterface.ORIENTATION_ROTATE_270) {
@@ -150,7 +155,8 @@ public final class DngCreator implements AutoCloseable {
* @throws java.lang.IllegalArgumentException if the given thumbnail image has a dimension
* larger than {@link #MAX_THUMBNAIL_DIMENSION}.
*/
- public DngCreator setThumbnail(Bitmap pixels) {
+ @NonNull
+ public DngCreator setThumbnail(@NonNull Bitmap pixels) {
if (pixels == null) {
throw new IllegalArgumentException("Null argument to setThumbnail");
}
@@ -185,7 +191,8 @@ public final class DngCreator implements AutoCloseable {
* @throws java.lang.IllegalArgumentException if the given thumbnail image has a dimension
* larger than {@link #MAX_THUMBNAIL_DIMENSION}.
*/
- public DngCreator setThumbnail(Image pixels) {
+ @NonNull
+ public DngCreator setThumbnail(@NonNull Image pixels) {
if (pixels == null) {
throw new IllegalArgumentException("Null argument to setThumbnail");
}
@@ -226,7 +233,8 @@ public final class DngCreator implements AutoCloseable {
* @throws java.lang.IllegalArgumentException if the given location object doesn't
* contain enough information to set location metadata.
*/
- public DngCreator setLocation(Location location) {
+ @NonNull
+ public DngCreator setLocation(@NonNull Location location) {
if (location == null) {
throw new IllegalArgumentException("Null location passed to setLocation");
}
@@ -258,7 +266,8 @@ public final class DngCreator implements AutoCloseable {
* @param description the user description string.
* @return this {@link #DngCreator} object.
*/
- public DngCreator setDescription(String description) {
+ @NonNull
+ public DngCreator setDescription(@NonNull String description) {
if (description == null) {
throw new IllegalArgumentException("Null description passed to setDescription.");
}
@@ -293,8 +302,8 @@ public final class DngCreator implements AutoCloseable {
* set to write a well-formatted DNG file.
* @throws java.lang.IllegalArgumentException if the size passed in does not match the
*/
- public void writeInputStream(OutputStream dngOutput, Size size, InputStream pixels, long offset)
- throws IOException {
+ public void writeInputStream(@NonNull OutputStream dngOutput, @NonNull Size size,
+ @NonNull InputStream pixels, @IntRange(from=0) long offset) throws IOException {
if (dngOutput == null) {
throw new IllegalArgumentException("Null dngOutput passed to writeInputStream");
} else if (size == null) {
@@ -345,7 +354,8 @@ public final class DngCreator implements AutoCloseable {
* @throws java.lang.IllegalStateException if not enough metadata information has been
* set to write a well-formatted DNG file.
*/
- public void writeByteBuffer(OutputStream dngOutput, Size size, ByteBuffer pixels, long offset)
+ public void writeByteBuffer(@NonNull OutputStream dngOutput, @NonNull Size size,
+ @NonNull ByteBuffer pixels, @IntRange(from=0) long offset)
throws IOException {
if (dngOutput == null) {
throw new IllegalArgumentException("Null dngOutput passed to writeByteBuffer");
@@ -381,7 +391,8 @@ public final class DngCreator implements AutoCloseable {
* @throws java.lang.IllegalStateException if not enough metadata information has been
* set to write a well-formatted DNG file.
*/
- public void writeImage(OutputStream dngOutput, Image pixels) throws IOException {
+ public void writeImage(@NonNull OutputStream dngOutput, @NonNull Image pixels)
+ throws IOException {
if (dngOutput == null) {
throw new IllegalArgumentException("Null dngOutput to writeImage");
} else if (pixels == null) {
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index fb3c098..657745c 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -16,6 +16,7 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CaptureResultExtras;
@@ -96,6 +97,7 @@ public final class TotalCaptureResult extends CaptureResult {
*
* @return unmodifiable list of partial results
*/
+ @NonNull
public List<CaptureResult> getPartialResults() {
return Collections.unmodifiableList(mPartialResults);
}
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 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
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/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 4508dc8..c4e8b15 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -607,6 +607,8 @@ public class CameraDeviceImpl extends CameraDevice {
}
public void prepare(Surface surface) throws CameraAccessException {
+ if (surface == null) throw new IllegalArgumentException("Surface is null");
+
synchronized(mInterfaceLock) {
int streamId = -1;
for (int i = 0; i < mConfiguredOutputs.size(); i++) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 26878c0..e175e9a 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1906,9 +1906,6 @@ public class ConnectivityManager {
*
* @return {@link ProxyInfo} for the current global HTTP proxy or {@code null}
* if no global HTTP proxy is set.
- *
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* @hide
*/
public ProxyInfo getGlobalProxy() {
@@ -1920,6 +1917,28 @@ public class ConnectivityManager {
}
/**
+ * Retrieve the global HTTP proxy, or if no global HTTP proxy is set, a
+ * network-specific HTTP proxy. If {@code network} is null, the
+ * network-specific proxy returned is the proxy of the default active
+ * network.
+ *
+ * @return {@link ProxyInfo} for the current global HTTP proxy, or if no
+ * global HTTP proxy is set, {@code ProxyInfo} for {@code network},
+ * or when {@code network} is {@code null},
+ * the {@code ProxyInfo} for the default active network. Returns
+ * {@code null} when no proxy applies or the caller doesn't have
+ * permission to use {@code network}.
+ * @hide
+ */
+ public ProxyInfo getProxyForNetwork(Network network) {
+ try {
+ return mService.getProxyForNetwork(network);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
* Get the current default HTTP proxy settings. If a global proxy is set it will be returned,
* otherwise if this process is bound to a {@link Network} using
* {@link #bindProcessToNetwork} then that {@code Network}'s proxy is returned, otherwise
@@ -1929,19 +1948,7 @@ public class ConnectivityManager {
* HTTP proxy is active.
*/
public ProxyInfo getDefaultProxy() {
- final Network network = getBoundNetworkForProcess();
- if (network != null) {
- final ProxyInfo globalProxy = getGlobalProxy();
- if (globalProxy != null) return globalProxy;
- final LinkProperties lp = getLinkProperties(network);
- if (lp != null) return lp.getHttpProxy();
- return null;
- }
- try {
- return mService.getDefaultProxy();
- } catch (RemoteException e) {
- return null;
- }
+ return getProxyForNetwork(getBoundNetworkForProcess());
}
/**
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 77200a5..89d23a2 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -104,15 +104,15 @@ interface IConnectivityManager
void setGlobalProxy(in ProxyInfo p);
- ProxyInfo getDefaultProxy();
+ ProxyInfo getProxyForNetwork(in Network nework);
- 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 @@ public class IpReachabilityMonitor {
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 @@ public class IpReachabilityMonitor {
}
}
+ 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 class IpReachabilityMonitor {
}
}
+ 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 @@ public class IpReachabilityMonitor {
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/Network.java b/core/java/android/net/Network.java
index 67ecb5d..754c6b3 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -247,12 +247,7 @@ public class Network implements Parcelable {
throw new IOException("No ConnectivityManager yet constructed, please construct one");
}
// TODO: Should this be optimized to avoid fetching the global proxy for every request?
- ProxyInfo proxyInfo = cm.getGlobalProxy();
- if (proxyInfo == null) {
- // TODO: Should this be optimized to avoid fetching LinkProperties for every request?
- final LinkProperties lp = cm.getLinkProperties(this);
- if (lp != null) proxyInfo = lp.getHttpProxy();
- }
+ final ProxyInfo proxyInfo = cm.getProxyForNetwork(this);
java.net.Proxy proxy = null;
if (proxyInfo != null) {
proxy = proxyInfo.makeProxy();
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 @@ public final class NetworkCapabilities implements Parcelable {
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/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 29dd8ad..4487cab 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -194,6 +194,12 @@ public class NetworkUtils {
public native static boolean protectFromVpn(int socketfd);
/**
+ * Determine if {@code uid} can access network designated by {@code netId}.
+ * @return {@code true} if {@code uid} can access network, {@code false} otherwise.
+ */
+ public native static boolean queryUserAccess(int uid, int netId);
+
+ /**
* Convert a IPv4 address from an integer to an InetAddress.
* @param hostAddress an int corresponding to the IPv4 address in network byte order
*/
diff --git a/core/java/android/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 class VpnService extends Service {
*/
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 @@ public class VpnService extends Service {
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.StructNdMsg;
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 @@ public class RtNetlinkNeighborMessage extends NetlinkMessage {
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 @@ public class RtNetlinkNeighborMessage extends NetlinkMessage {
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 @@ public class StructNdMsg {
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 @@ public class StructNdMsg {
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 class StructNlAttr {
}
}
+ 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 @@ public class StructNlMsgErr {
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 @@ public class StructNlMsgErr {
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 class StructNlMsgHdr {
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 @@ public class StructNlMsgHdr {
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 @@ public class StructNlMsgHdr {
byteBuffer.putShort(nlmsg_flags);
byteBuffer.putInt(nlmsg_seq);
byteBuffer.putInt(nlmsg_pid);
- return true;
}
@Override
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index f212daf..aa4b051 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -36,6 +36,7 @@ interface IUserManager {
ParcelFileDescriptor getUserIcon(int userHandle);
List<UserInfo> getUsers(boolean excludeDying);
List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
+ boolean canAddMoreManagedProfiles();
UserInfo getProfileParent(int userHandle);
UserInfo getUserInfo(int userHandle);
long getUserCreationTime(int userHandle);
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 @@ public final class MessageQueue {
}
/**
- * 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 @@ public final class MessageQueue {
}
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 @@ public final class MessageQueue {
// 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 @@ public final class MessageQueue {
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 @@ public final class MessageQueue {
}
/**
- * 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 @@ public final class MessageQueue {
* 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 @@ public final class MessageQueue {
* 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 @@ public final class MessageQueue {
/**
* 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 static android.system.OsConstants.S_ISREG;
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 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
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/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 9a0d0d0..e523285 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -136,4 +136,8 @@ public abstract class PowerManagerInternal {
public abstract void setDeviceIdleMode(boolean enabled);
public abstract void setDeviceIdleWhitelist(int[] appids);
+
+ public abstract void updateUidProcState(int uid, int procState);
+
+ public abstract void uidGone(int uid);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 3dee68c..11043b3 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -933,6 +933,22 @@ public class UserManager {
}
/**
+ * Checks whether it's possible to add more managed profiles. Caller must hold the MANAGE_USERS
+ * permission.
+ *
+ * @return true if more managed profiles can be added, false if limit has been reached.
+ * @hide
+ */
+ public boolean canAddMoreManagedProfiles() {
+ try {
+ return mService.canAddMoreManagedProfiles();
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not check if we can add more managed profiles", re);
+ return false;
+ }
+ }
+
+ /**
* Returns list of the profiles of userHandle including
* userHandle itself.
* Note that this returns both enabled and not enabled profiles. See
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 final class Settings {
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.ComponentName;
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 @@ public abstract class NotificationListenerService extends Service {
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 @@ public abstract class NotificationListenerService extends Service {
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 @@ public abstract class NotificationListenerService extends Service {
}
}
+ /** 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 @@ public abstract class NotificationListenerService extends Service {
}
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 class VoiceInteractionService extends Service {
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 abstract class VoiceInteractionSession implements KeyEvent.Callback,
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 @@ public abstract class WallpaperService extends Service {
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 @@ public abstract class WallpaperService extends Service {
// 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 @@ public abstract class WallpaperService extends Service {
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/util/SparseArray.java b/core/java/android/util/SparseArray.java
index 92e874f..dc965ed 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -134,6 +134,24 @@ public class SparseArray<E> implements Cloneable {
}
/**
+ * @hide
+ * Removes the mapping from the specified key, if there was any, returning the old value.
+ */
+ public E removeReturnOld(int key) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i >= 0) {
+ if (mValues[i] != DELETED) {
+ final E old = (E) mValues[i];
+ mValues[i] = DELETED;
+ mGarbage = true;
+ return old;
+ }
+ }
+ return null;
+ }
+
+ /**
* Alias for {@link #delete(int)}.
*/
public void remove(int key) {
diff --git a/core/java/android/view/ActionMode.java b/core/java/android/view/ActionMode.java
index 9f202a9..9f00455 100644
--- a/core/java/android/view/ActionMode.java
+++ b/core/java/android/view/ActionMode.java
@@ -44,6 +44,12 @@ public abstract class ActionMode {
*/
public static final int TYPE_FLOATING = 1;
+ /**
+ * Default snooze time.
+ */
+ public static final int SNOOZE_TIME_DEFAULT =
+ ViewConfiguration.getDefaultActionModeSnoozeTime();
+
private Object mTag;
private boolean mTitleOptionalHint;
private int mType = TYPE_PRIMARY;
@@ -207,6 +213,19 @@ public abstract class ActionMode {
public void invalidateContentRect() {}
/**
+ * Hide the action mode view from obstructing the content below for a short period.
+ * This only makes sense for action modes that support dynamic positioning on the screen.
+ * If this method is called again before the snooze time expires, the later snooze will
+ * cancel the former and then take effect.
+ * NOTE that there is an internal limit to how long the mode can be snoozed for. It's typically
+ * about a few seconds.
+ *
+ * @param snoozeTime The number of milliseconds to snooze for.
+ * @see #SNOOZE_TIME_DEFAULT
+ */
+ public void snooze(int snoozeTime) {}
+
+ /**
* Finish and close this action mode. The action mode's {@link ActionMode.Callback} will
* have its {@link Callback#onDestroyActionMode(ActionMode)} method called.
*/
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 final class Display {
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 @@ public final class DisplayInfo implements Parcelable {
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/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 4e91ad4..8c6fa3f 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -213,6 +213,11 @@ public class ViewConfiguration {
private static final int OVERFLING_DISTANCE = 6;
/**
+ * Default time to snooze an action mode for.
+ */
+ private static final int ACTION_MODE_SNOOZE_TIME_DEFAULT = 2000;
+
+ /**
* Configuration values for overriding {@link #hasPermanentMenuKey()} behavior.
* These constants must match the definition in res/values/config.xml.
*/
@@ -732,6 +737,13 @@ public class ViewConfiguration {
}
/**
+ * @return the default duration in milliseconds for {@link ActionMode#snooze(int)}.
+ */
+ public static int getDefaultActionModeSnoozeTime() {
+ return ACTION_MODE_SNOOZE_TIME_DEFAULT;
+ }
+
+ /**
* Report if the device has a permanent menu key available to the user.
*
* <p>As of Android 3.0, devices may not have a permanent menu key available.
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 @@ public class ViewPropertyAnimator {
/**
* 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 @@ public class ViewPropertyAnimator {
* @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 @@ public class ViewPropertyAnimator {
* @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 @@ public class ViewPropertyAnimator {
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..c4f9209 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -122,7 +122,6 @@ public final class ViewRootImpl implements ViewParent,
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 @@ public final class ViewRootImpl implements ViewParent,
/** Set to true once doDie() has been called. */
private boolean mRemoved;
- private final boolean mWindowIsRound;
-
/**
* Consistency verifier for debugging purposes.
*/
@@ -397,7 +394,6 @@ public final class ViewRootImpl implements ViewParent,
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 @@ public final class ViewRootImpl implements ViewParent,
stableInsets = mPendingStableInsets;
}
mLastWindowInsets = new WindowInsets(contentInsets,
- null /* windowDecorInsets */, stableInsets, mWindowIsRound);
+ null /* windowDecorInsets */, stableInsets,
+ mContext.getResources().getConfiguration().isScreenRound());
}
return mLastWindowInsets;
}
@@ -2024,8 +2021,8 @@ public final class ViewRootImpl implements ViewParent,
mLastWasImTarget = imTarget;
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null && imTarget) {
- imm.startGettingWindowFocus(mView);
- imm.onWindowFocus(mView, mView.findFocus(),
+ imm.onPreWindowFocus(mView, true /* hasWindowFocus */);
+ imm.onPostWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
}
@@ -3330,11 +3327,10 @@ public final class ViewRootImpl implements ViewParent,
.mayUseInputMethod(mWindowAttributes.flags);
InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
+ imm.onPreWindowFocus(mView, hasWindowFocus);
+ }
if (mView != null) {
- if (hasWindowFocus && imm != null && mLastWasImTarget &&
- !isInLocalFocusMode()) {
- imm.startGettingWindowFocus(mView);
- }
mAttachInfo.mKeyDispatchState.reset();
mView.dispatchWindowFocusChanged(hasWindowFocus);
mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
@@ -3344,7 +3340,7 @@ public final class ViewRootImpl implements ViewParent,
// so all of the view state is set up correctly.
if (hasWindowFocus) {
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
- imm.onWindowFocus(mView, mView.findFocus(),
+ imm.onPostWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
}
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 8dc49ac..5c8b023 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -25,60 +25,215 @@ import android.text.TextPaint;
* View.onProvideStructure}.
*/
public abstract class ViewStructure {
+ /**
+ * Set the identifier for this view.
+ *
+ * @param id The view's identifier, as per {@link View#getId View.getId()}.
+ * @param packageName The package name of the view's identifier, or null if there is none.
+ * @param typeName The type name of the view's identifier, or null if there is none.
+ * @param entryName The entry name of the view's identifier, or null if there is none.
+ */
public abstract void setId(int id, String packageName, String typeName, String entryName);
+ /**
+ * Set the basic dimensions of this view.
+ *
+ * @param left The view's left position, in pixels relative to its parent's left edge.
+ * @param top The view's top position, in pixels relative to its parent's top edge.
+ * @param scrollX How much the view's x coordinate space has been scrolled, in pixels.
+ * @param scrollY How much the view's y coordinate space has been scrolled, in pixels.
+ * @param width The view's visible width, in pixels. This is the width visible on screen,
+ * not the total data width of a scrollable view.
+ * @param height The view's visible height, in pixels. This is the height visible on
+ * screen, not the total data height of a scrollable view.
+ */
public abstract void setDimens(int left, int top, int scrollX, int scrollY, int width,
int height);
+ /**
+ * Set the visibility state of this view, as per
+ * {@link View#getVisibility View.getVisibility()}.
+ */
public abstract void setVisibility(int visibility);
/** @hide */
public abstract void setAssistBlocked(boolean state);
+ /**
+ * Set the enabled state of this view, as per {@link View#isEnabled View.isEnabled()}.
+ */
public abstract void setEnabled(boolean state);
+ /**
+ * Set the clickable state of this view, as per {@link View#isClickable View.isClickable()}.
+ */
public abstract void setClickable(boolean state);
+ /**
+ * Set the long clickable state of this view, as per
+ * {@link View#isLongClickable View.isLongClickable()}.
+ */
public abstract void setLongClickable(boolean state);
+ /**
+ * Set the stylus button pressable state of this view, as per
+ * {@link View#isStylusButtonPressable View.isStylusButtonPressable()}.
+ */
public abstract void setStylusButtonPressable(boolean state);
+ /**
+ * Set the focusable state of this view, as per {@link View#isFocusable View.isFocusable()}.
+ */
public abstract void setFocusable(boolean state);
+ /**
+ * Set the focused state of this view, as per {@link View#isFocused View.isFocused()}.
+ */
public abstract void setFocused(boolean state);
+ /**
+ * Set the accessibility focused state of this view, as per
+ * {@link View#isAccessibilityFocused View.isAccessibilityFocused()}.
+ */
public abstract void setAccessibilityFocused(boolean state);
+ /**
+ * Set the checkable state of this view, such as whether it implements the
+ * {@link android.widget.Checkable} interface.
+ */
public abstract void setCheckable(boolean state);
+ /**
+ * Set the checked state of this view, such as
+ * {@link android.widget.Checkable#isChecked Checkable.isChecked()}.
+ */
public abstract void setChecked(boolean state);
+ /**
+ * Set the selected state of this view, as per {@link View#isSelected View.isSelected()}.
+ */
public abstract void setSelected(boolean state);
+ /**
+ * Set the activated state of this view, as per {@link View#isActivated View.isActivated()}.
+ */
public abstract void setActivated(boolean state);
+ /**
+ * Set the class name of the view, as per
+ * {@link View#getAccessibilityClassName View.getAccessibilityClassName()}.
+ */
public abstract void setClassName(String className);
+ /**
+ * Set the content description of the view, as per
+ * {@link View#getContentDescription View.getContentDescription()}.
+ */
public abstract void setContentDescription(CharSequence contentDescription);
+ /**
+ * Set the text that is associated with this view. There is no selection
+ * associated with the text. The text may have style spans to supply additional
+ * display and semantic information.
+ */
public abstract void setText(CharSequence text);
+
+ /**
+ * Like {@link #setText(CharSequence)} but with an active selection
+ * extending from <var>selectionStart</var> through <var>selectionEnd</var>.
+ */
public abstract void setText(CharSequence text, int selectionStart, int selectionEnd);
+
+ /**
+ * Set default global style of the text previously set with
+ * {@link #setText}, derived from the given TextPaint object. Size, foreground color,
+ * background color, and style information will be extracted from the paint.
+ */
public abstract void setTextPaint(TextPaint paint);
+
+ /**
+ * Explicitly set default global style information for text that was previously set with
+ * {@link #setText}.
+ *
+ * @param size The size, in pixels, of the text.
+ * @param fgColor The foreground color, packed as 0xAARRGGBB.
+ * @param bgColor The background color, packed as 0xAARRGGBB.
+ * @param style Style flags, as defined by {@link android.app.AssistStructure.ViewNode}.
+ */
+ public abstract void setTextStyle(int size, int fgColor, int bgColor, int style);
+
+ /**
+ * Set optional hint text associated with this view; this is for example the text that is
+ * shown by an EditText when it is empty to indicate to the user the kind of text to input.
+ */
public abstract void setHint(CharSequence hint);
+ /**
+ * Retrieve the last {@link #setText(CharSequence)}.
+ */
public abstract CharSequence getText();
+
+ /**
+ * Retrieve the last selection start set by {@link #setText(CharSequence, int, int)}.
+ */
public abstract int getTextSelectionStart();
+
+ /**
+ * Retrieve the last selection end set by {@link #setText(CharSequence, int, int)}.
+ */
public abstract int getTextSelectionEnd();
+
+ /**
+ * Retrieve the last hint set by {@link #setHint}.
+ */
public abstract CharSequence getHint();
+ /**
+ * Get extra data associated with this view structure; the returned Bundle is mutable,
+ * allowing you to view and modify its contents. Keys placed in the Bundle should use
+ * an appropriate namespace prefix (such as com.google.MY_KEY) to avoid conflicts.
+ */
public abstract Bundle getExtras();
+
+ /**
+ * Returns true if {@link #getExtras} has been used to create extra content.
+ */
public abstract boolean hasExtras();
+ /**
+ * Set the number of children of this view, which defines the range of indices you can
+ * use with {@link #newChild} and {@link #asyncNewChild}. Calling this method again
+ * resets all of the child state of the view, removing any children that had previously
+ * been added.
+ */
public abstract void setChildCount(int num);
+
+ /**
+ * Return the child count as set by {@link #setChildCount}.
+ */
public abstract int getChildCount();
+
+ /**
+ * Create a new child {@link ViewStructure} in this view, putting into the list of
+ * children at <var>index</var>.
+ * @return Returns an fresh {@link ViewStructure} ready to be filled in.
+ */
public abstract ViewAssistStructure newChild(int index);
+ /**
+ * Like {@link #newChild}, but allows the caller to asynchronously populate the returned
+ * child. It can transfer the returned {@link ViewStructure} to another thread for it
+ * to build its content (and children etc). Once done, some thread must call
+ * {@link #asyncCommit} to tell the containing {@link ViewStructure} that the async
+ * population is done.
+ * @return Returns an fresh {@link ViewStructure} ready to be filled in.
+ */
public abstract ViewAssistStructure asyncNewChild(int index);
+
+ /**
+ * Call when done populating a {@link ViewStructure} returned by
+ * {@link #asyncNewChild}.
+ */
public abstract void asyncCommit();
/** @hide */
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 568e160..824b434 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1289,14 +1289,14 @@ public final class InputMethodManager {
void focusInLocked(View view) {
if (DEBUG) Log.v(TAG, "focusIn: " + view);
-
+
if (mCurRootView != view.getRootView()) {
// This is a request from a window that isn't in the window with
// IME focus, so ignore it.
if (DEBUG) Log.v(TAG, "Not IME target window, ignoring");
return;
}
-
+
mNextServedView = view;
scheduleCheckFocusLocked(view);
}
@@ -1392,7 +1392,7 @@ public final class InputMethodManager {
* Called by ViewAncestor when its window gets input focus.
* @hide
*/
- public void onWindowFocus(View rootView, View focusedView, int softInputMode,
+ public void onPostWindowFocus(View rootView, View focusedView, int softInputMode,
boolean first, int windowFlags) {
boolean forceNewFocus = false;
synchronized (mH) {
@@ -1441,14 +1441,27 @@ public final class InputMethodManager {
}
}
}
-
+
/** @hide */
- public void startGettingWindowFocus(View rootView) {
+ public void onPreWindowFocus(View rootView, boolean hasWindowFocus) {
synchronized (mH) {
- mCurRootView = rootView;
+ if (rootView == null) {
+ mCurRootView = null;
+ } if (hasWindowFocus) {
+ mCurRootView = rootView;
+ } else if (rootView == mCurRootView) {
+ // If the mCurRootView is losing window focus, release the strong reference to it
+ // so as not to prevent it from being garbage-collected.
+ mCurRootView = null;
+ } else {
+ if (DEBUG) {
+ Log.v(TAG, "Ignoring onPreWindowFocus()."
+ + " mCurRootView=" + mCurRootView + " rootView=" + rootView);
+ }
+ }
}
}
-
+
/**
* Report the current selection range.
*
diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java
index ab6a2f9..b3b95f8 100644
--- a/core/java/android/webkit/FindActionModeCallback.java
+++ b/core/java/android/webkit/FindActionModeCallback.java
@@ -154,9 +154,9 @@ public class FindActionModeCallback implements ActionMode.Callback, TextWatcher,
}
public void showSoftInput() {
- mInput.startGettingWindowFocus(mEditText.getRootView());
- mInput.focusIn(mEditText);
- mInput.showSoftInput(mEditText, 0);
+ if (mEditText.requestFocus()) {
+ mInput.showSoftInput(mEditText, 0);
+ }
}
public void updateMatchCount(int matchIndex, int matchCount, boolean isEmptyFind) {
diff --git a/core/java/android/webkit/ViewAssistStructure.java b/core/java/android/webkit/ViewAssistStructure.java
index 6f7a645..bbaceee 100644
--- a/core/java/android/webkit/ViewAssistStructure.java
+++ b/core/java/android/webkit/ViewAssistStructure.java
@@ -137,6 +137,11 @@ public class ViewAssistStructure extends android.view.ViewAssistStructure {
}
@Override
+ public void setTextStyle(int size, int fgColor, int bgColor, int style) {
+ mV.setTextStyle(size, fgColor, bgColor, style);
+ }
+
+ @Override
public void setHint(CharSequence hint) {
mV.setHint(hint);
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index c829783..d558c7b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -233,6 +233,24 @@ public class Editor {
final CursorAnchorInfoNotifier mCursorAnchorInfoNotifier = new CursorAnchorInfoNotifier();
+ private final Runnable mHideFloatingToolbar = new Runnable() {
+ @Override
+ public void run() {
+ if (mSelectionActionMode != null) {
+ mSelectionActionMode.snooze(ActionMode.SNOOZE_TIME_DEFAULT);
+ }
+ }
+ };
+
+ private final Runnable mShowFloatingToolbar = new Runnable() {
+ @Override
+ public void run() {
+ if (mSelectionActionMode != null) {
+ mSelectionActionMode.snooze(0); // snooze off.
+ }
+ }
+ };
+
Editor(TextView textView) {
mTextView = textView;
// Synchronize the filter list, which places the undo input filter at the end.
@@ -358,6 +376,9 @@ public class Editor {
mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable);
}
+ mTextView.removeCallbacks(mHideFloatingToolbar);
+ mTextView.removeCallbacks(mShowFloatingToolbar);
+
destroyDisplayListsData();
if (mSpellChecker != null) {
@@ -1169,6 +1190,8 @@ public class Editor {
}
void onTouchEvent(MotionEvent event) {
+ updateFloatingToolbarVisibility(event);
+
if (hasSelectionController()) {
getSelectionController().onTouchEvent(event);
}
@@ -1189,6 +1212,37 @@ public class Editor {
}
}
+ private void updateFloatingToolbarVisibility(MotionEvent event) {
+ if (mSelectionActionMode != null) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_MOVE:
+ hideFloatingToolbar();
+ break;
+ case MotionEvent.ACTION_UP: // fall through
+ case MotionEvent.ACTION_CANCEL:
+ showFloatingToolbar();
+ }
+ }
+ }
+
+ private void hideFloatingToolbar() {
+ if (mSelectionActionMode != null) {
+ mTextView.removeCallbacks(mShowFloatingToolbar);
+ // Delay the "hide" a little bit just in case a "show" will happen almost immediately.
+ mTextView.postDelayed(mHideFloatingToolbar, 100);
+ }
+ }
+
+ private void showFloatingToolbar() {
+ if (mSelectionActionMode != null) {
+ mTextView.removeCallbacks(mHideFloatingToolbar);
+ // Delay "show" so it doesn't interfere with click confirmations
+ // or double-clicks that could "dismiss" the floating toolbar.
+ int delay = ViewConfiguration.getDoubleTapTimeout();
+ mTextView.postDelayed(mShowFloatingToolbar, delay);
+ }
+ }
+
public void beginBatchEdit() {
mInBatchEditControllers = true;
final InputMethodState ims = mInputMethodState;
@@ -3661,6 +3715,8 @@ public class Editor {
@Override
public boolean onTouchEvent(MotionEvent ev) {
+ updateFloatingToolbarVisibility(ev);
+
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
startTouchUpFilter(getCurrentCursorOffset());
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.Nullable;
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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
void replaceSelectionWithText(CharSequence text) {
((Editable) mText).replace(getSelectionStart(), getSelectionEnd(), text);
- mEditor.startSelectionActionModeWithSelection();
}
/**
diff --git a/core/java/com/android/internal/alsa/AlsaCardsParser.java b/core/java/com/android/internal/alsa/AlsaCardsParser.java
index 5c0a888..67ee085 100644
--- a/core/java/com/android/internal/alsa/AlsaCardsParser.java
+++ b/core/java/com/android/internal/alsa/AlsaCardsParser.java
@@ -29,7 +29,7 @@ import java.util.ArrayList;
*/
public class AlsaCardsParser {
private static final String TAG = "AlsaCardsParser";
- protected static final boolean DEBUG = true;
+ protected static final boolean DEBUG = false;
private static final String kCardsFilePath = "/proc/asound/cards";
diff --git a/core/java/com/android/internal/app/ProcessMap.java b/core/java/com/android/internal/app/ProcessMap.java
index 6ff0304..23a8bd7 100644
--- a/core/java/com/android/internal/app/ProcessMap.java
+++ b/core/java/com/android/internal/app/ProcessMap.java
@@ -39,14 +39,16 @@ public class ProcessMap<E> {
return value;
}
- public void remove(String name, int uid) {
+ public E remove(String name, int uid) {
SparseArray<E> uids = mMap.get(name);
if (uids != null) {
- uids.remove(uid);
+ final E old = uids.removeReturnOld(uid);
if (uids.size() == 0) {
mMap.remove(name);
}
+ return old;
}
+ return null;
}
public ArrayMap<String, SparseArray<E>> getMap() {
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 interface MetricsConstants {
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 @@ public class ScreenShapeHelper {
}
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/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index c3f4da7..0195208 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -30,6 +30,9 @@ import com.android.internal.widget.FloatingToolbar;
public class FloatingActionMode extends ActionMode {
+ private static final int MAX_SNOOZE_TIME = 3000;
+ private static final int MOVING_HIDE_DELAY = 300;
+
private final Context mContext;
private final ActionMode.Callback2 mCallback;
private final MenuBuilder mMenu;
@@ -37,13 +40,29 @@ public class FloatingActionMode extends ActionMode {
private final Rect mContentRectOnWindow;
private final Rect mPreviousContentRectOnWindow;
private final int[] mViewPosition;
+ private final Rect mViewRect;
+ private final Rect mScreenRect;
private final View mOriginatingView;
+
+ private final Runnable mMovingOff = new Runnable() {
+ public void run() {
+ mFloatingToolbarVisibilityHelper.setMoving(false);
+ }
+ };
+
+ private final Runnable mSnoozeOff = new Runnable() {
+ public void run() {
+ mFloatingToolbarVisibilityHelper.setSnoozed(false);
+ }
+ };
+
private FloatingToolbar mFloatingToolbar;
+ private FloatingToolbarVisibilityHelper mFloatingToolbarVisibilityHelper;
public FloatingActionMode(
Context context, ActionMode.Callback2 callback, View originatingView) {
- mContext = context;
- mCallback = callback;
+ mContext = Preconditions.checkNotNull(context);
+ mCallback = Preconditions.checkNotNull(callback);
mMenu = new MenuBuilder(context).setDefaultShowAsAction(
MenuItem.SHOW_AS_ACTION_IF_ROOM);
setType(ActionMode.TYPE_FLOATING);
@@ -51,7 +70,10 @@ public class FloatingActionMode extends ActionMode {
mContentRectOnWindow = new Rect();
mPreviousContentRectOnWindow = new Rect();
mViewPosition = new int[2];
- mOriginatingView = originatingView;
+ mViewRect = new Rect();
+ mScreenRect = new Rect();
+ mOriginatingView = Preconditions.checkNotNull(originatingView);
+ mOriginatingView.getLocationInWindow(mViewPosition);
}
public void setFloatingToolbar(FloatingToolbar floatingToolbar) {
@@ -63,6 +85,7 @@ public class FloatingActionMode extends ActionMode {
return mCallback.onActionItemClicked(FloatingActionMode.this, item);
}
});
+ mFloatingToolbarVisibilityHelper = new FloatingToolbarVisibilityHelper(mFloatingToolbar);
}
@Override
@@ -82,7 +105,7 @@ public class FloatingActionMode extends ActionMode {
@Override
public void invalidate() {
- Preconditions.checkNotNull(mFloatingToolbar);
+ checkToolbarInitialized();
mCallback.onPrepareActionMode(this, mMenu);
mFloatingToolbar.updateLayout();
invalidateContentRect();
@@ -90,32 +113,79 @@ public class FloatingActionMode extends ActionMode {
@Override
public void invalidateContentRect() {
- Preconditions.checkNotNull(mFloatingToolbar);
+ checkToolbarInitialized();
mCallback.onGetContentRect(this, mOriginatingView, mContentRect);
repositionToolbar();
}
public void updateViewLocationInWindow() {
- Preconditions.checkNotNull(mFloatingToolbar);
+ checkToolbarInitialized();
mOriginatingView.getLocationInWindow(mViewPosition);
+ mViewRect.set(
+ mViewPosition[0],
+ mViewPosition[1],
+ mViewPosition[0] + mOriginatingView.getWidth(),
+ mViewPosition[1] + mOriginatingView.getHeight());
repositionToolbar();
}
private void repositionToolbar() {
+ checkToolbarInitialized();
mContentRectOnWindow.set(
mContentRect.left + mViewPosition[0],
mContentRect.top + mViewPosition[1],
mContentRect.right + mViewPosition[0],
mContentRect.bottom + mViewPosition[1]);
if (!mContentRectOnWindow.equals(mPreviousContentRectOnWindow)) {
+ if (!mPreviousContentRectOnWindow.isEmpty()) {
+ notifyContentRectMoving();
+ }
mFloatingToolbar.setContentRect(mContentRectOnWindow);
mFloatingToolbar.updateLayout();
}
mPreviousContentRectOnWindow.set(mContentRectOnWindow);
+
+ if (isContentRectWithinBounds()) {
+ mFloatingToolbarVisibilityHelper.setOutOfBounds(false);
+ } else {
+ mFloatingToolbarVisibilityHelper.setOutOfBounds(true);
+ }
+ }
+
+ private boolean isContentRectWithinBounds() {
+ mScreenRect.set(
+ 0,
+ 0,
+ mContext.getResources().getDisplayMetrics().widthPixels,
+ mContext.getResources().getDisplayMetrics().heightPixels);
+
+ return Rect.intersects(mContentRectOnWindow, mScreenRect)
+ && Rect.intersects(mContentRectOnWindow, mViewRect);
+ }
+
+ private void notifyContentRectMoving() {
+ mOriginatingView.removeCallbacks(mMovingOff);
+ mFloatingToolbarVisibilityHelper.setMoving(true);
+ mOriginatingView.postDelayed(mMovingOff, MOVING_HIDE_DELAY);
+ }
+
+ @Override
+ public void snooze(int snoozeTime) {
+ checkToolbarInitialized();
+ snoozeTime = Math.min(MAX_SNOOZE_TIME, snoozeTime);
+ mOriginatingView.removeCallbacks(mSnoozeOff);
+ if (snoozeTime <= 0) {
+ mSnoozeOff.run();
+ } else {
+ mFloatingToolbarVisibilityHelper.setSnoozed(true);
+ mOriginatingView.postDelayed(mSnoozeOff, snoozeTime);
+ }
}
@Override
public void finish() {
+ checkToolbarInitialized();
+ reset();
mCallback.onDestroyActionMode(this);
}
@@ -144,4 +214,56 @@ public class FloatingActionMode extends ActionMode {
return new MenuInflater(mContext);
}
+ /**
+ * @throws IlllegalStateException
+ */
+ private void checkToolbarInitialized() {
+ Preconditions.checkState(mFloatingToolbar != null);
+ Preconditions.checkState(mFloatingToolbarVisibilityHelper != null);
+ }
+
+ private void reset() {
+ mOriginatingView.removeCallbacks(mMovingOff);
+ mOriginatingView.removeCallbacks(mSnoozeOff);
+ }
+
+
+ /**
+ * A helper that shows/hides the floating toolbar depending on certain states.
+ */
+ private static final class FloatingToolbarVisibilityHelper {
+
+ private final FloatingToolbar mToolbar;
+
+ private boolean mSnoozed;
+ private boolean mMoving;
+ private boolean mOutOfBounds;
+
+ public FloatingToolbarVisibilityHelper(FloatingToolbar toolbar) {
+ mToolbar = Preconditions.checkNotNull(toolbar);
+ }
+
+ public void setSnoozed(boolean snoozed) {
+ mSnoozed = snoozed;
+ updateToolbarVisibility();
+ }
+
+ public void setMoving(boolean moving) {
+ mMoving = moving;
+ updateToolbarVisibility();
+ }
+
+ public void setOutOfBounds(boolean outOfBounds) {
+ mOutOfBounds = outOfBounds;
+ updateToolbarVisibility();
+ }
+
+ private void updateToolbarVisibility() {
+ if (mSnoozed || mMoving || mOutOfBounds) {
+ mToolbar.hide();
+ } else if (mToolbar.isHidden()) {
+ mToolbar.show();
+ }
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index fdc3547..b7a53b0 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -357,7 +357,7 @@ public final class FloatingToolbar {
mShowAnimation = createGrowFadeInFromBottom(mContentContainer);
mDismissAnimation = createShrinkFadeOutFromBottomAnimation(
mContentContainer,
- 0,
+ 150, // startDelay
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -367,7 +367,7 @@ public final class FloatingToolbar {
});
mHideAnimation = createShrinkFadeOutFromBottomAnimation(
mContentContainer,
- 150,
+ 0, // startDelay
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
diff --git a/core/java/com/android/internal/widget/TextViewInputDisabler.java b/core/java/com/android/internal/widget/TextViewInputDisabler.java
new file mode 100644
index 0000000..fb0b3b9
--- /dev/null
+++ b/core/java/com/android/internal/widget/TextViewInputDisabler.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package com.android.internal.widget;
+
+import android.text.InputFilter;
+import android.text.Spanned;
+import android.widget.TextView;
+
+/**
+ * Helper class to disable input on a TextView. The input is disabled by swapping in an InputFilter
+ * that discards all changes. Use with care if you have customized InputFilter on the target
+ * TextView.
+ */
+public class TextViewInputDisabler {
+ private TextView mTextView;
+ private InputFilter[] mDefaultFilters;
+ private InputFilter[] mNoInputFilters = new InputFilter[] {
+ new InputFilter () {
+ @Override
+ public CharSequence filter(CharSequence source, int start, int end, Spanned dest,
+ int dstart, int dend) {
+ return "";
+ }
+ }
+ };
+
+ public TextViewInputDisabler(TextView textView) {
+ mTextView = textView;
+ mDefaultFilters = mTextView.getFilters();
+ }
+
+ public void setInputEnabled(boolean enabled) {
+ mTextView.setFilters(enabled ? mDefaultFilters : mNoInputFilters);
+ }
+}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index a80abce..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 @@ public class SystemBackupAgent extends BackupAgentHelper {
// 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 @@ public class SystemBackupAgent extends BackupAgentHelper {
}
}
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 @@ public class SystemBackupAgent extends BackupAgentHelper {
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 @@ public class SystemBackupAgent extends BackupAgentHelper {
}
}
}
-
- @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/Region.cpp b/core/jni/android/graphics/Region.cpp
index cf02e39..3bab2df 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -212,10 +212,14 @@ static jlong Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel)
android::Parcel* p = android::parcelForJavaObject(env, parcel);
- SkRegion* region = new SkRegion;
- size_t size = p->readInt32();
- size_t actualSize = region->readFromMemory(p->readInplace(size), size);
+ const size_t size = p->readInt32();
+ const void* regionData = p->readInplace(size);
+ if (regionData == nullptr) {
+ return 0;
+ }
+ SkRegion* region = new SkRegion;
+ size_t actualSize = region->readFromMemory(regionData, size);
if (size != actualSize) {
delete region;
return 0;
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 60e8ed0..fada7ac 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -291,6 +291,11 @@ static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint
return (jboolean) !protectFromVpn(socket);
}
+static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId)
+{
+ return (jboolean) !queryUserAccess(uid, netId);
+}
+
// ----------------------------------------------------------------------------
@@ -311,6 +316,7 @@ static JNINativeMethod gNetworkUtilMethods[] = {
{ "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
{ "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
{ "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
+ { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
{ "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
};
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 @@ static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject c
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 @@ static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject c
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/values/attrs.xml b/core/res/res/values/attrs.xml
index 0d7d868..21c4a8d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1035,9 +1035,6 @@
<!-- The color applied to framework switch thumbs in their normal state. -->
<attr name="colorSwitchThumbNormal" format="color" />
- <!-- @hide The background used by framework controls. -->
- <attr name="controlBackground" format="reference" />
-
<!-- The color applied to the edge effect on scrolling containers. -->
<attr name="colorEdgeEffect" format="color" />
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/styles_material.xml b/core/res/res/values/styles_material.xml
index c2371ee..165d1f8 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -572,16 +572,16 @@ please see styles_device_defaults.xml.
<style name="Widget.Material.CompoundButton" parent="Widget.CompoundButton"/>
<style name="Widget.Material.CompoundButton.CheckBox" parent="Widget.CompoundButton.CheckBox">
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
</style>
<style name="Widget.Material.CompoundButton.RadioButton" parent="Widget.CompoundButton.RadioButton">
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
</style>
<style name="Widget.Material.CompoundButton.Star" parent="Widget.CompoundButton.Star">
<item name="button">@drawable/btn_star_material</item>
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
</style>
<style name="Widget.Material.CompoundButton.Switch">
@@ -590,7 +590,7 @@ please see styles_device_defaults.xml.
<item name="switchTextAppearance">@style/TextAppearance.Material.Widget.Switch</item>
<item name="textOn">@string/capital_on</item>
<item name="textOff">@string/capital_off</item>
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
<item name="showText">false</item>
</style>
@@ -727,7 +727,7 @@ please see styles_device_defaults.xml.
<item name="paddingStart">16dip</item>
<item name="paddingEnd">16dip</item>
<item name="mirrorForRtl">true</item>
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
</style>
<style name="Widget.Material.RatingBar" parent="Widget.RatingBar">
@@ -809,7 +809,7 @@ please see styles_device_defaults.xml.
</style>
<style name="Widget.Material.Toolbar.Button.Navigation" parent="Widget.Material">
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
<item name="minWidth">56dp</item>
<item name="scaleType">center</item>
<item name="paddingStart">@dimen/action_bar_navigation_padding_start_material</item>
@@ -866,7 +866,7 @@ please see styles_device_defaults.xml.
</style>
<style name="Widget.Material.ActionButton.CloseMode">
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
</style>
<style name="Widget.Material.ActionButton.Overflow">
@@ -962,7 +962,7 @@ please see styles_device_defaults.xml.
</style>
<style name="Widget.Material.MediaRouteButton">
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
<item name="externalRouteEnabledDrawable">@drawable/ic_media_route_material</item>
<item name="minWidth">56dp</item>
<item name="minHeight">48dp</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9c6887f..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" />
@@ -2277,4 +2278,5 @@
<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/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index f02fed1..3f8071f 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -390,8 +390,6 @@ please see themes_device_defaults.xml.
<item name="colorControlHighlight">@color/ripple_material_dark</item>
<item name="colorButtonNormal">@color/btn_default_material_dark</item>
<item name="colorSwitchThumbNormal">@color/switch_thumb_material_dark</item>
-
- <item name="controlBackground">@drawable/control_background_material</item>
</style>
<!-- Material theme (light version). -->
@@ -740,8 +738,6 @@ please see themes_device_defaults.xml.
<item name="colorControlHighlight">@color/ripple_material_light</item>
<item name="colorButtonNormal">@color/btn_default_material_light</item>
<item name="colorSwitchThumbNormal">@color/switch_thumb_material_light</item>
-
- <item name="controlBackground">@drawable/control_background_material</item>
</style>
<!-- Variant of the material (light) theme that has a solid (opaque) action bar
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 android.util.Log;
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 class RtNetlinkNeighborMessageTest extends TestCase {
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 @@ public class RtNetlinkNeighborMessageTest extends TestCase {
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 @@ public class RtNetlinkNeighborMessageTest extends TestCase {
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 @@ public class RtNetlinkNeighborMessageTest extends TestCase {
// 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 @@ public class TextViewTest extends AndroidTestCase {
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/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 0aa588b..5857de0 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -35,14 +35,14 @@
<familyset>
<family>
<fileset>
- <file variant="elegant">NotoNaskh-Regular.ttf</file>
- <file variant="elegant">NotoNaskh-Bold.ttf</file>
+ <file variant="elegant">NotoNaskhArabic-Regular.ttf</file>
+ <file variant="elegant">NotoNaskhArabic-Bold.ttf</file>
</fileset>
</family>
<family>
<fileset>
- <file variant="compact">NotoNaskhUI-Regular.ttf</file>
- <file variant="compact">NotoNaskhUI-Bold.ttf</file>
+ <file variant="compact">NotoNaskhArabicUI-Regular.ttf</file>
+ <file variant="compact">NotoNaskhArabicUI-Bold.ttf</file>
</fileset>
</family>
<family>
@@ -387,6 +387,11 @@
</family>
<family>
<fileset>
+ <file>NotoSansTibetan-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
<file>NotoSansTifinagh-Regular.ttf</file>
</fileset>
</family>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index f903aab..62da0ff 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -94,12 +94,12 @@
<!-- fallback fonts -->
<family variant="elegant">
- <font weight="400" style="normal">NotoNaskh-Regular.ttf</font>
- <font weight="700" style="normal">NotoNaskh-Bold.ttf</font>
+ <font weight="400" style="normal">NotoNaskhArabic-Regular.ttf</font>
+ <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
</family>
<family variant="compact">
- <font weight="400" style="normal">NotoNaskhUI-Regular.ttf</font>
- <font weight="700" style="normal">NotoNaskhUI-Bold.ttf</font>
+ <font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
</family>
<family>
<font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
@@ -320,6 +320,9 @@
<font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font>
</family>
<family>
+ <font weight="400" style="normal">NotoSansTibetan-Regular.ttf</font>
+ </family>
+ <family>
<font weight="400" style="normal">NotoSansTifinagh-Regular.ttf</font>
</family>
<family>
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&mdash;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&mdash;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&mdash;priced or
+free, with in-app products or subscriptions&mdash;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 @@ page.title=Android Lollipop
@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&mdash;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 @@ about Android 5.0 for consumers at
<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 @@ video apps and games to display smooth synchronized content.</p>
<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 @@ video apps and games to display smooth synchronized content.</p>
<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"&mdash;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 @@ video apps and games to display smooth synchronized content.</p>
<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 @@ video apps and games to display smooth synchronized content.</p>
<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 @@ video apps and games to display smooth synchronized content.</p>
<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 @@ video apps and games to display smooth synchronized content.</p>
<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 &amp; video</h2>
@@ -198,19 +183,17 @@ provide metadata that describes the capture settings of each frame.</p>
<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 @@ app is used by both profiles.</p>
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 @@ can configure global device settings.</p>
<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 @@ can configure global device settings.</p>
<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 &amp; 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 @@ can configure global device settings.</p>
<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 @@ excludeFromSuggestions=true
<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 @@ excludeFromSuggestions=true
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 @@ excludeFromSuggestions=true
</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 @@ nonavpage=true
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 @@ they didn't know your app could handle.</p>
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 @@ hotels in Maui on TripAdvisor”.</p>
<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 @@ can re-engage your users via structured data markup delivered in email notificat
<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>
-
-<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="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="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 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>
- 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 @@ nonavpage=true
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 @@ nonavpage=true
</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-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-query="tag:engagement"
+ data-sortOrder="random"
+ data-cardSizes="6x2"
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 @@ nonavpage=true
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 &mdash; 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..36a67b2 100644
--- a/docs/html/distribute/googleplay/edu/about.jd
+++ b/docs/html/distribute/googleplay/edu/about.jd
@@ -33,18 +33,20 @@ Xnonavpage=true
<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 &mdash; 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 @@ Xnonavpage=true
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 @@ nonavpage=true
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 @@ nonavpage=true
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 @@ page.metaDescription=The most visited store in the world for Android apps. Cloud
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 @@ page.metaDescription=The most visited store in the world for Android apps. Cloud
</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 @@ page.metaDescription=The most visited store in the world for Android apps. Cloud
<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 @@ precious development resources to build your own solution.</p>
</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>
-
-<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>
-
-<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>
-</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>
+<h2 id="maximize_your_ad_revenue">Maximize your ad revenue</h2>
+
+<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-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>
+<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 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 @@ nonavpage=true
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 @@ nonavpage=true
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 @@ nonavpage=true
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 @@ ongoing engagement. AdWords is a powerful and effective way to do both.</p>
<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 class="figure-right" style="width:40%;">
- <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>
-<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 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 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 @@ in mind with users who’ve already installed it on their phone. AdWords can
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/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 @@ footer.hide=1
<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 @@ grow your user base, and monetize more effectively with Google Play services.
<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&apos;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/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 @@ and features, and more. You can use In-app Billing to sell products as</p>
period.</li>
<li><strong>IAB Sandbox</strong>&mdash;The In-app Billing Sandbox now supports
testing subscription purchases.</li>
- <li><strong>IAB v2 shutdown</strong>&mdash;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>&mdash;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>&mdash;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/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..ac8b928
--- /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 @@ page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3
</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 @@ page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3
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 @@ var RESOURCE_COLLECTIONS = {
"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 @@ var RESOURCE_COLLECTIONS = {
"http://youtu.be/vzvpcEffvaE"
]
},
- "launch/static": {
+/* "launch/static": {
"title": "",
"resources": [
"http://www.youtube.com/watch?v=1RIz-cmTQB4",
@@ -221,7 +229,7 @@ var RESOURCE_COLLECTIONS = {
"distribute/users/know-your-user.html",
"distribute/googleplay/developer-console.html"
]
- },
+ }, */
"launch/static/ja": {
"title": "",
"resources": [
@@ -272,8 +280,18 @@ var RESOURCE_COLLECTIONS = {
"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 @@ var RESOURCE_COLLECTIONS = {
"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 @@ var RESOURCE_COLLECTIONS = {
"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 @@ var RESOURCE_COLLECTIONS = {
"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 @@ var RESOURCE_COLLECTIONS = {
"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 @@ var RESOURCE_COLLECTIONS = {
"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 @@ var RESOURCE_COLLECTIONS = {
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"image":"images/cards/card-cardboard_2x.png",
"type":"Design"
},
-
-
-
{
"title":"Maps",
"titleFriendly":"",
@@ -146,22 +143,6 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"image":"images/gcm/gcm-logo.png",
"type":"Guide"
},
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{
"title":"ClassDojo Developer Story",
"titleFriendly":"",
@@ -315,6 +296,30 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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,28 +1248,14 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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",
- "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",
+ "type": "Guide",
"titleFriendly": ""
},
+
{
"lang": "en",
"group": "",
@@ -1284,21 +1275,6 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
{
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
{
"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 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"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-overview.jd b/docs/html/preview/api-overview.jd
index b207e35..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=23
+sdk.platform.apiLevel=22-mnc
+page.image=images/cards/card-key-changes_16-9_2x.png
@jd:body
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 @@ footer.hide=1
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 @@ footer.hide=1
<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/setup-sdk.jd b/docs/html/preview/setup-sdk.jd
index 35fab1a..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
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 @@ parent.link=index.html
<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 @@ $ ./gradlew assembleRelease
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 @@ keyAlias "MyReleaseKey"
<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 @@ android {
<p>This creates your Android application .apk file inside the module <code>build/</code>
directory, named <code><em>&lt;your_module_name&gt;</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 @@ page.title=Managing Virtual Devices
<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 &gt; 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>&lt;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 &gt; Android &gt; 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>&lt;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 @@ id: 5 or "android-9"
<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/articles/keystore.jd b/docs/html/training/articles/keystore.jd
index 4005a05..20963f5 100644
--- a/docs/html/training/articles/keystore.jd
+++ b/docs/html/training/articles/keystore.jd
@@ -5,6 +5,7 @@ page.title=Android Keystore System
<div id="qv">
<h2>In this document</h2>
<ol>
+ <li><a href="#SecurityFeatures">Security Features</a></li>
<li><a href="#WhichShouldIUse">Choosing Between a Keychain or the Android Keystore Provider</a></li>
<li><a href="#UsingAndroidKeyStore">Using Android Keystore Provider
</a></li>
@@ -31,7 +32,8 @@ page.title=Android Keystore System
keystore, they can be used for cryptographic operations with the key material
remaining non-exportable. Moreover, it offers facilities to restrict when and
how keys can be used, such as requiring user authentication for key use or
- restricting encryption keys to be used only in certain block modes.</p>
+ restricting encryption keys to be used only in certain block modes. See
+ <a href="#SecurityFeatures">Security Features</a> section for more information.</p>
<p>The Keystore system is used by the {@link
android.security.KeyChain} API as well as the Android
@@ -39,6 +41,67 @@ page.title=Android Keystore System
(API level 18). This document goes over when and how to use the
Android Keystore provider.</p>
+
+<h2 id="SecurityFeatures">Security Features</h2>
+
+Android Keystore system protects key material from unauthorized use. Firstly, Android Keystore
+mitigates unauthorized use of key material outside of the Android device by preventing extraction of
+the key material from application processes and from the Android device as a whole. Secondly,
+Android KeyStore mitigates unauthorized use of key material on the Android device by making apps
+specify authorized uses of their keys and then enforcing these restrictions.
+
+<h3 id="ExtractionPrevention">Extraction Prevention</h3>
+
+Key material of Android Keystore keys is protected from extraction using two security measures:
+<ul>
+<li>Key material never enters the application process. When an application performs cryptographic
+ operations using an Android Keystore key, behind the scenes plaintext, ciphertext, and messages to
+ be signed or verified are fed to a system process which carries out the cryptographic operations.
+ If the app's process is compromised, the attacker may be able to use the app's keys but will not
+ be able to extract their key material (for example, to be used outside of the Android device).
+ </li>
+<li>Key material may be bound to the secure hardware (e.g., Trusted Execution Environment (TEE),
+ Secure Element (SE)) of the Android device. When this feature is enabled for a key, its key
+ material is never exposed outside of secure hardware. If the Android OS is compromised or an
+ attacker can read the device's internal storage, the attacker may be able to use any app's Android
+ Keystore keys on the Android device, but not extract them from the device. This feature is enabled
+ only if the device's secure hardware supports the particular combination of key algorithm, block
+ modes, padding schemes, and digests with which the key is authorized to be used. To check whether
+ the feature is enabled for a key, obtain a {@link android.security.keystore.KeyInfo} for the key
+ and inspect the return value of
+ {@link android.security.keystore.KeyInfo#isInsideSecureHardware() KeyInfo.isInsideSecurityHardware()}.
+ </li>
+</ul>
+
+<h3 id="KeyUseAuthorizations">Key Use Authorizations</h3>
+
+To mitigate unauthorized use of keys on the Android device, Android Keystore lets apps specify
+authorized uses of their keys when generating or importing the keys. Once a key is generated or
+imported, its authorizations can not be changed. Authorizations are then enforced by the Android
+Keystore whenever the key is used.
+
+<p>Supported key use authorizations fall into the following categories:
+<ul>
+<li><em>cryptography</em>: authorized key algorithm, operations or purposes (encrypt, decrypt, sign,
+ verify), padding schemes, block modes, digests with which the key can be used</li>
+<li><em>temporal validity interval</em>: interval of time during which the key is authorized for
+ use</li>
+<li><em>user authentication</em>: the key can only be used if the user has been authenticated
+ recently enough. See <a href="#UserAuthentication">Requiring User Authentication For Key Use</a>.
+ </li>
+</ul>
+
+<p>As an additional security measure, for keys whose key material is inside secure hardware (see
+ {@link android.security.keystore.KeyInfo#isInsideSecureHardware() KeyInfo.isInsideSecurityHardware()})
+ some key use authorizations may be enforced by secure hardware, depending on the Android device.
+ Cryptographic and user authentication authorizations are likely to be enforced by secure hardware.
+ Temporal validity interval authorizations are unlikely to be enforced by the secure hardware
+ because it normally does not have an independent secure real-time clock.
+
+<p>Whether a key's user authentication authorization is enforced by the secure hardware can be
+ queried using
+ {@link android.security.keystore.KeyInfo#isUserAuthenticationRequirementEnforcedBySecureHardware() KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware()}.
+
<h2 id="WhichShouldIUse">Choosing Between a Keychain or the
Android Keystore Provider</h2>
@@ -129,7 +192,7 @@ of the two modes:
for use as soon as the user unlocks the secure lock screen or confirms their secure lock screen
credentials using the {@link android.app.KeyguardManager#createConfirmDeviceCredentialIntent(CharSequence, CharSequence) KeyguardManager.createConfirmDeviceCredentialIntent}
flow. Each key specifies for how long the authorization remains valid for that key. Such keys
- can only be generated or imported if the secure lock screen is enabled (see {@link android.app.KeyguardManager#isDeviceSecure()}).
+ can only be generated or imported if the secure lock screen is enabled (see {@link android.app.KeyguardManager#isDeviceSecure() KeyguardManager.isDeviceSecure()}).
These keys become permanently invalidated once the secure lock screen is disabled or forcibly
reset (e.g. by a Device Admin).</li>
<li>User authentication is required for every use of the key. In this mode, a specific operation
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 @@ abstract class RippleComponent {
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 @@ abstract class RippleComponent {
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.Bitmap;
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 @@ public class RippleDrawable extends LayerDrawable {
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 @@ struct ResTable_config
// 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 @@ struct ResTable_config
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/keystore/java/android/security/keystore/AndroidKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 2c393fd..4b45fd7 100644
--- a/keystore/java/android/security/keystore/AndroidKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -52,19 +52,19 @@ import java.util.Locale;
* <p>
* This class can not be directly instantiated and must instead be used via the
* {@link KeyPairGenerator#getInstance(String)
- * KeyPairGenerator.getInstance("AndroidKeyPairGenerator")} API.
+ * KeyPairGenerator.getInstance("AndroidKeyStore")} API.
*
- * {@hide}
+ * @hide
*/
-public abstract class AndroidKeyPairGeneratorSpi extends KeyPairGeneratorSpi {
+public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGeneratorSpi {
- public static class RSA extends AndroidKeyPairGeneratorSpi {
+ public static class RSA extends AndroidKeyStoreKeyPairGeneratorSpi {
public RSA() {
super(KeyProperties.KEY_ALGORITHM_RSA);
}
}
- public static class EC extends AndroidKeyPairGeneratorSpi {
+ public static class EC extends AndroidKeyStoreKeyPairGeneratorSpi {
public EC() {
super(KeyProperties.KEY_ALGORITHM_EC);
}
@@ -94,7 +94,7 @@ public abstract class AndroidKeyPairGeneratorSpi extends KeyPairGeneratorSpi {
private int mKeyType;
private int mKeySize;
- protected AndroidKeyPairGeneratorSpi(@KeyProperties.KeyAlgorithmEnum String algorithm) {
+ protected AndroidKeyStoreKeyPairGeneratorSpi(@KeyProperties.KeyAlgorithmEnum String algorithm) {
mAlgorithm = algorithm;
}
@@ -283,7 +283,8 @@ public abstract class AndroidKeyPairGeneratorSpi extends KeyPairGeneratorSpi {
@Override
public void initialize(int keysize, SecureRandom random) {
- throw new IllegalArgumentException("cannot specify keysize with AndroidKeyPairGenerator");
+ throw new IllegalArgumentException(
+ "cannot specify keysize with AndroidKeyStore KeyPairGenerator");
}
@Override
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index b20a122..649a515 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -49,8 +49,8 @@ public class AndroidKeyStoreProvider extends Provider {
put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi");
// java.security.KeyPairGenerator
- put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyPairGeneratorSpi$EC");
- put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyPairGeneratorSpi$RSA");
+ put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
+ put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
// javax.crypto.KeyGenerator
put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
@@ -111,6 +111,7 @@ public class AndroidKeyStoreProvider extends Provider {
*
* @throws IllegalArgumentException if the provided primitive is not supported or is not backed
* by AndroidKeyStore provider.
+ * @throws IllegalStateException if the provided primitive is not initialized.
*/
public static long getKeyStoreOperationHandle(Object cryptoPrimitive) {
if (cryptoPrimitive == null) {
@@ -118,15 +119,17 @@ public class AndroidKeyStoreProvider extends Provider {
}
Object spi;
if (cryptoPrimitive instanceof Mac) {
- spi = ((Mac) cryptoPrimitive).getSpi();
+ spi = ((Mac) cryptoPrimitive).getCurrentSpi();
} else if (cryptoPrimitive instanceof Cipher) {
- spi = ((Cipher) cryptoPrimitive).getSpi();
+ spi = ((Cipher) cryptoPrimitive).getCurrentSpi();
} else {
throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive);
}
- if (!(spi instanceof KeyStoreCryptoOperation)) {
+ if (spi == null) {
+ throw new IllegalStateException("Crypto primitive not initialized");
+ } else if (!(spi instanceof KeyStoreCryptoOperation)) {
throw new IllegalArgumentException(
- "Crypto primitive not backed by AndroidKeyStore: " + cryptoPrimitive
+ "Crypto primitive not backed by AndroidKeyStore provider: " + cryptoPrimitive
+ ", spi: " + spi);
}
return ((KeyStoreCryptoOperation) spi).getOperationHandle();
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 @@ int ResTable_config::compare(const ResTable_config& o) const {
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 @@ int ResTable_config::compareLogical(const ResTable_config& o) const {
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 @@ int ResTable_config::diff(const ResTable_config& o) const {
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 @@ bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const {
}
}
+ 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 @@ bool ResTable_config::isBetterThan(const ResTable_config& o,
}
}
+ 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 @@ bool ResTable_config::match(const ResTable_config& settings) const {
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 @@ String8 ResTable_config::toString() const {
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 @@ TEST(ConfigTest, shouldSelectBestDensityWhenNoneSpecified) {
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(&notRoundConfig, 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/libs/hwui/utils/RingBuffer.h b/libs/hwui/utils/RingBuffer.h
index 6d0a06b..d822cb2 100644
--- a/libs/hwui/utils/RingBuffer.h
+++ b/libs/hwui/utils/RingBuffer.h
@@ -31,7 +31,7 @@ public:
RingBuffer() {}
~RingBuffer() {}
- constexpr size_t capacity() { return SIZE; }
+ constexpr size_t capacity() const { return SIZE; }
size_t size() { return mCount; }
T& next() {
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 @@ public class AudioTrack
/** 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 @@ public class AudioTrack
/**
* 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 @@ public class AudioTrack
//--------------------
/**
* 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 @@ public class AudioTrack
/**
* 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 @@ public class AudioTrack
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 @@ public class AudioTrack
*/
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 @@ public class AudioTrack
* @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 @@ public class AudioTrack
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 @@ public class AudioTrack
}
}
- // 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 @@ public class AudioTrack
// 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 @@ public class AudioTrack
* <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 @@ public class AudioTrack
* 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 @@ public class AudioTrack
//--------------------
/**
* 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 @@ public class AudioTrack
* 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 @@ public class AudioTrack
* 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 @@ public class AudioTrack
* 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 @@ public class AudioTrack
* 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 @@ public class AudioTrack
* 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 @@ public class AudioTrack
* 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 @@ public class AudioTrack
* 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.
- * 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.
+ * 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.
* 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 @@ public class AudioTrack
* 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 @@ public class AudioTrack
/**
* 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 @@ public class AudioTrack
* 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 @@ public class AudioTrack
}
/**
- * 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 @@ public class AudioTrack
* <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/midi/MidiReceiver.java b/media/java/android/media/midi/MidiReceiver.java
index 85c451f..12a5f04 100644
--- a/media/java/android/media/midi/MidiReceiver.java
+++ b/media/java/android/media/midi/MidiReceiver.java
@@ -87,29 +87,38 @@ abstract public class MidiReceiver {
}
/**
- * Called to send MIDI data to the receiver
+ * Called to send MIDI data to the receiver without a timestamp.
+ * Data will be processed by receiver in the order sent.
* Data will get split into multiple calls to {@link #onSend} if count exceeds
- * {@link #getMaxMessageSize}.
+ * {@link #getMaxMessageSize}. Blocks until all the data is sent or an exception occurs.
+ * In the latter case, the amount of data sent prior to the exception is not provided to caller.
+ * The communication should be considered corrupt. The sender should reestablish
+ * communication, reset all controllers and send all notes off.
*
* @param msg a byte array containing the MIDI data
* @param offset the offset of the first byte of the data in the array to be sent
* @param count the number of bytes of MIDI data in the array to be sent
- * @throws IOException
+ * @throws IOException if the data could not be sent in entirety
*/
public void send(byte[] msg, int offset, int count) throws IOException {
- send(msg, offset, count, System.nanoTime());
+ // TODO add public static final TIMESTAMP_NONE = 0L
+ send(msg, offset, count, 0L);
}
/**
- * Called to send MIDI data to the receiver to be handled at a specified time in the future
+ * Called to send MIDI data to the receiver with a specified timestamp.
+ * Data will be processed by receiver in order first by timestamp, then in the order sent.
* Data will get split into multiple calls to {@link #onSend} if count exceeds
- * {@link #getMaxMessageSize}.
+ * {@link #getMaxMessageSize}. Blocks until all the data is sent or an exception occurs.
+ * In the latter case, the amount of data sent prior to the exception is not provided to caller.
+ * The communication should be considered corrupt. The sender should reestablish
+ * communication, reset all controllers and send all notes off.
*
* @param msg a byte array containing the MIDI data
* @param offset the offset of the first byte of the data in the array to be sent
* @param count the number of bytes of MIDI data in the array to be sent
- * @param timestamp the timestamp of the message (based on {@link java.lang.System#nanoTime}
- * @throws IOException
+ * @param timestamp the timestamp of the message, based on {@link java.lang.System#nanoTime}
+ * @throws IOException if the data could not be sent in entirety
*/
public void send(byte[] msg, int offset, int count, long timestamp)
throws IOException {
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 @@ int32_t AConfiguration_getScreenLong(AConfiguration* config) {
>> 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 @@ void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong) {
| ((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/Keyguard/src/com/android/keyguard/EmergencyButton.java b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
index 1cabcdf..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 @@ public class EmergencyButton extends Button {
.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 @@ public class EmergencyButton extends Button {
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/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
index 3fcc3c3..f18c451 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
@@ -35,6 +35,8 @@ import android.view.inputmethod.InputMethodSubtype;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
+import com.android.internal.widget.TextViewInputDisabler;
+
import java.util.List;
/**
* Displays an alphanumeric (latin-1) key entry for the user to enter
@@ -49,6 +51,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
InputMethodManager mImm;
private TextView mPasswordEntry;
+ private TextViewInputDisabler mPasswordEntryDisabler;
+
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mFastOutLinearInInterpolator;
@@ -70,7 +74,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
protected void resetState() {
mSecurityMessageDisplay.setMessage(R.string.kg_password_instructions, false);
- mPasswordEntry.setEnabled(true);
+ setPasswordEntryEnabled(true);
}
@Override
@@ -123,6 +127,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
Context.INPUT_METHOD_SERVICE);
mPasswordEntry = (TextView) findViewById(getPasswordTextViewId());
+ mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry);
mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_PASSWORD);
@@ -185,7 +190,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
@Override
protected void setPasswordEntryEnabled(boolean enabled) {
- mPasswordEntry.setEnabled(enabled);
+ mPasswordEntryDisabler.setInputEnabled(enabled);
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 5d6b2f1..d3e7104 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -51,11 +51,13 @@ public final class BluetoothEventManager {
private final Collection<BluetoothCallback> mCallbacks =
new ArrayList<BluetoothCallback>();
+ private android.os.Handler mReceiverHandler;
+
interface Handler {
void onReceive(Context context, Intent intent, BluetoothDevice device);
}
- void addHandler(String action, Handler handler) {
+ private void addHandler(String action, Handler handler) {
mHandlerMap.put(action, handler);
mAdapterIntentFilter.addAction(action);
}
@@ -103,11 +105,18 @@ public final class BluetoothEventManager {
// Dock event broadcasts
addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
- mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter);
+ mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
}
void registerProfileIntentReceiver() {
- mContext.registerReceiver(mBroadcastReceiver, mProfileIntentFilter);
+ mContext.registerReceiver(mBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
+ }
+
+ public void setReceiverHandler(android.os.Handler handler) {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ mReceiverHandler = handler;
+ mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
+ registerProfileIntentReceiver();
}
/** Register to start receiving callbacks for Bluetooth events. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
index 9e2207e..2dc521e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
@@ -105,6 +105,13 @@ public class DelegateViewHelper {
return mPanelShowing;
}
+ public void abortCurrentGesture() {
+ if (mStarted) {
+ mStarted = false;
+ mBar.setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, false);
+ }
+ }
+
public void setSourceView(View view) {
mSourceView = view;
if (mSourceView != null) {
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 @@ public abstract class ExpandableView extends FrameLayout {
@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 @@ public abstract class ExpandableView extends FrameLayout {
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/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 7077a17..1dec227 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -245,6 +245,11 @@ public class NavigationBarView extends LinearLayout {
return intercept;
}
+ public void abortCurrentGesture() {
+ mDelegateHelper.abortCurrentGesture();
+ getHomeButton().abortCurrentGesture();
+ }
+
private H mHandler = new H();
public View getCurrentView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 9ef9211..c10be7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -117,6 +117,7 @@ public class NotificationPanelView extends PanelView implements
* intercepted yet.
*/
private boolean mIntercepting;
+ private boolean mPanelExpanded;
private boolean mQsExpanded;
private boolean mQsExpandedWhenExpandingStarted;
private boolean mQsFullyExpanded;
@@ -1496,13 +1497,22 @@ public class NotificationPanelView extends PanelView implements
updateHeader();
updateUnlockIcon();
updateNotificationTranslucency();
- mHeadsUpManager.setIsExpanded(!isFullyCollapsed());
+ updatePanelExpanded();
mNotificationStackScroller.setShadeExpanded(!isFullyCollapsed());
if (DEBUG) {
invalidate();
}
}
+ private void updatePanelExpanded() {
+ boolean isExpanded = !isFullyCollapsed();
+ if (mPanelExpanded != isExpanded) {
+ mHeadsUpManager.setIsExpanded(isExpanded);
+ mStatusBar.setPanelExpanded(isExpanded);
+ mPanelExpanded = isExpanded;
+ }
+ }
+
/**
* @return a temporary override of {@link #mQsMaxExpansionHeight}, which is needed when
* collapsing QS / the panel when QS was scrolled
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 2a9df19..9fe591e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1013,7 +1013,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
invokeAssistGesture(true /* vibrate */);
awakenDreams();
if (mNavigationBarView != null) {
- mNavigationBarView.getHomeButton().abortCurrentGesture();
+ mNavigationBarView.abortCurrentGesture();
}
}
};
@@ -1967,6 +1967,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
return !mUnlockMethodCache.isCurrentlyInsecure();
}
+ public void setPanelExpanded(boolean isExpanded) {
+ mStatusBarWindowManager.setPanelExpanded(isExpanded);
+ }
+
/**
* All changes to the status bar and notifications funnel through here and are batched.
*/
@@ -2027,7 +2031,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
// Expand the window to encompass the full screen in anticipation of the drag.
// This is only possible to do atomically because the status bar is at the top of the screen!
- mStatusBarWindowManager.setStatusBarExpanded(true);
+ mStatusBarWindowManager.setPanelVisible(true);
mStatusBarView.setFocusable(false);
visibilityChanged(true);
@@ -2156,7 +2160,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
visibilityChanged(false);
// Shrink the window to the size of the status bar only
- mStatusBarWindowManager.setStatusBarExpanded(false);
+ mStatusBarWindowManager.setPanelVisible(false);
mStatusBarWindowManager.setForceStatusBarVisible(false);
mStatusBarView.setFocusable(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index 422d868..4f1c652 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -114,12 +114,12 @@ public class StatusBarWindowManager {
}
private void applyFocusableFlag(State state) {
+ boolean panelFocusable = state.statusBarFocusable && state.panelExpanded;
if (state.isKeyguardShowingAndNotOccluded() && state.keyguardNeedsInput
- && state.bouncerShowing
- || BaseStatusBar.ENABLE_REMOTE_INPUT && state.statusBarExpanded) {
+ && state.bouncerShowing || BaseStatusBar.ENABLE_REMOTE_INPUT && panelFocusable) {
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- } else if (state.isKeyguardShowingAndNotOccluded() || state.statusBarFocusable) {
+ } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mLpChanged.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else {
@@ -130,7 +130,7 @@ public class StatusBarWindowManager {
private void applyHeight(State state) {
boolean expanded = !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
- || state.statusBarExpanded || state.keyguardFadingAway || state.bouncerShowing
+ || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
|| state.headsUpShowing);
if (expanded) {
mLpChanged.height = ViewGroup.LayoutParams.MATCH_PARENT;
@@ -213,9 +213,9 @@ public class StatusBarWindowManager {
apply(mCurrentState);
}
- public void setStatusBarExpanded(boolean expanded) {
- mCurrentState.statusBarExpanded = expanded;
- mCurrentState.statusBarFocusable = expanded;
+ public void setPanelVisible(boolean visible) {
+ mCurrentState.panelVisible = visible;
+ mCurrentState.statusBarFocusable = visible;
apply(mCurrentState);
}
@@ -267,11 +267,17 @@ public class StatusBarWindowManager {
apply(mCurrentState);
}
+ public void setPanelExpanded(boolean isExpanded) {
+ mCurrentState.panelExpanded = isExpanded;
+ apply(mCurrentState);
+ }
+
private static class State {
boolean keyguardShowing;
boolean keyguardOccluded;
boolean keyguardNeedsInput;
- boolean statusBarExpanded;
+ boolean panelVisible;
+ boolean panelExpanded;
boolean statusBarFocusable;
boolean bouncerShowing;
boolean keyguardFadingAway;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index 5893cb2..0eb7197 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -45,7 +45,7 @@ public class AccessPointControllerImpl
private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid";
private static final int[] ICONS = {
- R.drawable.ic_qs_wifi_0,
+ R.drawable.ic_qs_wifi_full_0,
R.drawable.ic_qs_wifi_full_1,
R.drawable.ic_qs_wifi_full_2,
R.drawable.ic_qs_wifi_full_3,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 8d4f302..114427c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -18,7 +18,9 @@ package com.android.systemui.statusbar.policy;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
+import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.util.Log;
import com.android.settingslib.bluetooth.BluetoothCallback;
@@ -42,9 +44,12 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
private boolean mConnecting;
private CachedBluetoothDevice mLastDevice;
+ private final H mHandler = new H();
+
public BluetoothControllerImpl(Context context, Looper bgLooper) {
mLocalBluetoothManager = LocalBluetoothManager.getInstance(context, null);
if (mLocalBluetoothManager != null) {
+ mLocalBluetoothManager.getEventManager().setReceiverHandler(new Handler(bgLooper));
mLocalBluetoothManager.getEventManager().registerCallback(this);
onBluetoothStateChanged(
mLocalBluetoothManager.getBluetoothAdapter().getBluetoothState());
@@ -71,7 +76,7 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
public void addStateChangedCallback(Callback cb) {
mCallbacks.add(cb);
- fireStateChange(cb);
+ mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
@@ -132,22 +137,6 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
: null;
}
- private void firePairedDevicesChanged() {
- for (Callback cb : mCallbacks) {
- cb.onBluetoothDevicesChanged();
- }
- }
-
- private void fireStateChange() {
- for (Callback cb : mCallbacks) {
- fireStateChange(cb);
- }
- }
-
- private void fireStateChange(Callback cb) {
- cb.onBluetoothStateChange(mEnabled, mConnecting);
- }
-
private void updateConnected() {
if (mLastDevice != null && mLastDevice.isConnected()) {
// Our current device is still valid.
@@ -163,7 +152,7 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
@Override
public void onBluetoothStateChanged(int bluetoothState) {
mEnabled = bluetoothState == BluetoothAdapter.STATE_ON;
- fireStateChange();
+ mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
@@ -175,25 +164,25 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
cachedDevice.registerCallback(this);
updateConnected();
- firePairedDevicesChanged();
+ mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
updateConnected();
- firePairedDevicesChanged();
+ mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
updateConnected();
- firePairedDevicesChanged();
+ mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
public void onDeviceAttributesChanged() {
updateConnected();
- firePairedDevicesChanged();
+ mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
@@ -201,6 +190,39 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
mConnecting = state == BluetoothAdapter.STATE_CONNECTING;
mLastDevice = cachedDevice;
updateConnected();
- fireStateChange();
+ mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
+ }
+
+ private final class H extends Handler {
+ private static final int MSG_PAIRED_DEVICES_CHANGED = 1;
+ private static final int MSG_STATE_CHANGED = 2;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PAIRED_DEVICES_CHANGED:
+ firePairedDevicesChanged();
+ break;
+ case MSG_STATE_CHANGED:
+ fireStateChange();
+ break;
+ }
+ }
+
+ private void firePairedDevicesChanged() {
+ for (BluetoothController.Callback cb : mCallbacks) {
+ cb.onBluetoothDevicesChanged();
+ }
+ }
+
+ private void fireStateChange() {
+ for (BluetoothController.Callback cb : mCallbacks) {
+ fireStateChange(cb);
+ }
+ }
+
+ private void fireStateChange(BluetoothController.Callback cb) {
+ cb.onBluetoothStateChange(mEnabled, mConnecting);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 6e0ca3c..1c6462e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -724,7 +724,7 @@ public class VolumeDialog {
}
row.slider.setProgress(newProgress);
}
- if (mAutomute) {
+ if (mAutomute && mShowing) {
if (vlevel == 0 && !row.ss.muted && row.stream == AudioManager.STREAM_MUSIC) {
mController.setStreamMute(row.stream, true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
index 3a8081f..c6d9e46 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
@@ -180,7 +180,7 @@ public class VolumeDialogController {
pw.print(" mEnabled: "); pw.println(mEnabled);
pw.print(" mDestroyed: "); pw.println(mDestroyed);
pw.print(" mVolumePolicy: "); pw.println(mVolumePolicy);
- pw.print(" mEnabled: "); pw.println(mEnabled);
+ pw.print(" mState: "); pw.println(mState.toString(4));
pw.print(" mShowDndTile: "); pw.println(mShowDndTile);
pw.print(" mHasVibrator: "); pw.println(mHasVibrator);
pw.print(" mRemoteStreams: "); pw.println(mMediaSessionsCallbacksW.mRemoteStreams
@@ -960,25 +960,45 @@ public class VolumeDialogController {
@Override
public String toString() {
+ return toString(0);
+ }
+
+ public String toString(int indent) {
final StringBuilder sb = new StringBuilder("{");
+ if (indent > 0) sep(sb, indent);
for (int i = 0; i < states.size(); i++) {
- if (i > 0) sb.append(',');
+ if (i > 0) {
+ sep(sb, indent);
+ }
final int stream = states.keyAt(i);
final StreamState ss = states.valueAt(i);
sb.append(AudioSystem.streamToString(stream)).append(":").append(ss.level)
- .append("[").append(ss.levelMin).append("..").append(ss.levelMax);
+ .append('[').append(ss.levelMin).append("..").append(ss.levelMax)
+ .append(']');
if (ss.muted) sb.append(" [MUTED]");
}
- sb.append(",ringerModeExternal:").append(ringerModeExternal);
- sb.append(",ringerModeInternal:").append(ringerModeInternal);
- sb.append(",zenMode:").append(zenMode);
- sb.append(",effectsSuppressor:").append(effectsSuppressor);
- sb.append(",effectsSuppressorName:").append(effectsSuppressorName);
- sb.append(",zenModeConfig:").append(zenModeConfig);
- sb.append(",activeStream:").append(activeStream);
+ sep(sb, indent); sb.append("ringerModeExternal:").append(ringerModeExternal);
+ sep(sb, indent); sb.append("ringerModeInternal:").append(ringerModeInternal);
+ sep(sb, indent); sb.append("zenMode:").append(zenMode);
+ sep(sb, indent); sb.append("effectsSuppressor:").append(effectsSuppressor);
+ sep(sb, indent); sb.append("effectsSuppressorName:").append(effectsSuppressorName);
+ sep(sb, indent); sb.append("zenModeConfig:").append(zenModeConfig);
+ sep(sb, indent); sb.append("activeStream:").append(activeStream);
+ if (indent > 0) sep(sb, indent);
return sb.append('}').toString();
}
+ private static void sep(StringBuilder sb, int indent) {
+ if (indent > 0) {
+ sb.append('\n');
+ for (int i = 0; i < indent; i++) {
+ sb.append(' ');
+ }
+ } else {
+ sb.append(',');
+ }
+ }
+
public Condition getManualExitCondition() {
return zenModeConfig != null && zenModeConfig.manualRule != null
? zenModeConfig.manualRule.condition : null;
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.content.DialogInterface;
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 @@ public class ConfirmDialog extends AlertActivity
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 @@ public class ConfirmDialog extends AlertActivity
@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.Handler;
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 @@ public class ManageDialog extends AlertActivity implements
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 @@ public class ManageDialog extends AlertActivity implements
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 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
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..7ac2655 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1071,23 +1071,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- 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 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
};
+ /**
+ * 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 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
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 @@ public class ConnectivityService extends IConnectivityManager.Stub
// 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 @@ public class ConnectivityService extends IConnectivityManager.Stub
(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;
}
@@ -2684,7 +2688,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
actionToken);
}
- public ProxyInfo getDefaultProxy() {
+ private ProxyInfo getDefaultProxy() {
// this information is already available as a world read/writable jvm property
// so this API change wouldn't have a benifit. It also breaks the passing
// of proxy info to all the JVMs.
@@ -2696,6 +2700,22 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ public ProxyInfo getProxyForNetwork(Network network) {
+ if (network == null) return getDefaultProxy();
+ final ProxyInfo globalProxy = getGlobalProxy();
+ if (globalProxy != null) return globalProxy;
+ if (!NetworkUtils.queryUserAccess(Binder.getCallingUid(), network.netId)) return null;
+ // Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
+ // caller may not have.
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ if (nai == null) return null;
+ synchronized (nai) {
+ final ProxyInfo proxyInfo = nai.linkProperties.getHttpProxy();
+ if (proxyInfo == null) return null;
+ return new ProxyInfo(proxyInfo);
+ }
+ }
+
// Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
// (e.g. if mGlobalProxy==null fall back to network-specific proxy, if network-specific
// proxy is null then there is no proxy in place).
@@ -2945,29 +2965,48 @@ public class ConnectivityService extends IConnectivityManager.Stub
/**
* 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 +3108,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
/**
- * 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 +3573,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
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 +3600,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 +3971,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 +4602,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public void factoryReset() {
enforceConnectivityInternalPermission();
+ final int userId = UserHandle.getCallingUserId();
+
// Turn airplane mode off
setAirplaneMode(false);
@@ -4569,16 +4613,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
// 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 @@ option java_package com.android.server
# 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/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 925fae0..ec2bd67 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -71,6 +71,7 @@ class ActivityManagerDebugConfig {
static final boolean DEBUG_TASKS = DEBUG_ALL || false;
static final boolean DEBUG_THUMBNAILS = DEBUG_ALL || false;
static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
+ static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false;
static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
@@ -104,6 +105,8 @@ class ActivityManagerDebugConfig {
static final String POSTFIX_TASKS = (APPEND_CATEGORY_NAME) ? "_Tasks" : "";
static final String POSTFIX_THUMBNAILS = (APPEND_CATEGORY_NAME) ? "_Thumbnails" : "";
static final String POSTFIX_TRANSITION = (APPEND_CATEGORY_NAME) ? "_Transition" : "";
+ static final String POSTFIX_UID_OBSERVERS = (APPEND_CATEGORY_NAME)
+ ? "_UidObservers" : "";
static final String POSTFIX_URI_PERMISSION = (APPEND_CATEGORY_NAME) ? "_UriPermission" : "";
static final String POSTFIX_USER_LEAVING = (APPEND_CATEGORY_NAME) ? "_UserLeaving" : "";
static final String POSTFIX_VISIBILITY = (APPEND_CATEGORY_NAME) ? "_Visibility" : "";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a8ab667..6842304 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -129,6 +129,7 @@ import android.app.INotificationManager;
import android.app.IProcessObserver;
import android.app.IServiceConnection;
import android.app.IStopUserCallback;
+import android.app.IUidObserver;
import android.app.IUiAutomationConnection;
import android.app.IUserSwitchObserver;
import android.app.Instrumentation;
@@ -253,7 +254,6 @@ import java.util.concurrent.atomic.AtomicLong;
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- private static final String USER_DATA_DIR = "/data/user/";
// File that stores last updated system version and called preboot receivers
static final String CALLED_PRE_BOOTS_FILENAME = "called_pre_boots.dat";
@@ -278,6 +278,7 @@ public final class ActivityManagerService extends ActivityManagerNative
private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+ private static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
private static final String TAG_URI_PERMISSION = TAG + POSTFIX_URI_PERMISSION;
private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
@@ -655,9 +656,14 @@ public final class ActivityManagerService extends ActivityManagerNative
long mPreviousProcessVisibleTime;
/**
+ * Track all uids that have actively running processes.
+ */
+ final SparseArray<UidRecord> mActiveUids = new SparseArray<>();
+
+ /**
* Which uses have been started, so are allowed to run code.
*/
- final SparseArray<UserStartedState> mStartedUsers = new SparseArray<UserStartedState>();
+ final SparseArray<UserStartedState> mStartedUsers = new SparseArray<>();
/**
* LRU list of history of current users. Most recently current is at the end.
@@ -1023,6 +1029,11 @@ public final class ActivityManagerService extends ActivityManagerNative
private IVoiceInteractionSession mRunningVoice;
/**
+ * For some direct access we need to power manager.
+ */
+ PowerManagerInternal mLocalPowerManager;
+
+ /**
* We want to hold a wake lock while running a voice interaction session, since
* this may happen with the screen off and we need to keep the CPU running to
* be able to continue to interact with the user.
@@ -1174,7 +1185,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final long[] mTmpLong = new long[1];
- static class ProcessChangeItem {
+ static final class ProcessChangeItem {
static final int CHANGE_ACTIVITIES = 1<<0;
static final int CHANGE_PROCESS_STATE = 1<<1;
int changes;
@@ -1184,14 +1195,17 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean foregroundActivities;
}
- final RemoteCallbackList<IProcessObserver> mProcessObservers
- = new RemoteCallbackList<IProcessObserver>();
+ final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
- final ArrayList<ProcessChangeItem> mPendingProcessChanges
- = new ArrayList<ProcessChangeItem>();
- final ArrayList<ProcessChangeItem> mAvailProcessChanges
- = new ArrayList<ProcessChangeItem>();
+ final ArrayList<ProcessChangeItem> mPendingProcessChanges = new ArrayList<>();
+ final ArrayList<ProcessChangeItem> mAvailProcessChanges = new ArrayList<>();
+
+ final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>();
+ UidRecord.ChangeItem[] mActiveUidChanges = new UidRecord.ChangeItem[5];
+
+ final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>();
+ final ArrayList<UidRecord.ChangeItem> mAvailUidChanges = new ArrayList<>();
/**
* Runtime CPU use collection thread. This object's lock is used to
@@ -1316,6 +1330,7 @@ public final class ActivityManagerService extends ActivityManagerNative
static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 51;
static final int DELETE_DUMPHEAP_MSG = 52;
static final int FOREGROUND_PROFILE_CHANGED_MSG = 53;
+ static final int DISPATCH_UIDS_CHANGED = 54;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1538,6 +1553,19 @@ public final class ActivityManagerService extends ActivityManagerNative
d.dismiss();
break;
}
+ case DISPATCH_PROCESSES_CHANGED: {
+ dispatchProcessesChanged();
+ break;
+ }
+ case DISPATCH_PROCESS_DIED: {
+ final int pid = msg.arg1;
+ final int uid = msg.arg2;
+ dispatchProcessDied(pid, uid);
+ break;
+ }
+ case DISPATCH_UIDS_CHANGED: {
+ dispatchUidsChanged();
+ } break;
}
}
}
@@ -1723,16 +1751,6 @@ public final class ActivityManagerService extends ActivityManagerNative
sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
}
} break;
- case DISPATCH_PROCESSES_CHANGED: {
- dispatchProcessesChanged();
- break;
- }
- case DISPATCH_PROCESS_DIED: {
- final int pid = msg.arg1;
- final int uid = msg.arg2;
- dispatchProcessDied(pid, uid);
- break;
- }
case REPORT_MEM_USAGE_MSG: {
final ArrayList<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>)msg.obj;
Thread thread = new Thread() {
@@ -2091,7 +2109,6 @@ public final class ActivityManagerService extends ActivityManagerNative
app.pid = MY_PID;
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
- mProcessNames.put(app.processName, app.uid, app);
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.put(app.pid, app);
}
@@ -2343,6 +2360,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public void initPowerManagement() {
mStackSupervisor.initPowerManagement();
mBatteryStatsService.initPowerManagement();
+ mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");
mVoiceWakeLock.setReferenceCounted(false);
@@ -3073,10 +3091,6 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
}
app.crashHandler = crashHandler;
- mProcessNames.put(processName, app.uid, app);
- if (isolated) {
- mIsolatedProcesses.put(app.uid, app);
- }
checkTime(startTime, "startProcess: done creating new process record");
} else {
// If this is a new package in the process, add the package to the list
@@ -3562,7 +3576,6 @@ public final class ActivityManagerService extends ActivityManagerNative
mActiveProcessChanges = new ProcessChangeItem[N];
}
mPendingProcessChanges.toArray(mActiveProcessChanges);
- mAvailProcessChanges.addAll(mPendingProcessChanges);
mPendingProcessChanges.clear();
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"*** Delivering " + N + " process changes");
@@ -3595,6 +3608,12 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
mProcessObservers.finishBroadcast();
+
+ synchronized (this) {
+ for (int j=0; j<N; j++) {
+ mAvailProcessChanges.add(mActiveProcessChanges[j]);
+ }
+ }
}
private void dispatchProcessDied(int pid, int uid) {
@@ -3612,6 +3631,67 @@ public final class ActivityManagerService extends ActivityManagerNative
mProcessObservers.finishBroadcast();
}
+ private void dispatchUidsChanged() {
+ int N;
+ synchronized (this) {
+ N = mPendingUidChanges.size();
+ if (mActiveUidChanges.length < N) {
+ mActiveUidChanges = new UidRecord.ChangeItem[N];
+ }
+ for (int i=0; i<N; i++) {
+ final UidRecord.ChangeItem change = mPendingUidChanges.get(i);
+ mActiveUidChanges[i] = change;
+ change.uidRecord.pendingChange = null;
+ change.uidRecord = null;
+ }
+ mPendingUidChanges.clear();
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "*** Delivering " + N + " uid changes");
+ }
+
+ if (mLocalPowerManager != null) {
+ for (int j=0; j<N; j++) {
+ UidRecord.ChangeItem item = mActiveUidChanges[j];
+ if (item.gone) {
+ mLocalPowerManager.uidGone(item.uid);
+ } else {
+ mLocalPowerManager.updateUidProcState(item.uid, item.processState);
+ }
+ }
+ }
+
+ int i = mUidObservers.beginBroadcast();
+ while (i > 0) {
+ i--;
+ final IUidObserver observer = mUidObservers.getBroadcastItem(i);
+ if (observer != null) {
+ try {
+ for (int j=0; j<N; j++) {
+ UidRecord.ChangeItem item = mActiveUidChanges[j];
+ if (item.gone) {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "UID gone uid=" + item.uid);
+ observer.onUidGone(item.uid);
+ } else {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "UID CHANGED uid=" + item.uid
+ + ": " + item.processState);
+ observer.onUidStateChanged(item.uid, item.processState);
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mUidObservers.finishBroadcast();
+
+ synchronized (this) {
+ for (int j=0; j<N; j++) {
+ mAvailUidChanges.add(mActiveUidChanges[j]);
+ }
+ }
+ }
+
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
@@ -5623,6 +5703,47 @@ public final class ActivityManagerService extends ActivityManagerNative
return didSomething;
}
+ private final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
+ ProcessRecord old = mProcessNames.remove(name, uid);
+ if (old != null) {
+ old.uidRecord.numProcs--;
+ if (old.uidRecord.numProcs == 0) {
+ // No more processes using this uid, tell clients it is gone.
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "No more processes in " + old.uidRecord);
+ enqueueUidChangeLocked(old.uidRecord, true);
+ mActiveUids.remove(uid);
+ }
+ old.uidRecord = null;
+ }
+ mIsolatedProcesses.remove(uid);
+ return old;
+ }
+
+ private final void addProcessNameLocked(ProcessRecord proc) {
+ // We shouldn't already have a process under this name, but just in case we
+ // need to clean up whatever may be there now.
+ ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
+ if (old != null) {
+ Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+ }
+ UidRecord uidRec = mActiveUids.get(proc.uid);
+ if (uidRec == null) {
+ uidRec = new UidRecord(proc.uid);
+ // This is the first appearance of the uid, report it now!
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Creating new process uid: " + uidRec);
+ mActiveUids.put(proc.uid, uidRec);
+ enqueueUidChangeLocked(uidRec, false);
+ }
+ proc.uidRecord = uidRec;
+ uidRec.numProcs++;
+ mProcessNames.put(proc.processName, proc.uid, proc);
+ if (proc.isolated) {
+ mIsolatedProcesses.put(proc.uid, proc);
+ }
+ }
+
private final boolean removeProcessLocked(ProcessRecord app,
boolean callerWillRestart, boolean allowRestart, String reason) {
final String name = app.processName;
@@ -5630,8 +5751,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
"Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")");
- mProcessNames.remove(name, uid);
- mIsolatedProcesses.remove(app.uid);
+ removeProcessNameLocked(name, uid);
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
@@ -5684,8 +5804,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.w(TAG, "Process " + app + " failed to attach");
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId,
pid, app.uid, app.processName);
- mProcessNames.remove(app.processName, app.uid);
- mIsolatedProcesses.remove(app.uid);
+ removeProcessNameLocked(app.processName, app.uid);
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
@@ -6164,17 +6283,6 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- @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;
@@ -9885,7 +9993,6 @@ public final class ActivityManagerService extends ActivityManagerNative
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid) {
String proc = customProcess != null ? customProcess : info.processName;
- BatteryStatsImpl.Uid.Proc ps = null;
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
final int userId = UserHandle.getUserId(info.uid);
int uid = info.uid;
@@ -9920,6 +10027,7 @@ public final class ActivityManagerService extends ActivityManagerNative
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
r.persistent = true;
}
+ addProcessNameLocked(r);
return r;
}
@@ -9934,10 +10042,6 @@ public final class ActivityManagerService extends ActivityManagerNative
if (app == null) {
app = newProcessRecordLocked(info, null, isolated, 0);
- mProcessNames.put(info.processName, app.uid, app);
- if (isolated) {
- mIsolatedProcesses.put(app.uid, app);
- }
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
@@ -10613,6 +10717,21 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ public void registerUidObserver(IUidObserver observer) {
+ enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+ "registerUidObserver()");
+ synchronized (this) {
+ mUidObservers.register(observer);
+ }
+ }
+
+ @Override
+ public void unregisterUidObserver(IUidObserver observer) {
+ synchronized (this) {
+ mUidObservers.unregister(observer);
+ }
+ }
+
@Override
public boolean convertFromTranslucent(IBinder token) {
final long origId = Binder.clearCallingIdentity();
@@ -11327,7 +11446,6 @@ public final class ActivityManagerService extends ActivityManagerNative
mRecentTasks.clear();
mRecentTasks.addAll(mTaskPersister.restoreTasksLocked());
- mTaskPersister.restoreTasksFromOtherDeviceLocked();
mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
mTaskPersister.startPersisting();
@@ -12932,6 +13050,20 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ if (mActiveUids.size() > 0) {
+ if (needSep) {
+ pw.println();
+ }
+ pw.println(" UID states:");
+ for (int i=0; i<mActiveUids.size(); i++) {
+ UidRecord uidRec = mActiveUids.valueAt(i);
+ pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid);
+ pw.print(": "); pw.println(uidRec);
+ }
+ needSep = true;
+ printedAnything = true;
+ }
+
if (mLruProcesses.size() > 0) {
if (needSep) {
pw.println();
@@ -15174,7 +15306,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mAvailProcessChanges.add(item);
}
}
- mHandler.obtainMessage(DISPATCH_PROCESS_DIED, app.pid, app.info.uid, null).sendToTarget();
+ mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED, app.pid, app.info.uid, null).sendToTarget();
// If the caller is restarting this app, then leave it in its
// current lists and let the caller take care of it.
@@ -15185,8 +15317,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (!app.persistent || app.isolated) {
if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
"Removing non-persistent process during cleanup: " + app);
- mProcessNames.remove(app.processName, app.uid);
- mIsolatedProcesses.remove(app.uid);
+ removeProcessNameLocked(app.processName, app.uid);
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
@@ -15218,7 +15349,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (index < 0) {
ProcessList.remove(app.pid);
}
- mProcessNames.put(app.processName, app.uid, app);
+ addProcessNameLocked(app);
startProcessLocked(app, "restart", app.processName);
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
@@ -16191,18 +16322,12 @@ public final class ActivityManagerService extends ActivityManagerNative
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 +16342,6 @@ public final class ActivityManagerService extends ActivityManagerNative
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);
@@ -18310,7 +18432,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (NA > 0) {
item = mAvailProcessChanges.remove(NA-1);
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
- "Retreiving available item: " + item);
+ "Retrieving available item: " + item);
} else {
item = new ProcessChangeItem();
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
@@ -18322,7 +18444,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (mPendingProcessChanges.size() == 0) {
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"*** Enqueueing dispatch processes changed!");
- mHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED).sendToTarget();
+ mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED).sendToTarget();
}
mPendingProcessChanges.add(item);
}
@@ -18341,6 +18463,31 @@ public final class ActivityManagerService extends ActivityManagerNative
return success;
}
+ private final void enqueueUidChangeLocked(UidRecord uidRec, boolean gone) {
+ if (uidRec.pendingChange == null) {
+ if (mPendingUidChanges.size() == 0) {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "*** Enqueueing dispatch uid changed!");
+ mUiHandler.obtainMessage(DISPATCH_UIDS_CHANGED).sendToTarget();
+ }
+ final int NA = mAvailUidChanges.size();
+ if (NA > 0) {
+ uidRec.pendingChange = mAvailUidChanges.remove(NA-1);
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Retrieving available item: " + uidRec.pendingChange);
+ } else {
+ uidRec.pendingChange = new UidRecord.ChangeItem();
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Allocating new item: " + uidRec.pendingChange);
+ }
+ uidRec.pendingChange.uidRecord = uidRec;
+ uidRec.pendingChange.uid = uidRec.uid;
+ mPendingUidChanges.add(uidRec.pendingChange);
+ }
+ uidRec.pendingChange.gone = gone;
+ uidRec.pendingChange.processState = uidRec.setProcState;
+ }
+
private void maybeUpdateUsageStats(ProcessRecord app) {
if (DEBUG_USAGE_STATS) {
Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
@@ -18484,6 +18631,14 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.i(TAG, "updateOomAdj: top=" + TOP_ACT, e);
}
+ // Reset state in all uid records.
+ for (int i=mActiveUids.size()-1; i>=0; i--) {
+ final UidRecord uidRec = mActiveUids.valueAt(i);
+ if (false && DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Starting update of " + uidRec);
+ uidRec.reset();
+ }
+
mAdjSeq++;
mNewNumServiceProcs = 0;
mNewNumAServiceProcs = 0;
@@ -18631,6 +18786,12 @@ public final class ActivityManagerService extends ActivityManagerNative
// good to avoid having whatever code was running in them
// left sitting around after no longer needed.
app.kill("isolated not needed", true);
+ } else {
+ // Keeping this process, update its uid.
+ final UidRecord uidRec = app.uidRecord;
+ if (uidRec != null && uidRec.curProcState > app.curProcState) {
+ uidRec.curProcState = app.curProcState;
+ }
}
if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
@@ -18826,6 +18987,18 @@ public final class ActivityManagerService extends ActivityManagerNative
requestPssAllProcsLocked(now, false, mProcessStats.isMemFactorLowered());
}
+ // Update from any uid changes.
+ for (int i=mActiveUids.size()-1; i>=0; i--) {
+ final UidRecord uidRec = mActiveUids.valueAt(i);
+ if (uidRec.setProcState != uidRec.curProcState) {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Changes in " + uidRec + ": proc state from " + uidRec.setProcState
+ + " to " + uidRec.curProcState);
+ uidRec.setProcState = uidRec.curProcState;
+ enqueueUidChangeLocked(uidRec, false);
+ }
+ }
+
if (mProcessStats.shouldWriteNowLocked(now)) {
mHandler.post(new Runnable() {
@Override public void run() {
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 @@ final class ActivityRecord {
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 class ActivityRecord {
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 @@ final class ActivityRecord {
}
}
- 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 @@ final class ActivityRecord {
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 @@ final class ActivityRecord {
(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/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 29e14f8..14759c3 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -62,8 +62,8 @@ final class ProcessRecord {
final int userId; // user of process.
final String processName; // name of the process
// List of packages running in the process
- final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList
- = new ArrayMap<String, ProcessStats.ProcessStateHolder>();
+ final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList = new ArrayMap<>();
+ UidRecord uidRecord; // overall state of process's uid.
ArraySet<String> pkgDeps; // additional packages we have a dependency on
IApplicationThread thread; // the actual proc... may be null only if
// 'persistent' is true (in which case we
@@ -142,24 +142,20 @@ final class ProcessRecord {
Object adjTarget; // Debugging: target component impacting oom_adj.
Runnable crashHandler; // Optional local handler to be invoked in the process crash.
- // contains HistoryRecord objects
- final ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
+ // all activities running in the process
+ final ArrayList<ActivityRecord> activities = new ArrayList<>();
// all ServiceRecord running in this process
- final ArraySet<ServiceRecord> services = new ArraySet<ServiceRecord>();
+ final ArraySet<ServiceRecord> services = new ArraySet<>();
// services that are currently executing code (need to remain foreground).
- final ArraySet<ServiceRecord> executingServices
- = new ArraySet<ServiceRecord>();
+ final ArraySet<ServiceRecord> executingServices = new ArraySet<>();
// All ConnectionRecord this process holds
- final ArraySet<ConnectionRecord> connections
- = new ArraySet<ConnectionRecord>();
+ final ArraySet<ConnectionRecord> connections = new ArraySet<>();
// all IIntentReceivers that are registered from this process.
- final ArraySet<ReceiverList> receivers = new ArraySet<ReceiverList>();
+ final ArraySet<ReceiverList> receivers = new ArraySet<>();
// class (String) -> ContentProviderRecord
- final ArrayMap<String, ContentProviderRecord> pubProviders
- = new ArrayMap<String, ContentProviderRecord>();
+ final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
// All ContentProviderRecord process is using
- final ArrayList<ContentProviderConnection> conProviders
- = new ArrayList<ContentProviderConnection>();
+ final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();
boolean execServicesFg; // do we need to be executing services in the foreground?
boolean persistent; // always keep this application running?
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 @@ import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
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 @@ public class TaskPersister {
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 @@ public class TaskPersister {
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 @@ public class TaskPersister {
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 @@ public class TaskPersister {
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 @@ public class TaskPersister {
} 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 @@ public class TaskPersister {
} 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 @@ public class TaskPersister {
}
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 @@ public class TaskPersister {
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 @@ public class TaskPersister {
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 @@ public class TaskPersister {
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 @@ public class TaskPersister {
}
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 @@ public class TaskPersister {
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 @@ public class TaskPersister {
}
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 @@ public class TaskPersister {
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 @@ public class TaskPersister {
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 @@ public class TaskPersister {
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 @@ public class TaskPersister {
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 @@ public class TaskPersister {
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 @@ public class TaskPersister {
// 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 @@ public class TaskPersister {
}
}
}
-
- /**
- * 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.HOME_ACTIVITY_TYPE;
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 @@ final class TaskRecord {
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 @@ final class TaskRecord {
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 @@ final class TaskRecord {
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 @@ final class TaskRecord {
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 @@ final class TaskRecord {
(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/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
new file mode 100644
index 0000000..b4efbf0
--- /dev/null
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package com.android.server.am;
+
+import android.app.ActivityManager;
+import android.os.UserHandle;
+
+/**
+ * Overall information about a uid that has actively running processes.
+ */
+public final class UidRecord {
+ final int uid;
+ int curProcState;
+ int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+ int numProcs;
+
+ static final class ChangeItem {
+ UidRecord uidRecord;
+ int uid;
+ boolean gone;
+ int processState;
+ }
+
+ ChangeItem pendingChange;
+
+ public UidRecord(int _uid) {
+ uid = _uid;
+ reset();
+ }
+
+ public void reset() {
+ curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("UidRecord{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(' ');
+ UserHandle.formatUid(sb, uid);
+ sb.append(' ');
+ sb.append(ProcessList.makeProcStateString(curProcState));
+ sb.append(" / ");
+ sb.append(numProcs);
+ sb.append(" procs}");
+ return sb.toString();
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 4e83992..fba9258 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -149,7 +149,6 @@ public class NetworkMonitor extends StateMachine {
/**
* Force evaluation even if it has succeeded in the past.
* arg1 = UID responsible for requesting this reeval. Will be billed for data.
- * arg2 = Number of evaluation attempts to make. (If 0, make INITIAL_ATTEMPTS attempts.)
*/
public static final int CMD_FORCE_REEVALUATION = BASE + 8;
@@ -183,20 +182,18 @@ public class NetworkMonitor extends StateMachine {
private final int mLingerDelayMs;
private int mLingerToken = 0;
- // Negative values disable reevaluation.
- private static final String REEVALUATE_DELAY_PROPERTY = "persist.netmon.reeval_delay";
- // When connecting, attempt to validate 3 times, pausing 5s between them.
- private static final int DEFAULT_REEVALUATE_DELAY_MS = 5000;
- private static final int INITIAL_ATTEMPTS = 3;
- // If a network is not validated, make one attempt every 10 mins to see if it starts working.
- private static final int REEVALUATE_PAUSE_MS = 10*60*1000;
- private static final int PERIODIC_ATTEMPTS = 1;
- // When an application calls reportNetworkConnectivity, only make one attempt.
- private static final int REEVALUATE_ATTEMPTS = 1;
- private final int mReevaluateDelayMs;
+ // Start mReevaluateDelayMs at this value and double.
+ private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
+ private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
+ // Before network has been evaluated this many times, ignore repeated reevaluate requests.
+ private static final int IGNORE_REEVALUATE_ATTEMPTS = 5;
private int mReevaluateToken = 0;
private static final int INVALID_UID = -1;
private int mUidResponsibleForReeval = INVALID_UID;
+ // When network has been evaluated this many times:
+ // 1. report NETWORK_TEST_RESULT_INVALID
+ // 2. stop blaming UID that requested re-evaluation for further attempts
+ private static final int INITIAL_EVALUATION_ATTEMPTS = 3;
private final Context mContext;
private final Handler mConnectivityServiceHandler;
@@ -211,19 +208,12 @@ public class NetworkMonitor extends StateMachine {
// Set if the user explicitly selected "Do not use this network" in captive portal sign-in app.
private boolean mUserDoesNotWant = false;
-
- // How many times we should attempt validation. Only checked in EvaluatingState; must be set
- // before entering EvaluatingState. Note that whatever code causes us to transition to
- // EvaluatingState last decides how many attempts will be made, so if one codepath were to
- // enter EvaluatingState with a specific number of attempts, and then another were to enter it
- // with a different number of attempts, the second number would be used. This is not currently
- // a problem because EvaluatingState is not reentrant.
- private int mMaxAttempts;
+ // Avoids surfacing "Sign in to network" notification.
+ private boolean mDontDisplaySigninNotification = false;
public boolean systemReady = false;
private final State mDefaultState = new DefaultState();
- private final State mOfflineState = new OfflineState();
private final State mValidatedState = new ValidatedState();
private final State mMaybeNotifyState = new MaybeNotifyState();
private final State mEvaluatingState = new EvaluatingState();
@@ -247,7 +237,6 @@ public class NetworkMonitor extends StateMachine {
mDefaultRequest = defaultRequest;
addState(mDefaultState);
- addState(mOfflineState, mDefaultState);
addState(mValidatedState, mDefaultState);
addState(mMaybeNotifyState, mDefaultState);
addState(mEvaluatingState, mMaybeNotifyState);
@@ -260,8 +249,6 @@ public class NetworkMonitor extends StateMachine {
if (mServer == null) mServer = DEFAULT_SERVER;
mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
- mReevaluateDelayMs = SystemProperties.getInt(REEVALUATE_DELAY_PROPERTY,
- DEFAULT_REEVALUATE_DELAY_MS);
mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
@@ -289,7 +276,6 @@ public class NetworkMonitor extends StateMachine {
return HANDLED;
case CMD_NETWORK_CONNECTED:
if (DBG) log("Connected");
- mMaxAttempts = INITIAL_ATTEMPTS;
transitionTo(mEvaluatingState);
return HANDLED;
case CMD_NETWORK_DISCONNECTED:
@@ -303,7 +289,6 @@ public class NetworkMonitor extends StateMachine {
case CMD_FORCE_REEVALUATION:
if (DBG) log("Forcing reevaluation");
mUidResponsibleForReeval = message.arg1;
- mMaxAttempts = message.arg2 != 0 ? message.arg2 : REEVALUATE_ATTEMPTS;
transitionTo(mEvaluatingState);
return HANDLED;
case CMD_CAPTIVE_PORTAL_APP_FINISHED:
@@ -313,18 +298,23 @@ public class NetworkMonitor extends StateMachine {
mCaptivePortalLoggedInResponseToken = String.valueOf(new Random().nextLong());
switch (message.arg1) {
case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_DISMISSED:
- sendMessage(CMD_FORCE_REEVALUATION, 0 /* no UID */,
- 0 /* INITIAL_ATTEMPTS */);
+ sendMessage(CMD_FORCE_REEVALUATION, 0 /* no UID */, 0);
break;
case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS:
+ mDontDisplaySigninNotification = true;
// TODO: Distinguish this from a network that actually validates.
// Displaying the "!" on the system UI icon may still be a good idea.
transitionTo(mValidatedState);
break;
case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_UNWANTED:
+ mDontDisplaySigninNotification = true;
mUserDoesNotWant = true;
+ mConnectivityServiceHandler.sendMessage(obtainMessage(
+ EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0,
+ mNetworkAgentInfo));
// TODO: Should teardown network.
- transitionTo(mOfflineState);
+ mUidResponsibleForReeval = 0;
+ transitionTo(mEvaluatingState);
break;
}
return HANDLED;
@@ -334,42 +324,6 @@ public class NetworkMonitor extends StateMachine {
}
}
- // Being in the OfflineState State indicates a Network is unwanted or failed validation.
- private class OfflineState extends State {
- @Override
- public void enter() {
- mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
- NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
- if (!mUserDoesNotWant) {
- sendMessageDelayed(CMD_FORCE_REEVALUATION, 0 /* no UID */,
- PERIODIC_ATTEMPTS, REEVALUATE_PAUSE_MS);
- }
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) log(getName() + message.toString());
- switch (message.what) {
- case CMD_FORCE_REEVALUATION:
- // If the user has indicated they explicitly do not want to use this network,
- // don't allow a reevaluation as this will be pointless and could result in
- // the user being annoyed with repeated unwanted notifications.
- return mUserDoesNotWant ? HANDLED : NOT_HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- public void exit() {
- // NOTE: This removes the delayed message posted by enter() but will inadvertently
- // remove any other CMD_FORCE_REEVALUATION in the message queue. At the moment this
- // is harmless. If in the future this becomes problematic a different message could
- // be used.
- removeMessages(CMD_FORCE_REEVALUATION);
- }
- }
-
// Being in the ValidatedState State indicates a Network is:
// - Successfully validated, or
// - Wanted "as is" by the user, or
@@ -426,18 +380,20 @@ public class NetworkMonitor extends StateMachine {
}
// Being in the EvaluatingState State indicates the Network is being evaluated for internet
- // connectivity.
+ // connectivity, or that the user has indicated that this network is unwanted.
private class EvaluatingState extends State {
- private int mAttempt;
+ private int mReevaluateDelayMs;
+ private int mAttempts;
@Override
public void enter() {
- mAttempt = 1;
sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
if (mUidResponsibleForReeval != INVALID_UID) {
TrafficStats.setThreadStatsUid(mUidResponsibleForReeval);
mUidResponsibleForReeval = INVALID_UID;
}
+ mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
+ mAttempts = 0;
}
@Override
@@ -445,7 +401,7 @@ public class NetworkMonitor extends StateMachine {
if (DBG) log(getName() + message.toString());
switch (message.what) {
case CMD_REEVALUATE:
- if (message.arg1 != mReevaluateToken)
+ if (message.arg1 != mReevaluateToken || mUserDoesNotWant)
return HANDLED;
// Don't bother validating networks that don't satisify the default request.
// This includes:
@@ -469,6 +425,7 @@ public class NetworkMonitor extends StateMachine {
transitionTo(mValidatedState);
return HANDLED;
}
+ mAttempts++;
// Note: This call to isCaptivePortal() could take up to a minute. Resolving the
// server's IP addresses could hit the DNS timeout, and attempting connections
// to each of the server's several IP addresses (currently one IPv4 and one
@@ -480,16 +437,27 @@ public class NetworkMonitor extends StateMachine {
transitionTo(mValidatedState);
} else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
transitionTo(mCaptivePortalState);
- } else if (++mAttempt > mMaxAttempts) {
- transitionTo(mOfflineState);
- } else if (mReevaluateDelayMs >= 0) {
+ } else {
Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
sendMessageDelayed(msg, mReevaluateDelayMs);
+ if (mAttempts >= INITIAL_EVALUATION_ATTEMPTS) {
+ mConnectivityServiceHandler.sendMessage(obtainMessage(
+ EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0,
+ mNetworkAgentInfo));
+ // Don't continue to blame UID forever.
+ TrafficStats.clearThreadStatsUid();
+ }
+ mReevaluateDelayMs *= 2;
+ if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
+ mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
+ }
}
return HANDLED;
case CMD_FORCE_REEVALUATION:
- // Ignore duplicate requests.
- return HANDLED;
+ // Before IGNORE_REEVALUATE_ATTEMPTS attempts are made,
+ // ignore any re-evaluation requests. After, restart the
+ // evaluation process via EvaluatingState#enter.
+ return mAttempts < IGNORE_REEVALUATE_ATTEMPTS ? HANDLED : NOT_HANDLED;
default:
return NOT_HANDLED;
}
@@ -533,6 +501,8 @@ public class NetworkMonitor extends StateMachine {
public void enter() {
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
+ // Don't annoy user with sign-in notifications.
+ if (mDontDisplaySigninNotification) return;
// Create a CustomIntentReceiver that sends us a
// CMD_LAUNCH_CAPTIVE_PORTAL_APP message when the user
// touches the notification.
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 @@ public class Vpn {
}
/**
- * 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 @@ public class Vpn {
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 @@ final class DisplayDeviceInfo {
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 @@ final class DisplayDeviceInfo {
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 @@ final class LocalDisplayAdapter extends DisplayAdapter {
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 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
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 @@ final class LogicalDisplay {
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 class FingerprintService extends SystemService {
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/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 24ab3b8..1a79568 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -81,6 +81,7 @@ import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.IProcessObserver;
+import android.app.IUidObserver;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.usage.UsageStatsManagerInternal;
@@ -153,10 +154,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.AppOpsService;
import com.android.server.DeviceIdleController;
import com.android.server.LocalServices;
-import com.android.server.SystemConfig;
import com.google.android.collect.Lists;
import org.xmlpull.v1.XmlPullParser;
@@ -294,9 +293,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub
/** Set of currently active {@link Notification} tags. */
private final ArraySet<String> mActiveNotifs = new ArraySet<String>();
- /** Foreground at both UID and PID granularity. */
+ /** Foreground at UID granularity. */
final SparseIntArray mUidState = new SparseIntArray();
- final SparseArray<SparseIntArray> mUidPidState = new SparseArray<>();
/** The current maximum process state that we are considering to be foreground. */
private int mCurForegroundState = ActivityManager.PROCESS_STATE_TOP;
@@ -411,7 +409,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub
updateScreenOn();
try {
- mActivityManager.registerProcessObserver(mProcessObserver);
+ mActivityManager.registerUidObserver(mUidObserver);
mNetworkManager.registerObserver(mAlertObserver);
} catch (RemoteException e) {
// ignored; both services live in system_server
@@ -477,40 +475,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub
}
- private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
- @Override
- public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
- }
-
- @Override
- public void onProcessStateChanged(int pid, int uid, int procState) {
+ private IUidObserver mUidObserver = new IUidObserver.Stub() {
+ @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
synchronized (mRulesLock) {
- // because a uid can have multiple pids running inside, we need to
- // remember all pid states and summarize foreground at uid level.
-
- // record foreground for this specific pid
- SparseIntArray pidState = mUidPidState.get(uid);
- if (pidState == null) {
- pidState = new SparseIntArray(2);
- mUidPidState.put(uid, pidState);
- }
- pidState.put(pid, procState);
- computeUidStateLocked(uid);
+ updateUidStateLocked(uid, procState);
}
}
- @Override
- public void onProcessDied(int pid, int uid) {
+ @Override public void onUidGone(int uid) throws RemoteException {
synchronized (mRulesLock) {
- // clear records and recompute, when they exist
- final SparseIntArray pidState = mUidPidState.get(uid);
- if (pidState != null) {
- pidState.delete(pid);
- if (pidState.size() <= 0) {
- mUidPidState.remove(uid);
- }
- computeUidStateLocked(uid);
- }
+ removeUidStateLocked(uid);
}
}
};
@@ -1919,14 +1893,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub
fout.print(state);
fout.print(state <= mCurForegroundState ? " (fg)" : " (bg)");
- fout.print(" pids=");
- final int foregroundIndex = mUidPidState.indexOfKey(uid);
- if (foregroundIndex < 0) {
- fout.print("UNKNOWN");
- } else {
- dumpSparseIntArray(fout, mUidPidState.valueAt(foregroundIndex));
- }
-
fout.print(" rules=");
final int rulesIndex = mUidRules.indexOfKey(uid);
if (rulesIndex < 0) {
@@ -1957,36 +1923,38 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub
}
/**
- * Process state of PID changed; recompute state at UID level. If
- * changed, will trigger {@link #updateRulesForUidLocked(int)}.
+ * Process state of UID changed; if needed, will trigger
+ * {@link #updateRulesForUidLocked(int)}.
*/
- void computeUidStateLocked(int uid) {
- final SparseIntArray pidState = mUidPidState.get(uid);
-
- // current pid is dropping foreground; examine other pids
- int uidState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
- if (pidState != null) {
- final int size = pidState.size();
- for (int i = 0; i < size; i++) {
- final int state = pidState.valueAt(i);
- if (state < uidState) {
- uidState = state;
- }
- }
- }
-
+ void updateUidStateLocked(int uid, int uidState) {
final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
if (oldUidState != uidState) {
// state changed, push updated rules
mUidState.put(uid, uidState);
- final boolean oldForeground = oldUidState <= mCurForegroundState;
- final boolean newForeground = uidState <= mCurForegroundState;
- if (oldForeground != newForeground) {
- updateRulesForUidLocked(uid);
+ updateRulesForUidStateChangeLocked(uid, oldUidState, uidState);
+ }
+ }
+
+ void removeUidStateLocked(int uid) {
+ final int index = mUidState.indexOfKey(uid);
+ if (index >= 0) {
+ final int oldUidState = mUidState.valueAt(index);
+ mUidState.removeAt(index);
+ if (oldUidState != ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
+ updateRulesForUidStateChangeLocked(uid, oldUidState,
+ ActivityManager.PROCESS_STATE_CACHED_EMPTY);
}
}
}
+ void updateRulesForUidStateChangeLocked(int uid, int oldUidState, int newUidState) {
+ final boolean oldForeground = oldUidState <= mCurForegroundState;
+ final boolean newForeground = newUidState <= mCurForegroundState;
+ if (oldForeground != newForeground) {
+ updateRulesForUidLocked(uid);
+ }
+ }
+
private void updateScreenOn() {
synchronized (mRulesLock) {
try {
@@ -2381,16 +2349,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub
}
}
- private static void dumpSparseIntArray(PrintWriter fout, SparseIntArray value) {
- fout.print("[");
- final int size = value.size();
- for (int i = 0; i < size; i++) {
- fout.print(value.keyAt(i) + "=" + value.valueAt(i));
- if (i < size - 1) fout.print(",");
- }
- fout.print("]");
- }
-
@Override
public void factoryReset(String subscriber) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
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 @@ public class NotificationManagerService extends SystemService {
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 @@ public class NotificationManagerService extends SystemService {
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 @@ public class NotificationManagerService extends SystemService {
for (String key : noLongerVisibleKeys) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r == null) continue;
- r.stats.onVisibilityChanged(false);
+ r.setVisibility(false);
}
}
}
@@ -2784,8 +2785,11 @@ public class NotificationManagerService extends SystemService {
// 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.os.UserHandle;
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 @@ public final class NotificationRecord {
// 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 @@ public final class NotificationRecord {
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 @@ public final class NotificationRecord {
mIntercept = previous.mIntercept;
mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
mCreationTimeMs = previous.mCreationTimeMs;
+ mVisibleSinceMs = previous.mVisibleSinceMs;
// Don't copy mGlobalSortKey, recompute it.
}
@@ -181,6 +190,8 @@ public final class NotificationRecord {
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 @@ public final class NotificationRecord {
}
/**
+ * 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 @@ public final class NotificationRecord {
}
/**
+ * 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/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 509289b..0ecdd61 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6301,7 +6301,6 @@ public class PackageManagerService extends IPackageManager.Stub {
}
final String path = scanFile.getPath();
- final String codePath = pkg.applicationInfo.getCodePath();
final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
setBundledAppAbisAndRoots(pkg, pkgSetting);
@@ -6327,128 +6326,28 @@ public class PackageManagerService extends IPackageManager.Stub {
setNativeLibraryPaths(pkg);
} else {
- // TODO: We can probably be smarter about this stuff. For installed apps,
- // we can calculate this information at install time once and for all. For
- // system apps, we can probably assume that this information doesn't change
- // after the first boot scan. As things stand, we do lots of unnecessary work.
-
- // Give ourselves some initial paths; we'll come back for another
- // pass once we've determined ABI below.
- setNativeLibraryPaths(pkg);
-
- final boolean isAsec = pkg.isForwardLocked() || isExternal(pkg);
- final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir;
- final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa;
-
- NativeLibraryHelper.Handle handle = null;
- try {
- handle = NativeLibraryHelper.Handle.create(scanFile);
- // TODO(multiArch): This can be null for apps that didn't go through the
- // usual installation process. We can calculate it again, like we
- // do during install time.
- //
- // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally
- // unnecessary.
- final File nativeLibraryRoot = new File(nativeLibraryRootStr);
-
- // Null out the abis so that they can be recalculated.
- pkg.applicationInfo.primaryCpuAbi = null;
- pkg.applicationInfo.secondaryCpuAbi = null;
- if (isMultiArch(pkg.applicationInfo)) {
- // Warn if we've set an abiOverride for multi-lib packages..
- // By definition, we need to copy both 32 and 64 bit libraries for
- // such packages.
- if (pkg.cpuAbiOverride != null
- && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
- Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
- }
-
- int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
- int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
- if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
- if (isAsec) {
- abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
- } else {
- abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
- useIsaSpecificSubdirs);
- }
- }
-
- maybeThrowExceptionForMultiArchCopy(
- "Error unpackaging 32 bit native libs for multiarch app.", abi32);
-
- if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
- if (isAsec) {
- abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
- } else {
- abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
- useIsaSpecificSubdirs);
- }
- }
-
- maybeThrowExceptionForMultiArchCopy(
- "Error unpackaging 64 bit native libs for multiarch app.", abi64);
-
- if (abi64 >= 0) {
- pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
- }
-
- if (abi32 >= 0) {
- final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
- if (abi64 >= 0) {
- pkg.applicationInfo.secondaryCpuAbi = abi;
- } else {
- pkg.applicationInfo.primaryCpuAbi = abi;
- }
- }
- } else {
- String[] abiList = (cpuAbiOverride != null) ?
- new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
-
- // Enable gross and lame hacks for apps that are built with old
- // SDK tools. We must scan their APKs for renderscript bitcode and
- // not launch them if it's present. Don't bother checking on devices
- // that don't have 64 bit support.
- boolean needsRenderScriptOverride = false;
- if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
- NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
- abiList = Build.SUPPORTED_32_BIT_ABIS;
- needsRenderScriptOverride = true;
- }
-
- final int copyRet;
- if (isAsec) {
- copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
- } else {
- copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
- }
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+ deriveNonSystemPackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);
+ } else {
+ // Verify the ABIs haven't changed since we last deduced them.
+ String oldPrimaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
+ String oldSecondaryCpuAbi = pkg.applicationInfo.secondaryCpuAbi;
- if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Error unpackaging native libs for app, errorCode=" + copyRet);
- }
+ // TODO: The only purpose of this code is to update the native library paths
+ // based on the final install location. We can simplify this and avoid having
+ // to scan the package again.
+ deriveNonSystemPackageAbi(pkg, scanFile, cpuAbiOverride, false /* extract libs */);
+ if (!TextUtils.equals(oldPrimaryCpuAbi, pkg.applicationInfo.primaryCpuAbi)) {
+ throw new IllegalStateException("unexpected abi change for " + pkg.packageName + " ("
+ + oldPrimaryCpuAbi + "-> " + pkg.applicationInfo.primaryCpuAbi);
+ }
- if (copyRet >= 0) {
- pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
- } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) {
- pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
- } else if (needsRenderScriptOverride) {
- pkg.applicationInfo.primaryCpuAbi = abiList[0];
- }
+ if (!TextUtils.equals(oldSecondaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi)) {
+ throw new IllegalStateException("unexpected abi change for " + pkg.packageName + " ("
+ + oldSecondaryCpuAbi + "-> " + pkg.applicationInfo.secondaryCpuAbi);
}
- } catch (IOException ioe) {
- Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
- } finally {
- IoUtils.closeQuietly(handle);
}
- // Now that we've calculated the ABIs and determined if it's an internal app,
- // we will go ahead and populate the nativeLibraryPath.
- setNativeLibraryPaths(pkg);
-
if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
final int[] userIds = sUserManager.getUserIds();
synchronized (mInstallLock) {
@@ -6478,9 +6377,21 @@ public class PackageManagerService extends IPackageManager.Stub {
Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
}
+ // If there's a mismatch between the abi-override in the package setting
+ // and the abiOverride specified for the install. Warn about this because we
+ // would've already compiled the app without taking the package setting into
+ // account.
+ if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
+ if (cpuAbiOverride == null && pkgSetting.cpuAbiOverrideString != null) {
+ Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
+ " for package: " + pkg.packageName);
+ }
+ }
+
pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
+
// Copy the derived override back to the parsed package, so that we can
// update the package settings accordingly.
pkg.cpuAbiOverride = cpuAbiOverride;
@@ -6974,6 +6885,144 @@ public class PackageManagerService extends IPackageManager.Stub {
}
/**
+ * Derive the ABI of a non-system package located at {@code scanFile}. This information
+ * is derived purely on the basis of the contents of {@code scanFile} and
+ * {@code cpuAbiOverride}.
+ *
+ * If {@code extractLibs} is true, native libraries are extracted from the app if required.
+ */
+ public void deriveNonSystemPackageAbi(PackageParser.Package pkg, File scanFile,
+ String cpuAbiOverride, boolean extractLibs)
+ throws PackageManagerException {
+ // TODO: We can probably be smarter about this stuff. For installed apps,
+ // we can calculate this information at install time once and for all. For
+ // system apps, we can probably assume that this information doesn't change
+ // after the first boot scan. As things stand, we do lots of unnecessary work.
+
+ // Give ourselves some initial paths; we'll come back for another
+ // pass once we've determined ABI below.
+ setNativeLibraryPaths(pkg);
+
+ // We would never need to extract libs for forward-locked and external packages,
+ // since the container service will do it for us.
+ if (pkg.isForwardLocked() || isExternal(pkg)) {
+ extractLibs = false;
+ }
+
+ final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir;
+ final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa;
+
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = NativeLibraryHelper.Handle.create(scanFile);
+ // TODO(multiArch): This can be null for apps that didn't go through the
+ // usual installation process. We can calculate it again, like we
+ // do during install time.
+ //
+ // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally
+ // unnecessary.
+ final File nativeLibraryRoot = new File(nativeLibraryRootStr);
+
+ // Null out the abis so that they can be recalculated.
+ pkg.applicationInfo.primaryCpuAbi = null;
+ pkg.applicationInfo.secondaryCpuAbi = null;
+ if (isMultiArch(pkg.applicationInfo)) {
+ // Warn if we've set an abiOverride for multi-lib packages..
+ // By definition, we need to copy both 32 and 64 bit libraries for
+ // such packages.
+ if (pkg.cpuAbiOverride != null
+ && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
+ Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
+ }
+
+ int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
+ int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
+ if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
+ if (extractLibs) {
+ abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
+ useIsaSpecificSubdirs);
+ } else {
+ abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
+ }
+ }
+
+ maybeThrowExceptionForMultiArchCopy(
+ "Error unpackaging 32 bit native libs for multiarch app.", abi32);
+
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
+ if (extractLibs) {
+ abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
+ useIsaSpecificSubdirs);
+ } else {
+ abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
+ }
+ }
+
+ maybeThrowExceptionForMultiArchCopy(
+ "Error unpackaging 64 bit native libs for multiarch app.", abi64);
+
+ if (abi64 >= 0) {
+ pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
+ }
+
+ if (abi32 >= 0) {
+ final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
+ if (abi64 >= 0) {
+ pkg.applicationInfo.secondaryCpuAbi = abi;
+ } else {
+ pkg.applicationInfo.primaryCpuAbi = abi;
+ }
+ }
+ } else {
+ String[] abiList = (cpuAbiOverride != null) ?
+ new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
+
+ // Enable gross and lame hacks for apps that are built with old
+ // SDK tools. We must scan their APKs for renderscript bitcode and
+ // not launch them if it's present. Don't bother checking on devices
+ // that don't have 64 bit support.
+ boolean needsRenderScriptOverride = false;
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
+ NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+ abiList = Build.SUPPORTED_32_BIT_ABIS;
+ needsRenderScriptOverride = true;
+ }
+
+ final int copyRet;
+ if (extractLibs) {
+ copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
+ } else {
+ copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
+ }
+
+ if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Error unpackaging native libs for app, errorCode=" + copyRet);
+ }
+
+ if (copyRet >= 0) {
+ pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
+ } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) {
+ pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
+ } else if (needsRenderScriptOverride) {
+ pkg.applicationInfo.primaryCpuAbi = abiList[0];
+ }
+ }
+ } catch (IOException ioe) {
+ Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
+ } finally {
+ IoUtils.closeQuietly(handle);
+ }
+
+ // Now that we've calculated the ABIs and determined if it's an internal app,
+ // we will go ahead and populate the nativeLibraryPath.
+ setNativeLibraryPaths(pkg);
+ }
+
+ /**
* Adjusts ABIs for a set of packages belonging to a shared user so that they all match.
* i.e, so that all packages can be run inside a single process if required.
*
@@ -11566,6 +11615,16 @@ public class PackageManagerService extends IPackageManager.Stub {
} else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
// Enable SCAN_NO_DEX flag to skip dexopt at a later stage
scanFlags |= SCAN_NO_DEX;
+
+ try {
+ deriveNonSystemPackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
+ true /* extract libs */);
+ } catch (PackageManagerException pme) {
+ Slog.e(TAG, "Error deriving application ABI", pme);
+ res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error ");
+ return;
+ }
+
// Run dexopt before old package gets removed, to minimize time when app is unavailable
int result = mPackageDexOptimizer
.performDexOpt(pkg, null /* instruction sets */, true /* forceDex */,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 40c8ca3..15d1535 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -580,6 +580,28 @@ public class UserManagerService extends IUserManager.Stub {
* Check if we've hit the limit of how many users can be created.
*/
private boolean isUserLimitReachedLocked() {
+ return getAliveUsersExcludingGuestsCountLocked() >= UserManager.getMaxSupportedUsers();
+ }
+
+ @Override
+ public boolean canAddMoreManagedProfiles() {
+ checkManageUsersPermission("check if more managed profiles can be added.");
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ return false;
+ }
+ synchronized(mPackagesLock) {
+ // Limit number of managed profiles that can be created
+ if (numberOfUsersOfTypeLocked(UserInfo.FLAG_MANAGED_PROFILE, true)
+ >= MAX_MANAGED_PROFILES) {
+ return false;
+ }
+ int usersCount = getAliveUsersExcludingGuestsCountLocked();
+ // We allow creating a managed profile in the special case where there is only one user.
+ return usersCount == 1 || usersCount < UserManager.getMaxSupportedUsers();
+ }
+ }
+
+ private int getAliveUsersExcludingGuestsCountLocked() {
int aliveUserCount = 0;
final int totalUserCount = mUsers.size();
// Skip over users being removed
@@ -590,7 +612,7 @@ public class UserManagerService extends IUserManager.Stub {
aliveUserCount++;
}
}
- return aliveUserCount >= UserManager.getMaxSupportedUsers();
+ return aliveUserCount;
}
/**
@@ -1176,7 +1198,11 @@ public class UserManagerService extends IUserManager.Stub {
Log.w(LOG_TAG, "Cannot add user. DISALLOW_ADD_USER is enabled.");
return null;
}
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ return null;
+ }
final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0;
+ final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0;
final long ident = Binder.clearCallingIdentity();
UserInfo userInfo = null;
try {
@@ -1187,19 +1213,16 @@ public class UserManagerService extends IUserManager.Stub {
parent = getUserInfoLocked(parentId);
if (parent == null) return null;
}
- // If we're not adding a guest user and the limit has been reached,
- // cannot add a user.
- if (!isGuest && isUserLimitReachedLocked()) {
+ if (isManagedProfile && !canAddMoreManagedProfiles()) {
return null;
}
- // If we're adding a guest and there already exists one, bail.
- if (isGuest && findCurrentGuestUserLocked() != null) {
+ if (!isGuest && !isManagedProfile && isUserLimitReachedLocked()) {
+ // If we're not adding a guest user or a managed profile and the limit has
+ // been reached, cannot add a user.
return null;
}
- // Limit number of managed profiles that can be created
- if ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0
- && numberOfUsersOfTypeLocked(UserInfo.FLAG_MANAGED_PROFILE, true)
- >= MAX_MANAGED_PROFILES) {
+ // If we're adding a guest and there already exists one, bail.
+ if (isGuest && findCurrentGuestUserLocked() != null) {
return null;
}
int userId = getNextAvailableIdLocked();
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 @@ public class PhoneWindowManager implements WindowManagerPolicy {
};
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/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 74df0a0..5aea746 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.power;
+import android.app.ActivityManager;
+import android.util.SparseIntArray;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
@@ -436,6 +438,8 @@ public final class PowerManagerService extends SystemService
// Set of app ids that we will always respect the wake locks for.
int[] mDeviceIdleWhitelist = new int[0];
+ private final SparseIntArray mUidState = new SparseIntArray();
+
// True if theater mode is enabled
private boolean mTheaterModeEnabled;
@@ -2316,6 +2320,24 @@ public final class PowerManagerService extends SystemService
}
}
+ void updateUidProcStateInternal(int uid, int procState) {
+ synchronized (mLock) {
+ mUidState.put(uid, procState);
+ if (mDeviceIdleMode) {
+ updateWakeLockDisabledStatesLocked();
+ }
+ }
+ }
+
+ void uidGoneInternal(int uid) {
+ synchronized (mLock) {
+ mUidState.delete(uid);
+ if (mDeviceIdleMode) {
+ updateWakeLockDisabledStatesLocked();
+ }
+ }
+ }
+
private void updateWakeLockDisabledStatesLocked() {
boolean changed = false;
final int numWakeLocks = mWakeLocks.size();
@@ -2349,7 +2371,10 @@ public final class PowerManagerService extends SystemService
// If we are in idle mode, we will ignore all partial wake locks that are
// for application uids that are not whitelisted.
if (appid >= Process.FIRST_APPLICATION_UID &&
- Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0) {
+ Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0 &&
+ mUidState.get(wakeLock.mOwnerUid,
+ ActivityManager.PROCESS_STATE_CACHED_EMPTY)
+ > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
disabled = true;
}
}
@@ -2650,6 +2675,13 @@ public final class PowerManagerService extends SystemService
pw.println("Screen dim duration: " + screenDimDuration + " ms");
pw.println();
+ pw.println("UID states:");
+ for (int i=0; i<mUidState.size(); i++) {
+ pw.print(" UID "); UserHandle.formatUid(pw, mUidState.keyAt(i));
+ pw.print(": "); pw.println(mUidState.valueAt(i));
+ }
+
+ pw.println();
pw.println("Wake Locks: size=" + mWakeLocks.size());
for (WakeLock wl : mWakeLocks) {
pw.println(" " + wl);
@@ -3451,5 +3483,15 @@ public final class PowerManagerService extends SystemService
public void setDeviceIdleWhitelist(int[] appids) {
setDeviceIdleWhitelistInternal(appids);
}
+
+ @Override
+ public void updateUidProcState(int uid, int procState) {
+ updateUidProcStateInternal(uid, procState);
+ }
+
+ @Override
+ public void uidGone(int uid) {
+ uidGoneInternal(uid);
+ }
}
}
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 @@ final class AccessibilityController {
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/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 0357de2..60bbc48 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -325,8 +325,7 @@ public class AppTransition implements Dump {
mListeners.add(listener);
}
- public void notifyAppTransitionFinishedLocked(AppWindowAnimator animator) {
- IBinder token = animator != null ? animator.mAppToken.token : null;
+ public void notifyAppTransitionFinishedLocked(IBinder token) {
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onAppTransitionFinishedLocked(token);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 3feec82..2e89385 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -345,7 +345,7 @@ public class AppWindowAnimator {
for (int i = 0; i < numAllAppWinAnimators; i++) {
mAllAppWinAnimators.get(i).finishExit();
}
- mService.mAppTransition.notifyAppTransitionFinishedLocked(this);
+ mService.mAppTransition.notifyAppTransitionFinishedLocked(mAppToken.token);
return false;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 22ef801..d956d76 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -252,11 +252,6 @@ public class WindowManagerService extends IWindowManager.Stub
static final int LAYER_OFFSET_DIM = 1;
/**
- * Blur surface layer is immediately below dim layer.
- */
- static final int LAYER_OFFSET_BLUR = 2;
-
- /**
* FocusedStackFrame layer is immediately above focused window.
*/
static final int LAYER_OFFSET_FOCUSED_STACK = 1;
@@ -266,27 +261,12 @@ public class WindowManagerService extends IWindowManager.Stub
* the thumbnail (or in other words as far as possible above the window
* below it).
*/
- static final int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER-1;
-
- /**
- * Layer at which to put the rotation freeze snapshot.
- */
- static final int FREEZE_LAYER = (TYPE_LAYER_MULTIPLIER * 200) + 1;
-
- /**
- * Layer at which to put the mask for emulated screen sizes.
- */
- static final int MASK_LAYER = TYPE_LAYER_MULTIPLIER * 200;
+ static final int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1;
/** The maximum length we will accept for a loaded animation duration:
* this is 10 seconds.
*/
- static final int MAX_ANIMATION_DURATION = 10*1000;
-
- /** Amount of time (in milliseconds) to animate the fade-in-out transition for
- * compatible windows.
- */
- static final int DEFAULT_FADE_IN_OUT_DURATION = 400;
+ static final int MAX_ANIMATION_DURATION = 10 * 1000;
/** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;
@@ -381,48 +361,43 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* All currently active sessions with clients.
*/
- final ArraySet<Session> mSessions = new ArraySet<Session>();
+ final ArraySet<Session> mSessions = new ArraySet<>();
/**
* Mapping from an IWindow IBinder to the server's Window object.
* This is also used as the lock for all of our state.
* NOTE: Never call into methods that lock ActivityManagerService while holding this object.
*/
- final HashMap<IBinder, WindowState> mWindowMap = new HashMap<IBinder, WindowState>();
+ final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();
/**
* Mapping from a token IBinder to a WindowToken object.
*/
- final HashMap<IBinder, WindowToken> mTokenMap = new HashMap<IBinder, WindowToken>();
+ final HashMap<IBinder, WindowToken> mTokenMap = new HashMap<>();
/**
* List of window tokens that have finished starting their application,
* and now need to have the policy remove their windows.
*/
- final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<AppWindowToken>();
+ final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
/**
* Fake windows added to the window manager. Note: ordered from top to
* bottom, opposite of mWindows.
*/
- final ArrayList<FakeWindowImpl> mFakeWindows = new ArrayList<FakeWindowImpl>();
+ final ArrayList<FakeWindowImpl> mFakeWindows = new ArrayList<>();
/**
* Windows that are being resized. Used so we can tell the client about
* the resize after closing the transaction in which we resized the
* underlying surface.
*/
- final ArrayList<WindowState> mResizingWindows = new ArrayList<WindowState>();
+ final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
/**
* Windows whose animations have ended and now must be removed.
*/
- final ArrayList<WindowState> mPendingRemove = new ArrayList<WindowState>();
-
- /**
- * Stacks whose animations have ended and whose tasks, apps, selves may now be removed.
- */
- final ArraySet<TaskStack> mPendingStacksRemove = new ArraySet<TaskStack>();
+ final ArrayList<WindowState> mPendingRemove = new ArrayList<>();
/**
* Used when processing mPendingRemove to avoid working on the original array.
@@ -432,13 +407,13 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Windows whose surface should be destroyed.
*/
- final ArrayList<WindowState> mDestroySurface = new ArrayList<WindowState>();
+ final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
/**
* Windows that have lost input focus and are waiting for the new
* focus window to be displayed before they are told about this.
*/
- ArrayList<WindowState> mLosingFocus = new ArrayList<WindowState>();
+ ArrayList<WindowState> mLosingFocus = new ArrayList<>();
/**
* This is set when we have run out of memory, and will either be an empty
@@ -449,7 +424,7 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Windows that clients are waiting to have drawn.
*/
- ArrayList<WindowState> mWaitingForDrawn = new ArrayList<WindowState>();
+ ArrayList<WindowState> mWaitingForDrawn = new ArrayList<>();
/**
* And the callback to make when they've all been drawn.
*/
@@ -466,7 +441,7 @@ public class WindowManagerService extends IWindowManager.Stub
* This array is essentially a cache for all userId for
* {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled}
*/
- SparseArray<Boolean> mScreenCaptureDisabled = new SparseArray<Boolean>();
+ SparseArray<Boolean> mScreenCaptureDisabled = new SparseArray<>();
IInputMethodManager mInputMethodManager;
@@ -840,8 +815,7 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mInTouchMode;
private ViewServer mViewServer;
- private final ArrayList<WindowChangeListener> mWindowChangeListeners =
- new ArrayList<WindowChangeListener>();
+ private final ArrayList<WindowChangeListener> mWindowChangeListeners = new ArrayList<>();
private boolean mWindowsChanged = false;
public interface WindowChangeListener {
@@ -859,6 +833,10 @@ public class WindowManagerService extends IWindowManager.Stub
// For example, when this flag is true, there will be no wallpaper service.
final boolean mOnlyCore;
+ // List of clients without a transtiton animation that we notify once we are done transitioning
+ // since they won't be notified through the app window animator.
+ private final List<IBinder> mNoAnimationNotifyOnTransitionFinished = new ArrayList<>();
+
/** Listener to notify activity manager about app transitions. */
private final WindowManagerInternal.AppTransitionListener mActivityManagerAppTransitionNotifier
= new WindowManagerInternal.AppTransitionListener() {
@@ -5926,8 +5904,7 @@ public class WindowManagerService extends IWindowManager.Stub
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
@@ -7344,6 +7321,11 @@ public class WindowManagerService extends IWindowManager.Stub
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,
@@ -9224,6 +9206,7 @@ public class WindowManagerService extends IWindowManager.Stub
transit = AppTransition.TRANSIT_UNSET;
}
mSkipAppTransitionAnimation = false;
+ mNoAnimationNotifyOnTransitionFinished.clear();
mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
@@ -9393,7 +9376,13 @@ public class WindowManagerService extends IWindowManager.Stub
appAnimator.animation = null;
}
wtoken.inPendingTransaction = false;
- setTokenVisibilityLocked(wtoken, animLp, true, transit, false, voiceInteraction);
+ if (!setTokenVisibilityLocked(
+ wtoken, animLp, true, transit, false, voiceInteraction)){
+ // This token isn't going to be animating. Add it to the list of tokens to
+ // be notified of app transition complete since the notification will not be
+ // sent be the app window animator.
+ mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
+ }
wtoken.updateReportedVisibilityLocked();
wtoken.waitingToShow = false;
@@ -9560,6 +9549,12 @@ public class WindowManagerService extends IWindowManager.Stub
mAppTransition.setIdle();
+ for (int i = mNoAnimationNotifyOnTransitionFinished.size() - 1; i >= 0; i--) {
+ final IBinder token = mNoAnimationNotifyOnTransitionFinished.get(i);
+ mAppTransition.notifyAppTransitionFinishedLocked(token);
+ }
+ mNoAnimationNotifyOnTransitionFinished.clear();
+
if (mDeferredHideWallpaper != null) {
hideWallpapersLocked(mDeferredHideWallpaper);
mDeferredHideWallpaper = null;
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 27c97d0..638783d 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -50,7 +50,7 @@ import java.util.ArrayList;
*/
public final class UsbAlsaManager {
private static final String TAG = UsbAlsaManager.class.getSimpleName();
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static final String ALSA_DIRECTORY = "/dev/snd/";
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.IBinder;
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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
}
}
+ 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 @@ bool parse(const String8& str, ConfigDescription* out) {
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 @@ void applyVersionForCompatibility(ConfigDescription* config) {
}
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 @@ bool parseScreenLayoutLong(const char* name, ResTable_config* out) {
| 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 f73a508..04c763f 100644
--- a/tools/aapt/AaptConfig.h
+++ b/tools/aapt/AaptConfig.h
@@ -60,6 +60,7 @@ bool parseScreenWidthDp(const char* str, android::ResTable_config* out = NULL);
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 @@ enum {
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 @@ TEST(AaptConfigTest, TestParsingOfCarAttribute) {
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/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 572fdc9..771cf57 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -444,7 +444,7 @@ public final class BridgeTypedArray extends TypedArray {
@Override
public int getDimensionPixelSize(int index, int defValue) {
try {
- return getDimension(index);
+ return getDimension(index, null);
} catch (RuntimeException e) {
String s = getString(index);
@@ -474,12 +474,12 @@ public final class BridgeTypedArray extends TypedArray {
@Override
public int getLayoutDimension(int index, String name) {
try {
- // this will throw an exception
- return getDimension(index);
+ // this will throw an exception if not found.
+ return getDimension(index, name);
} catch (RuntimeException e) {
if (LayoutInflater_Delegate.sIsInInclude) {
- throw new RuntimeException();
+ throw new RuntimeException("Layout Dimension '" + name + "' not found.");
}
Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
@@ -494,9 +494,13 @@ public final class BridgeTypedArray extends TypedArray {
return getDimensionPixelSize(index, defValue);
}
- private int getDimension(int index) {
+ /** @param name attribute name, used for error reporting. */
+ private int getDimension(int index, @Nullable String name) {
String s = getString(index);
if (s == null) {
+ if (name != null) {
+ throw new RuntimeException("Attribute '" + name + "' not found");
+ }
throw new RuntimeException();
}
// Check if the value is a magic constant that doesn't require a unit.
diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
index 4072302..27b406a 100644
--- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -181,7 +181,8 @@ public class LayoutInflater_Delegate {
// ---- END CHANGES
params = group.generateLayoutParams(attrs);
-
+ } catch (RuntimeException ignored) {
+ // Ignore, just fail over to child attrs.
} finally {
// ---- START CHANGES
sIsInInclude = false;
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 final class ResourceHelper {
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 @@ interface IWifiManager
int getVerboseLoggingLevel();
- int getAggressiveHandover();
-
void enableAggressiveHandover(int enabled);
+ int getAggressiveHandover();
+ 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 @@ public class WifiInfo implements Parcelable {
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 @@ public class WifiInfo implements Parcelable {
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 @@ public class WifiManager {
* 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 @@ public class WifiManager {
* 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 @@ public class WifiManager {
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;
+ }
}