Merge "Added apis for changing the system whitelist"
diff --git a/Android.bp b/Android.bp
index 43fb48a..80f1c37 100644
--- a/Android.bp
+++ b/Android.bp
@@ -65,3 +65,25 @@
optional_subdirs = [
"core/tests/utiltests/jni",
]
+
+java_library {
+ name: "hwbinder",
+ no_framework_libs: true,
+
+ srcs: [
+ "core/java/android/os/HidlSupport.java",
+ "core/java/android/annotation/NonNull.java",
+ "core/java/android/os/HwBinder.java",
+ "core/java/android/os/HwBlob.java",
+ "core/java/android/os/HwParcel.java",
+ "core/java/android/os/IHwBinder.java",
+ "core/java/android/os/IHwInterface.java",
+ "core/java/android/os/DeadObjectException.java",
+ "core/java/android/os/DeadSystemException.java",
+ "core/java/android/os/RemoteException.java",
+ "core/java/android/util/AndroidException.java",
+ ],
+
+ dxflags: ["--core-library"],
+ installable: false,
+}
diff --git a/Android.mk b/Android.mk
index aa7caa5..98e4299 100644
--- a/Android.mk
+++ b/Android.mk
@@ -552,6 +552,8 @@
wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl \
wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl \
wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
+ wifi/java/android/net/wifi/rtt/IRttCallback.aidl \
+ wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl \
wifi/java/android/net/wifi/IWifiScanner.aidl \
wifi/java/android/net/wifi/IRttManager.aidl \
packages/services/PacProcessor/com/android/net/IProxyService.aidl \
@@ -650,32 +652,6 @@
framework_built := $(call java-lib-deps,framework)
-# HwBinder
-# =======================================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- core/java/android/os/HidlSupport.java \
- core/java/android/annotation/NonNull.java \
- core/java/android/os/HwBinder.java \
- core/java/android/os/HwBlob.java \
- core/java/android/os/HwParcel.java \
- core/java/android/os/IHwBinder.java \
- core/java/android/os/IHwInterface.java \
- core/java/android/os/DeadObjectException.java \
- core/java/android/os/DeadSystemException.java \
- core/java/android/os/RemoteException.java \
- core/java/android/util/AndroidException.java \
-
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := hwbinder
-
-LOCAL_DX_FLAGS := --core-library
-LOCAL_UNINSTALLABLE_MODULE := true
-include $(BUILD_JAVA_LIBRARY)
-
# Copy AIDL files to be preprocessed and included in the SDK,
# specified relative to the root of the build tree.
# ============================================================
@@ -721,6 +697,8 @@
frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pGroup.aidl \
frameworks/base/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.aidl \
frameworks/base/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.aidl \
+ frameworks/base/wifi/java/android/net/wifi/rtt/RangingRequest.aidl \
+ frameworks/base/wifi/java/android/net/wifi/rtt/RangingResult.aidl \
frameworks/base/wifi/java/android/net/wifi/WpsInfo.aidl \
frameworks/base/wifi/java/android/net/wifi/ScanResult.aidl \
frameworks/base/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.aidl \
diff --git a/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
index e644a1f..b4c7f54 100644
--- a/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
@@ -16,35 +16,28 @@
package android.text;
-import android.app.Activity;
+import static android.text.Layout.Alignment.ALIGN_NORMAL;
+
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
-import android.os.Bundle;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.StubActivity;
-
-import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
import android.text.style.ReplacementSpan;
import android.util.ArraySet;
-import static android.text.Layout.Alignment.ALIGN_NORMAL;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Locale;
-import java.util.Random;
-
-import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized.Parameters;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Random;
@LargeTest
@RunWith(Parameterized.class)
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
index 40b56f4..d219d3a 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
@@ -16,31 +16,26 @@
package android.widget;
-import android.app.Activity;
-import android.os.Bundle;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.StubActivity;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.rule.ActivityTestRule;
import android.text.Selection;
import android.view.KeyEvent;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Locale;
-
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized.Parameters;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.Arrays;
+import java.util.Collection;
@LargeTest
@RunWith(Parameterized.class)
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
index b100acb..b6cf7d3 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
@@ -16,31 +16,26 @@
package android.widget;
-import android.app.Activity;
-import android.os.Bundle;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.StubActivity;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.rule.ActivityTestRule;
import android.text.Selection;
import android.view.KeyEvent;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Locale;
-
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized.Parameters;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.Arrays;
+import java.util.Collection;
@LargeTest
@RunWith(Parameterized.class)
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
index 7fc5e4f..e95676b 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
@@ -16,27 +16,21 @@
package android.widget;
-import android.app.Activity;
-import android.os.Bundle;
-import android.perftests.utils.PerfStatusReporter;
-import android.util.Log;
-
import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.StubActivity;
import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
import android.support.test.rule.ActivityTestRule;
-import android.support.test.InstrumentationRegistry;
-import java.util.Locale;
-import java.util.Collection;
-import java.util.Arrays;
-
-import org.junit.Test;
import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
-import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Locale;
@LargeTest
@RunWith(Parameterized.class)
diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk
index e3f7775..2db0dd6 100644
--- a/apct-tests/perftests/multiuser/Android.mk
+++ b/apct-tests/perftests/multiuser/Android.mk
@@ -20,7 +20,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test
+ android-support-test \
+ ub-uiautomator
LOCAL_PACKAGE_NAME := MultiUserPerfTests
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
index c7bebf3..629e6f4 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
@@ -17,13 +17,16 @@
import android.os.Bundle;
import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import java.io.IOException;
import java.util.ArrayList;
// Based on //platform/frameworks/base/apct-tests/perftests/utils/BenchmarkState.java
public class BenchmarkRunner {
- private static long COOL_OFF_PERIOD_MS = 2000;
+ private static final long COOL_OFF_PERIOD_MS = 1000;
private static final int NUM_ITERATIONS = 4;
@@ -70,9 +73,13 @@
}
private void prepareForNextRun() {
- // TODO: Once http://b/63115387 is fixed, look into using "am wait-for-broadcast-idle"
- // command instead of waiting for a fixed amount of time.
SystemClock.sleep(COOL_OFF_PERIOD_MS);
+ try {
+ UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ .executeShellCommand("am wait-for-broadcast-idle");
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot execute shell command", e);
+ }
mStartTimeNs = System.nanoTime();
mPausedDurationNs = 0;
}
diff --git a/api/current.txt b/api/current.txt
index f641083..f865d14 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -919,6 +919,7 @@
field public static final int multiprocess = 16842771; // 0x1010013
field public static final int name = 16842755; // 0x1010003
field public static final int navigationBarColor = 16843858; // 0x1010452
+ field public static final int navigationBarDividerColor = 16844141; // 0x101056d
field public static final int navigationContentDescription = 16843969; // 0x10104c1
field public static final int navigationIcon = 16843968; // 0x10104c0
field public static final int navigationMode = 16843471; // 0x10102cf
@@ -6448,6 +6449,7 @@
method public void uninstallAllUserCaCerts(android.content.ComponentName);
method public void uninstallCaCert(android.content.ComponentName, byte[]);
method public void wipeData(int);
+ method public void wipeDataWithReason(int, java.lang.CharSequence);
field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
field public static final java.lang.String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE";
@@ -6682,6 +6684,7 @@
method public android.graphics.Matrix getTransformation();
method public int getVisibility();
method public java.lang.String getWebDomain();
+ method public java.lang.String getWebScheme();
method public int getWidth();
method public boolean isAccessibilityFocused();
method public boolean isActivated();
@@ -8972,6 +8975,7 @@
field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
field public static final java.lang.String INPUT_SERVICE = "input";
+ field public static final java.lang.String IPSEC_SERVICE = "ipsec";
field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler";
field public static final java.lang.String KEYGUARD_SERVICE = "keyguard";
field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps";
@@ -25639,6 +25643,67 @@
field public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
}
+ public final class IpSecAlgorithm implements android.os.Parcelable {
+ ctor public IpSecAlgorithm(java.lang.String, byte[]);
+ ctor public IpSecAlgorithm(java.lang.String, byte[], int);
+ method public int describeContents();
+ method public byte[] getKey();
+ method public java.lang.String getName();
+ method public int getTruncationLengthBits();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final java.lang.String AUTH_HMAC_MD5 = "hmac(md5)";
+ field public static final java.lang.String AUTH_HMAC_SHA1 = "hmac(sha1)";
+ field public static final java.lang.String AUTH_HMAC_SHA256 = "hmac(sha256)";
+ field public static final java.lang.String AUTH_HMAC_SHA384 = "hmac(sha384)";
+ field public static final java.lang.String AUTH_HMAC_SHA512 = "hmac(sha512)";
+ field public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
+ field public static final java.lang.String CRYPT_AES_CBC = "cbc(aes)";
+ }
+
+ public final class IpSecManager {
+ method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+ method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+ method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
+ method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
+ }
+
+ public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
+ }
+
+ public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
+ method public void close();
+ method protected void finalize();
+ method public int getSpi();
+ }
+
+ public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException {
+ method public int getSpi();
+ }
+
+ public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
+ method public void close() throws java.io.IOException;
+ method public int getPort();
+ method public java.io.FileDescriptor getSocket();
+ }
+
+ public final class IpSecTransform implements java.lang.AutoCloseable {
+ method public void close();
+ field public static final int DIRECTION_IN = 0; // 0x0
+ field public static final int DIRECTION_OUT = 1; // 0x1
+ }
+
+ public static class IpSecTransform.Builder {
+ ctor public IpSecTransform.Builder(android.content.Context);
+ method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
+ method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
+ }
+
public class LinkAddress implements android.os.Parcelable {
method public int describeContents();
method public java.net.InetAddress getAddress();
@@ -25661,7 +25726,7 @@
field public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR;
}
- public class LocalServerSocket {
+ public class LocalServerSocket implements java.io.Closeable {
ctor public LocalServerSocket(java.lang.String) throws java.io.IOException;
ctor public LocalServerSocket(java.io.FileDescriptor) throws java.io.IOException;
method public android.net.LocalSocket accept() throws java.io.IOException;
@@ -37072,6 +37137,8 @@
method public android.service.autofill.Dataset.Builder setId(java.lang.String);
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
+ method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern);
+ method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
}
public final class FillCallback {
@@ -37139,8 +37206,10 @@
}
public static class ImageTransformation.Builder {
- ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
- method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+ ctor public deprecated ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+ ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int, java.lang.CharSequence);
+ method public deprecated android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+ method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int, java.lang.CharSequence);
method public android.service.autofill.ImageTransformation build();
}
@@ -37158,6 +37227,9 @@
field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
}
+ public abstract interface Sanitizer {
+ }
+
public final class SaveCallback {
method public void onFailure(java.lang.CharSequence);
method public void onSuccess();
@@ -37181,6 +37253,7 @@
public static final class SaveInfo.Builder {
ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
ctor public SaveInfo.Builder(int);
+ method public android.service.autofill.SaveInfo.Builder addSanitizer(android.service.autofill.Sanitizer, android.view.autofill.AutofillId...);
method public android.service.autofill.SaveInfo build();
method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -37199,6 +37272,13 @@
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
}
+ public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+ ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.TextValueSanitizer> CREATOR;
+ }
+
public abstract interface Transformation {
}
@@ -40627,8 +40707,13 @@
public class DownloadStateCallback {
ctor public DownloadStateCallback();
+ ctor public DownloadStateCallback(int);
+ method public final boolean isFilterFlagSet(int);
method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+ field public static final int ALL_UPDATES = 0; // 0x0
+ field public static final int PROGRESS_UPDATES = 1; // 0x1
+ field public static final int STATE_UPDATES = 2; // 0x2
}
public final class FileInfo implements android.os.Parcelable {
@@ -40702,6 +40787,7 @@
public class ServiceInfo {
method public java.util.List<java.util.Locale> getLocales();
method public java.lang.CharSequence getNameForLocale(java.util.Locale);
+ method public java.util.Set<java.util.Locale> getNamedContentLocales();
method public java.lang.String getServiceClassName();
method public java.lang.String getServiceId();
method public java.util.Date getSessionEndTime();
@@ -45893,8 +45979,8 @@
method public boolean hasTransientState();
method public boolean hasWindowFocus();
method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
- method public void invalidate(android.graphics.Rect);
- method public void invalidate(int, int, int, int);
+ method public deprecated void invalidate(android.graphics.Rect);
+ method public deprecated void invalidate(int, int, int, int);
method public void invalidate();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void invalidateOutline();
@@ -48630,19 +48716,26 @@
package android.view.textclassifier {
public final class TextClassification {
+ method public int getActionCount();
method public float getConfidenceScore(java.lang.String);
method public java.lang.String getEntity(int);
method public int getEntityCount();
+ method public android.graphics.drawable.Drawable getIcon(int);
method public android.graphics.drawable.Drawable getIcon();
+ method public android.content.Intent getIntent(int);
method public android.content.Intent getIntent();
+ method public java.lang.CharSequence getLabel(int);
method public java.lang.CharSequence getLabel();
+ method public android.view.View.OnClickListener getOnClickListener(int);
method public android.view.View.OnClickListener getOnClickListener();
method public java.lang.String getText();
}
public static final class TextClassification.Builder {
ctor public TextClassification.Builder();
+ method public android.view.textclassifier.TextClassification.Builder addAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener);
method public android.view.textclassifier.TextClassification build();
+ method public android.view.textclassifier.TextClassification.Builder clearActions();
method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float);
method public android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable);
method public android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent);
@@ -52324,6 +52417,8 @@
field public static final int OP_CONST_CLASS = 28; // 0x1c
field public static final int OP_CONST_CLASS_JUMBO = 255; // 0xff
field public static final int OP_CONST_HIGH16 = 21; // 0x15
+ field public static final int OP_CONST_METHOD_HANDLE = 254; // 0xfe
+ field public static final int OP_CONST_METHOD_TYPE = 255; // 0xff
field public static final int OP_CONST_STRING = 26; // 0x1a
field public static final int OP_CONST_STRING_JUMBO = 27; // 0x1b
field public static final int OP_CONST_WIDE = 24; // 0x18
diff --git a/api/system-current.txt b/api/system-current.txt
index eee24de..a8b41be 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1051,6 +1051,7 @@
field public static final int multiprocess = 16842771; // 0x1010013
field public static final int name = 16842755; // 0x1010003
field public static final int navigationBarColor = 16843858; // 0x1010452
+ field public static final int navigationBarDividerColor = 16844141; // 0x101056d
field public static final int navigationContentDescription = 16843969; // 0x10104c1
field public static final int navigationIcon = 16843968; // 0x10104c0
field public static final int navigationMode = 16843471; // 0x10102cf
@@ -6313,6 +6314,7 @@
}
public class VrManager {
+ method public void setAndBindVrCompositor(android.content.ComponentName);
method public void setPersistentVrModeEnabled(boolean);
}
@@ -6682,6 +6684,7 @@
method public void uninstallAllUserCaCerts(android.content.ComponentName);
method public void uninstallCaCert(android.content.ComponentName, byte[]);
method public void wipeData(int);
+ method public void wipeDataWithReason(int, java.lang.CharSequence);
field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
@@ -6932,6 +6935,7 @@
method public android.graphics.Matrix getTransformation();
method public int getVisibility();
method public java.lang.String getWebDomain();
+ method public java.lang.String getWebScheme();
method public int getWidth();
method public boolean isAccessibilityFocused();
method public boolean isActivated();
@@ -9484,6 +9488,7 @@
field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control";
field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
field public static final java.lang.String INPUT_SERVICE = "input";
+ field public static final java.lang.String IPSEC_SERVICE = "ipsec";
field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler";
field public static final java.lang.String KEYGUARD_SERVICE = "keyguard";
field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps";
@@ -27874,6 +27879,69 @@
field public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
}
+ public final class IpSecAlgorithm implements android.os.Parcelable {
+ ctor public IpSecAlgorithm(java.lang.String, byte[]);
+ ctor public IpSecAlgorithm(java.lang.String, byte[], int);
+ method public int describeContents();
+ method public byte[] getKey();
+ method public java.lang.String getName();
+ method public int getTruncationLengthBits();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final java.lang.String AUTH_HMAC_MD5 = "hmac(md5)";
+ field public static final java.lang.String AUTH_HMAC_SHA1 = "hmac(sha1)";
+ field public static final java.lang.String AUTH_HMAC_SHA256 = "hmac(sha256)";
+ field public static final java.lang.String AUTH_HMAC_SHA384 = "hmac(sha384)";
+ field public static final java.lang.String AUTH_HMAC_SHA512 = "hmac(sha512)";
+ field public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
+ field public static final java.lang.String CRYPT_AES_CBC = "cbc(aes)";
+ }
+
+ public final class IpSecManager {
+ method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+ method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+ method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
+ method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
+ }
+
+ public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
+ }
+
+ public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
+ method public void close();
+ method protected void finalize();
+ method public int getSpi();
+ }
+
+ public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException {
+ method public int getSpi();
+ }
+
+ public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
+ method public void close() throws java.io.IOException;
+ method public int getPort();
+ method public java.io.FileDescriptor getSocket();
+ }
+
+ public final class IpSecTransform implements java.lang.AutoCloseable {
+ method public void close();
+ field public static final int DIRECTION_IN = 0; // 0x0
+ field public static final int DIRECTION_OUT = 1; // 0x1
+ }
+
+ public static class IpSecTransform.Builder {
+ ctor public IpSecTransform.Builder(android.content.Context);
+ method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
+ method public android.net.IpSecTransform.Builder setNattKeepalive(int);
+ method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
+ method public android.net.IpSecTransform.Builder setUnderlyingNetwork(android.net.Network);
+ }
+
public class LinkAddress implements android.os.Parcelable {
method public int describeContents();
method public java.net.InetAddress getAddress();
@@ -27896,7 +27964,7 @@
field public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR;
}
- public class LocalServerSocket {
+ public class LocalServerSocket implements java.io.Closeable {
ctor public LocalServerSocket(java.lang.String) throws java.io.IOException;
ctor public LocalServerSocket(java.io.FileDescriptor) throws java.io.IOException;
method public android.net.LocalSocket accept() throws java.io.IOException;
@@ -40165,6 +40233,8 @@
method public android.service.autofill.Dataset.Builder setId(java.lang.String);
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
+ method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern);
+ method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
}
public final class FillCallback {
@@ -40232,8 +40302,10 @@
}
public static class ImageTransformation.Builder {
- ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
- method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+ ctor public deprecated ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+ ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int, java.lang.CharSequence);
+ method public deprecated android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+ method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int, java.lang.CharSequence);
method public android.service.autofill.ImageTransformation build();
}
@@ -40251,6 +40323,9 @@
field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
}
+ public abstract interface Sanitizer {
+ }
+
public final class SaveCallback {
method public void onFailure(java.lang.CharSequence);
method public void onSuccess();
@@ -40274,6 +40349,7 @@
public static final class SaveInfo.Builder {
ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
ctor public SaveInfo.Builder(int);
+ method public android.service.autofill.SaveInfo.Builder addSanitizer(android.service.autofill.Sanitizer, android.view.autofill.AutofillId...);
method public android.service.autofill.SaveInfo build();
method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -40292,6 +40368,13 @@
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
}
+ public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+ ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.TextValueSanitizer> CREATOR;
+ }
+
public abstract interface Transformation {
}
@@ -40825,17 +40908,22 @@
public final class Suggestion implements android.os.Parcelable {
method public int describeContents();
+ method public int getFlags();
+ method public android.graphics.drawable.Icon getIcon();
method public java.lang.String getId();
method public android.app.PendingIntent getPendingIntent();
method public java.lang.CharSequence getSummary();
method public java.lang.CharSequence getTitle();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.settings.suggestions.Suggestion> CREATOR;
+ field public static final int FLAG_HAS_BUTTON = 1; // 0x1
}
public static class Suggestion.Builder {
ctor public Suggestion.Builder(java.lang.String);
method public android.service.settings.suggestions.Suggestion build();
+ method public android.service.settings.suggestions.Suggestion.Builder setFlags(int);
+ method public android.service.settings.suggestions.Suggestion.Builder setIcon(android.graphics.drawable.Icon);
method public android.service.settings.suggestions.Suggestion.Builder setPendingIntent(android.app.PendingIntent);
method public android.service.settings.suggestions.Suggestion.Builder setSummary(java.lang.CharSequence);
method public android.service.settings.suggestions.Suggestion.Builder setTitle(java.lang.CharSequence);
@@ -44222,8 +44310,13 @@
public class DownloadStateCallback {
ctor public DownloadStateCallback();
+ ctor public DownloadStateCallback(int);
+ method public final boolean isFilterFlagSet(int);
method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+ field public static final int ALL_UPDATES = 0; // 0x0
+ field public static final int PROGRESS_UPDATES = 1; // 0x1
+ field public static final int STATE_UPDATES = 2; // 0x2
}
public final class FileInfo implements android.os.Parcelable {
@@ -44306,6 +44399,7 @@
public class ServiceInfo {
method public java.util.List<java.util.Locale> getLocales();
method public java.lang.CharSequence getNameForLocale(java.util.Locale);
+ method public java.util.Set<java.util.Locale> getNamedContentLocales();
method public java.lang.String getServiceClassName();
method public java.lang.String getServiceId();
method public java.util.Date getSessionEndTime();
@@ -49583,8 +49677,8 @@
method public boolean hasTransientState();
method public boolean hasWindowFocus();
method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
- method public void invalidate(android.graphics.Rect);
- method public void invalidate(int, int, int, int);
+ method public deprecated void invalidate(android.graphics.Rect);
+ method public deprecated void invalidate(int, int, int, int);
method public void invalidate();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void invalidateOutline();
@@ -52324,19 +52418,26 @@
package android.view.textclassifier {
public final class TextClassification {
+ method public int getActionCount();
method public float getConfidenceScore(java.lang.String);
method public java.lang.String getEntity(int);
method public int getEntityCount();
+ method public android.graphics.drawable.Drawable getIcon(int);
method public android.graphics.drawable.Drawable getIcon();
+ method public android.content.Intent getIntent(int);
method public android.content.Intent getIntent();
+ method public java.lang.CharSequence getLabel(int);
method public java.lang.CharSequence getLabel();
+ method public android.view.View.OnClickListener getOnClickListener(int);
method public android.view.View.OnClickListener getOnClickListener();
method public java.lang.String getText();
}
public static final class TextClassification.Builder {
ctor public TextClassification.Builder();
+ method public android.view.textclassifier.TextClassification.Builder addAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener);
method public android.view.textclassifier.TextClassification build();
+ method public android.view.textclassifier.TextClassification.Builder clearActions();
method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float);
method public android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable);
method public android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent);
@@ -56385,6 +56486,8 @@
field public static final int OP_CONST_CLASS = 28; // 0x1c
field public static final int OP_CONST_CLASS_JUMBO = 255; // 0xff
field public static final int OP_CONST_HIGH16 = 21; // 0x15
+ field public static final int OP_CONST_METHOD_HANDLE = 254; // 0xfe
+ field public static final int OP_CONST_METHOD_TYPE = 255; // 0xff
field public static final int OP_CONST_STRING = 26; // 0x1a
field public static final int OP_CONST_STRING_JUMBO = 27; // 0x1b
field public static final int OP_CONST_WIDE = 24; // 0x18
diff --git a/api/test-current.txt b/api/test-current.txt
index a62bffc..38b15b4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -919,6 +919,7 @@
field public static final int multiprocess = 16842771; // 0x1010013
field public static final int name = 16842755; // 0x1010003
field public static final int navigationBarColor = 16843858; // 0x1010452
+ field public static final int navigationBarDividerColor = 16844141; // 0x101056d
field public static final int navigationContentDescription = 16843969; // 0x10104c1
field public static final int navigationIcon = 16843968; // 0x10104c0
field public static final int navigationMode = 16843471; // 0x10102cf
@@ -3863,9 +3864,13 @@
method public void moveTaskToFront(int, int);
method public void moveTaskToFront(int, int, android.os.Bundle);
method public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
+ method public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException;
+ method public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
method public deprecated void restartPackage(java.lang.String);
method public static void setVrThread(int);
method public void setWatchHeapLimit(long);
+ method public static boolean supportsMultiWindow(android.content.Context);
+ method public static boolean supportsSplitScreenMultiWindow(android.content.Context);
field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1
field public static final int LOCK_TASK_MODE_NONE = 0; // 0x0
@@ -4014,14 +4019,11 @@
}
public static class ActivityManager.StackId {
- field public static final int ASSISTANT_STACK_ID = 6; // 0x6
field public static final int DOCKED_STACK_ID = 3; // 0x3
field public static final int FREEFORM_WORKSPACE_STACK_ID = 2; // 0x2
field public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1; // 0x1
- field public static final int HOME_STACK_ID = 0; // 0x0
field public static final int INVALID_STACK_ID = -1; // 0xffffffff
field public static final int PINNED_STACK_ID = 4; // 0x4
- field public static final int RECENTS_STACK_ID = 5; // 0x5
}
public static class ActivityManager.TaskDescription implements android.os.Parcelable {
@@ -6515,6 +6517,7 @@
method public void uninstallAllUserCaCerts(android.content.ComponentName);
method public void uninstallCaCert(android.content.ComponentName, byte[]);
method public void wipeData(int);
+ method public void wipeDataWithReason(int, java.lang.CharSequence);
field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
@@ -6752,6 +6755,7 @@
method public android.graphics.Matrix getTransformation();
method public int getVisibility();
method public java.lang.String getWebDomain();
+ method public java.lang.String getWebScheme();
method public int getWidth();
method public boolean isAccessibilityFocused();
method public boolean isActivated();
@@ -9045,6 +9049,7 @@
field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
field public static final java.lang.String INPUT_SERVICE = "input";
+ field public static final java.lang.String IPSEC_SERVICE = "ipsec";
field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler";
field public static final java.lang.String KEYGUARD_SERVICE = "keyguard";
field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps";
@@ -25836,6 +25841,67 @@
field public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
}
+ public final class IpSecAlgorithm implements android.os.Parcelable {
+ ctor public IpSecAlgorithm(java.lang.String, byte[]);
+ ctor public IpSecAlgorithm(java.lang.String, byte[], int);
+ method public int describeContents();
+ method public byte[] getKey();
+ method public java.lang.String getName();
+ method public int getTruncationLengthBits();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final java.lang.String AUTH_HMAC_MD5 = "hmac(md5)";
+ field public static final java.lang.String AUTH_HMAC_SHA1 = "hmac(sha1)";
+ field public static final java.lang.String AUTH_HMAC_SHA256 = "hmac(sha256)";
+ field public static final java.lang.String AUTH_HMAC_SHA384 = "hmac(sha384)";
+ field public static final java.lang.String AUTH_HMAC_SHA512 = "hmac(sha512)";
+ field public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
+ field public static final java.lang.String CRYPT_AES_CBC = "cbc(aes)";
+ }
+
+ public final class IpSecManager {
+ method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+ method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+ method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
+ method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
+ }
+
+ public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
+ }
+
+ public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
+ method public void close();
+ method protected void finalize();
+ method public int getSpi();
+ }
+
+ public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException {
+ method public int getSpi();
+ }
+
+ public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
+ method public void close() throws java.io.IOException;
+ method public int getPort();
+ method public java.io.FileDescriptor getSocket();
+ }
+
+ public final class IpSecTransform implements java.lang.AutoCloseable {
+ method public void close();
+ field public static final int DIRECTION_IN = 0; // 0x0
+ field public static final int DIRECTION_OUT = 1; // 0x1
+ }
+
+ public static class IpSecTransform.Builder {
+ ctor public IpSecTransform.Builder(android.content.Context);
+ method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
+ method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
+ }
+
public class LinkAddress implements android.os.Parcelable {
method public int describeContents();
method public java.net.InetAddress getAddress();
@@ -25858,7 +25924,7 @@
field public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR;
}
- public class LocalServerSocket {
+ public class LocalServerSocket implements java.io.Closeable {
ctor public LocalServerSocket(java.lang.String) throws java.io.IOException;
ctor public LocalServerSocket(java.io.FileDescriptor) throws java.io.IOException;
method public android.net.LocalSocket accept() throws java.io.IOException;
@@ -31821,6 +31887,21 @@
method public static void setThreadPolicy(android.os.StrictMode.ThreadPolicy);
method public static void setViolationLogger(android.os.StrictMode.ViolationLogger);
method public static void setVmPolicy(android.os.StrictMode.VmPolicy);
+ field public static final int DETECT_CUSTOM = 8; // 0x8
+ field public static final int DETECT_DISK_READ = 2; // 0x2
+ field public static final int DETECT_DISK_WRITE = 1; // 0x1
+ field public static final int DETECT_NETWORK = 4; // 0x4
+ field public static final int DETECT_RESOURCE_MISMATCH = 16; // 0x10
+ field public static final int DETECT_UNBUFFERED_IO = 32; // 0x20
+ field public static final int DETECT_VM_ACTIVITY_LEAKS = 1024; // 0x400
+ field public static final int DETECT_VM_CLEARTEXT_NETWORK = 16384; // 0x4000
+ field public static final int DETECT_VM_CLOSABLE_LEAKS = 512; // 0x200
+ field public static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 32768; // 0x8000
+ field public static final int DETECT_VM_CURSOR_LEAKS = 256; // 0x100
+ field public static final int DETECT_VM_FILE_URI_EXPOSURE = 8192; // 0x2000
+ field public static final int DETECT_VM_INSTANCE_LEAKS = 2048; // 0x800
+ field public static final int DETECT_VM_REGISTRATION_LEAKS = 4096; // 0x1000
+ field public static final int DETECT_VM_UNTAGGED_SOCKET = -2147483648; // 0x80000000
}
public static final class StrictMode.ThreadPolicy {
@@ -37345,6 +37426,8 @@
method public android.service.autofill.Dataset.Builder setId(java.lang.String);
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
+ method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern);
+ method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
}
public final class FillCallback {
@@ -37413,11 +37496,17 @@
}
public static class ImageTransformation.Builder {
- ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
- method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+ ctor public deprecated ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+ ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int, java.lang.CharSequence);
+ method public deprecated android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+ method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int, java.lang.CharSequence);
method public android.service.autofill.ImageTransformation build();
}
+ public abstract class InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+ ctor public InternalSanitizer();
+ }
+
public final class LuhnChecksumValidator implements android.os.Parcelable android.service.autofill.Validator {
ctor public LuhnChecksumValidator(android.view.autofill.AutofillId...);
method public int describeContents();
@@ -37434,6 +37523,9 @@
field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
}
+ public abstract interface Sanitizer {
+ }
+
public final class SaveCallback {
method public void onFailure(java.lang.CharSequence);
method public void onSuccess();
@@ -37457,6 +37549,7 @@
public static final class SaveInfo.Builder {
ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
ctor public SaveInfo.Builder(int);
+ method public android.service.autofill.SaveInfo.Builder addSanitizer(android.service.autofill.Sanitizer, android.view.autofill.AutofillId...);
method public android.service.autofill.SaveInfo build();
method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -37475,6 +37568,14 @@
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
}
+ public final class TextValueSanitizer extends android.service.autofill.InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+ ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
+ method public int describeContents();
+ method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.TextValueSanitizer> CREATOR;
+ }
+
public abstract interface Transformation {
}
@@ -40961,8 +41062,13 @@
public class DownloadStateCallback {
ctor public DownloadStateCallback();
+ ctor public DownloadStateCallback(int);
+ method public final boolean isFilterFlagSet(int);
method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+ field public static final int ALL_UPDATES = 0; // 0x0
+ field public static final int PROGRESS_UPDATES = 1; // 0x1
+ field public static final int STATE_UPDATES = 2; // 0x2
}
public final class FileInfo implements android.os.Parcelable {
@@ -41036,6 +41142,7 @@
public class ServiceInfo {
method public java.util.List<java.util.Locale> getLocales();
method public java.lang.CharSequence getNameForLocale(java.util.Locale);
+ method public java.util.Set<java.util.Locale> getNamedContentLocales();
method public java.lang.String getServiceClassName();
method public java.lang.String getServiceId();
method public java.util.Date getSessionEndTime();
@@ -46417,8 +46524,8 @@
method public boolean hasTransientState();
method public boolean hasWindowFocus();
method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
- method public void invalidate(android.graphics.Rect);
- method public void invalidate(int, int, int, int);
+ method public deprecated void invalidate(android.graphics.Rect);
+ method public deprecated void invalidate(int, int, int, int);
method public void invalidate();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void invalidateOutline();
@@ -49170,19 +49277,26 @@
package android.view.textclassifier {
public final class TextClassification {
+ method public int getActionCount();
method public float getConfidenceScore(java.lang.String);
method public java.lang.String getEntity(int);
method public int getEntityCount();
+ method public android.graphics.drawable.Drawable getIcon(int);
method public android.graphics.drawable.Drawable getIcon();
+ method public android.content.Intent getIntent(int);
method public android.content.Intent getIntent();
+ method public java.lang.CharSequence getLabel(int);
method public java.lang.CharSequence getLabel();
+ method public android.view.View.OnClickListener getOnClickListener(int);
method public android.view.View.OnClickListener getOnClickListener();
method public java.lang.String getText();
}
public static final class TextClassification.Builder {
ctor public TextClassification.Builder();
+ method public android.view.textclassifier.TextClassification.Builder addAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener);
method public android.view.textclassifier.TextClassification build();
+ method public android.view.textclassifier.TextClassification.Builder clearActions();
method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float);
method public android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable);
method public android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent);
@@ -52879,6 +52993,8 @@
field public static final int OP_CONST_CLASS = 28; // 0x1c
field public static final int OP_CONST_CLASS_JUMBO = 255; // 0xff
field public static final int OP_CONST_HIGH16 = 21; // 0x15
+ field public static final int OP_CONST_METHOD_HANDLE = 254; // 0xfe
+ field public static final int OP_CONST_METHOD_TYPE = 255; // 0xff
field public static final int OP_CONST_STRING = 26; // 0x1a
field public static final int OP_CONST_STRING_JUMBO = 27; // 0x1b
field public static final int OP_CONST_WIDE = 24; // 0x18
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 73ec63f..b16188e 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -24,14 +24,20 @@
BootAnimationUtil.cpp \
ifeq ($(PRODUCT_IOT),true)
+
+LOCAL_SHARED_LIBRARIES += \
+ libandroidthings \
+ libchrome \
+
LOCAL_SRC_FILES += \
iot/iotbootanimation_main.cpp \
- iot/BootAction.cpp
+ iot/BootAction.cpp \
+ iot/BootParameters.cpp \
LOCAL_SHARED_LIBRARIES += \
libandroidthings \
libbase \
- libbinder
+ libbinder \
LOCAL_STATIC_LIBRARIES += cpufeatures
@@ -77,13 +83,19 @@
libutils \
libbinder \
libui \
- libskia \
+ libhwui \
libEGL \
libGLESv1_CM \
libgui \
libtinyalsa \
libbase
+ifeq ($(PRODUCT_IOT),true)
+
+LOCAL_INIT_RC := iot/bootanim_iot.rc
+
+endif # PRODUCT_IOT
+
ifdef TARGET_32_BIT_SURFACEFLINGER
LOCAL_32_BIT_ONLY := true
endif
diff --git a/cmds/bootanimation/iot/BootAction.cpp b/cmds/bootanimation/iot/BootAction.cpp
index 889eb2f..fa79744 100644
--- a/cmds/bootanimation/iot/BootAction.cpp
+++ b/cmds/bootanimation/iot/BootAction.cpp
@@ -18,17 +18,11 @@
#define LOG_TAG "BootAction"
-#include <android-base/strings.h>
-#include <cpu-features.h>
#include <dlfcn.h>
+
#include <pio/peripheral_manager_client.h>
#include <utils/Log.h>
-using android::base::Split;
-using android::base::Join;
-using android::base::StartsWith;
-using android::base::EndsWith;
-
namespace android {
BootAction::~BootAction() {
@@ -37,7 +31,8 @@
}
}
-bool BootAction::init(const std::string& libraryPath) {
+bool BootAction::init(const std::string& libraryPath,
+ const std::vector<ABootActionParameter>& parameters) {
APeripheralManagerClient* client = nullptr;
ALOGD("Connecting to peripheralmanager");
// Wait for peripheral manager to come up.
@@ -51,6 +46,7 @@
ALOGD("Peripheralmanager is up.");
APeripheralManagerClient_delete(client);
+
ALOGI("Loading boot action %s", libraryPath.c_str());
mLibHandle = dlopen(libraryPath.c_str(), RTLD_NOW);
if (mLibHandle == nullptr) {
@@ -82,7 +78,7 @@
}
ALOGD("Entering boot_action_init");
- bool result = mLibInit();
+ bool result = mLibInit(parameters.data(), parameters.size());
ALOGD("Returned from boot_action_init");
return result;
}
diff --git a/cmds/bootanimation/iot/BootAction.h b/cmds/bootanimation/iot/BootAction.h
index 495aa4f..5e2495f 100644
--- a/cmds/bootanimation/iot/BootAction.h
+++ b/cmds/bootanimation/iot/BootAction.h
@@ -18,7 +18,9 @@
#define _BOOTANIMATION_BOOTACTION_H
#include <string>
+#include <vector>
+#include <boot_action/boot_action.h> // libandroidthings native API.
#include <utils/RefBase.h>
namespace android {
@@ -28,7 +30,8 @@
~BootAction();
// libraryPath is a fully qualified path to the target .so library.
- bool init(const std::string& libraryPath);
+ bool init(const std::string& libraryPath,
+ const std::vector<ABootActionParameter>& parameters);
// The animation is going to start playing partNumber for the playCount'th
// time, update the action as needed.
@@ -41,7 +44,8 @@
void shutdown();
private:
- typedef bool (*libInit)();
+ typedef bool (*libInit)(const ABootActionParameter* parameters,
+ size_t num_parameters);
typedef void (*libStartPart)(int partNumber, int playNumber);
typedef void (*libShutdown)();
diff --git a/cmds/bootanimation/iot/BootParameters.cpp b/cmds/bootanimation/iot/BootParameters.cpp
new file mode 100644
index 0000000..da6ad0d
--- /dev/null
+++ b/cmds/bootanimation/iot/BootParameters.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "BootParameters.h"
+
+#define LOG_TAG "BootParameters"
+
+#include <fcntl.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <base/json/json_parser.h>
+#include <base/json/json_reader.h>
+#include <base/json/json_value_converter.h>
+#include <utils/Log.h>
+
+using android::base::RemoveFileIfExists;
+using android::base::ReadFileToString;
+using base::JSONReader;
+using base::JSONValueConverter;
+using base::Value;
+
+namespace android {
+
+namespace {
+
+// Brightness and volume are stored as integer strings in next_boot.json.
+// They are divided by this constant to produce the actual float values in
+// range [0.0, 1.0]. This constant must match its counterpart in
+// DeviceManager.
+constexpr const float kFloatScaleFactor = 1000.0f;
+
+constexpr const char* kNextBootFile = "/data/misc/bootanimation/next_boot.json";
+constexpr const char* kLastBootFile = "/data/misc/bootanimation/last_boot.json";
+
+void swapBootConfigs() {
+ // rename() will fail if next_boot.json doesn't exist, so delete
+ // last_boot.json manually first.
+ std::string err;
+ if (!RemoveFileIfExists(kLastBootFile, &err))
+ ALOGE("Unable to delete last boot file: %s", err.c_str());
+
+ if (rename(kNextBootFile, kLastBootFile) && errno != ENOENT)
+ ALOGE("Unable to swap boot files: %s", strerror(errno));
+
+ int fd = open(kNextBootFile, O_CREAT, DEFFILEMODE);
+ if (fd == -1) {
+ ALOGE("Unable to create next boot file: %s", strerror(errno));
+ } else {
+ // Make next_boot.json writable to everyone so DeviceManagementService
+ // can save saved_parameters there.
+ if (fchmod(fd, DEFFILEMODE))
+ ALOGE("Unable to set next boot file permissions: %s", strerror(errno));
+ close(fd);
+ }
+}
+
+} // namespace
+
+BootParameters::SavedBootParameters::SavedBootParameters()
+ : brightness(-kFloatScaleFactor), volume(-kFloatScaleFactor) {}
+
+void BootParameters::SavedBootParameters::RegisterJSONConverter(
+ JSONValueConverter<SavedBootParameters>* converter) {
+ converter->RegisterIntField("brightness", &SavedBootParameters::brightness);
+ converter->RegisterIntField("volume", &SavedBootParameters::volume);
+ converter->RegisterRepeatedString("param_names",
+ &SavedBootParameters::param_names);
+ converter->RegisterRepeatedString("param_values",
+ &SavedBootParameters::param_values);
+}
+
+BootParameters::BootParameters() {
+ swapBootConfigs();
+ loadParameters();
+}
+
+void BootParameters::loadParameters() {
+ std::string contents;
+ if (!ReadFileToString(kLastBootFile, &contents)) {
+ if (errno != ENOENT)
+ ALOGE("Unable to read from %s: %s", kLastBootFile, strerror(errno));
+
+ return;
+ }
+
+ std::unique_ptr<Value> json = JSONReader::Read(contents);
+ if (json.get() == nullptr) {
+ return;
+ }
+
+ JSONValueConverter<SavedBootParameters> converter;
+ if (converter.Convert(*(json.get()), &mRawParameters)) {
+ mBrightness = mRawParameters.brightness / kFloatScaleFactor;
+ mVolume = mRawParameters.volume / kFloatScaleFactor;
+
+ if (mRawParameters.param_names.size() == mRawParameters.param_values.size()) {
+ for (size_t i = 0; i < mRawParameters.param_names.size(); i++) {
+ mParameters.push_back({
+ .key = mRawParameters.param_names[i]->c_str(),
+ .value = mRawParameters.param_values[i]->c_str()
+ });
+ }
+ } else {
+ ALOGW("Parameter names and values size mismatch");
+ }
+ }
+}
+
+} // namespace android
diff --git a/cmds/bootanimation/iot/BootParameters.h b/cmds/bootanimation/iot/BootParameters.h
new file mode 100644
index 0000000..ff3b018
--- /dev/null
+++ b/cmds/bootanimation/iot/BootParameters.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef _BOOTANIMATION_BOOT_PARAMETERS_H_
+#define _BOOTANIMATION_BOOT_PARAMETERS_H_
+
+#include <list>
+#include <vector>
+
+#include <base/json/json_value_converter.h>
+#include <boot_action/boot_action.h> // libandroidthings native API.
+
+namespace android {
+
+// Provides access to the parameters set by DeviceManager.reboot().
+class BootParameters {
+public:
+ // Constructor loads the parameters for this boot and swaps the param files
+ // to clear the parameters for next boot.
+ BootParameters();
+
+ // Returns true if volume/brightness were explicitly set on reboot.
+ bool hasVolume() const { return mVolume >= 0; }
+ bool hasBrightness() const { return mBrightness >= 0; }
+
+ // Returns volume/brightness in [0,1], or -1 if unset.
+ float getVolume() const { return mVolume; }
+ float getBrightness() const { return mBrightness; }
+
+ // Returns the additional boot parameters that were set on reboot.
+ const std::vector<ABootActionParameter>& getParameters() const { return mParameters; }
+
+private:
+ // Raw boot saved_parameters loaded from .json.
+ struct SavedBootParameters {
+ int brightness;
+ int volume;
+ ScopedVector<std::string> param_names;
+ ScopedVector<std::string> param_values;
+
+ SavedBootParameters();
+ static void RegisterJSONConverter(
+ ::base::JSONValueConverter<SavedBootParameters>* converter);
+ };
+
+ void loadParameters();
+
+ float mVolume = -1.f;
+ float mBrightness = -1.f;
+ std::vector<ABootActionParameter> mParameters;
+
+ // ABootActionParameter is just a raw pointer so we need to keep the
+ // original strings around to avoid losing them.
+ SavedBootParameters mRawParameters;
+};
+
+} // namespace android
+
+
+#endif // _BOOTANIMATION_BOOT_PARAMETERS_H_
diff --git a/cmds/bootanimation/iot/bootanim_iot.rc b/cmds/bootanimation/iot/bootanim_iot.rc
new file mode 100644
index 0000000..2fc1336
--- /dev/null
+++ b/cmds/bootanimation/iot/bootanim_iot.rc
@@ -0,0 +1,2 @@
+on post-fs-data
+ mkdir /data/misc/bootanimation 0777 root root
diff --git a/cmds/bootanimation/iot/iotbootanimation_main.cpp b/cmds/bootanimation/iot/iotbootanimation_main.cpp
index d62478b..742f9c24 100644
--- a/cmds/bootanimation/iot/iotbootanimation_main.cpp
+++ b/cmds/bootanimation/iot/iotbootanimation_main.cpp
@@ -28,6 +28,7 @@
#include "BootAction.h"
#include "BootAnimationUtil.h"
+#include "BootParameters.h"
using namespace android;
using android::base::ReadFileToString;
@@ -37,7 +38,11 @@
namespace {
-class BootActionAnimationCallbacks : public android::BootAnimation::Callbacks {public:
+class BootActionAnimationCallbacks : public android::BootAnimation::Callbacks {
+public:
+ BootActionAnimationCallbacks(std::unique_ptr<BootParameters> bootParameters)
+ : mBootParameters(std::move(bootParameters)) {}
+
void init(const Vector<Animation::Part>&) override {
std::string library_path("/oem/lib/");
@@ -51,7 +56,7 @@
library_path += property;
mBootAction = new BootAction();
- if (!mBootAction->init(library_path)) {
+ if (!mBootAction->init(library_path, mBootParameters->getParameters())) {
mBootAction = NULL;
}
};
@@ -64,6 +69,20 @@
void shutdown() override {
if (mBootAction != nullptr) {
+ // If we have a bootaction we want to wait until we are actually
+ // told to shut down. If the animation exits early keep the action
+ // running.
+ char value[PROPERTY_VALUE_MAX] = {0};
+ for (int exitRequested = 0; exitRequested == 0; ) {
+ property_get("service.bootanim.exit", value, "0");
+ exitRequested = atoi(value);
+
+ // Poll value at 10hz.
+ if (exitRequested == 0) {
+ usleep(100000);
+ }
+ }
+
mBootAction->shutdown();
// Give it two seconds to shut down.
sleep(2);
@@ -72,6 +91,7 @@
};
private:
+ std::unique_ptr<BootParameters> mBootParameters;
sp<BootAction> mBootAction = nullptr;
};
@@ -80,6 +100,9 @@
int main() {
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
+ // Clear our params for next boot no matter what.
+ std::unique_ptr<BootParameters> bootParameters(new BootParameters());
+
if (bootAnimationDisabled()) {
ALOGI("boot animation disabled");
return 0;
@@ -90,7 +113,8 @@
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
- sp<BootAnimation> boot = new BootAnimation(new BootActionAnimationCallbacks());
+ sp<BootAnimation> boot = new BootAnimation(
+ new BootActionAnimationCallbacks(std::move(bootParameters)));
IPCThreadState::self()->joinThreadPool();
return 0;
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index 830bf9e..cb5fd02 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -23,7 +23,7 @@
LOCAL_MODULE := incidentd
LOCAL_SRC_FILES := \
- src/EncodedBuffer.cpp \
+ src/PrivacyBuffer.cpp \
src/FdBuffer.cpp \
src/IncidentService.cpp \
src/Privacy.cpp \
@@ -31,7 +31,6 @@
src/Section.cpp \
src/io_util.cpp \
src/main.cpp \
- src/protobuf.cpp \
src/report_directory.cpp
LOCAL_CFLAGS += \
@@ -54,6 +53,7 @@
libcutils \
libincident \
liblog \
+ libprotoutil \
libselinux \
libservices \
libutils
@@ -93,16 +93,15 @@
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
LOCAL_SRC_FILES := \
- src/EncodedBuffer.cpp \
+ src/PrivacyBuffer.cpp \
src/FdBuffer.cpp \
src/Privacy.cpp \
src/Reporter.cpp \
src/Section.cpp \
src/io_util.cpp \
- src/protobuf.cpp \
src/report_directory.cpp \
tests/section_list.cpp \
- tests/EncodedBuffer_test.cpp \
+ tests/PrivacyBuffer_test.cpp \
tests/FdBuffer_test.cpp \
tests/Reporter_test.cpp \
tests/Section_test.cpp \
@@ -116,6 +115,7 @@
libcutils \
libincident \
liblog \
+ libprotoutil \
libselinux \
libservices \
libutils \
diff --git a/cmds/incidentd/src/EncodedBuffer.cpp b/cmds/incidentd/src/EncodedBuffer.cpp
deleted file mode 100644
index e8f2c11..0000000
--- a/cmds/incidentd/src/EncodedBuffer.cpp
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#include "EncodedBuffer.h"
-#include "io_util.h"
-#include "protobuf.h"
-
-#include <deque>
-
-const size_t BUFFER_SIZE = 4 * 1024; // 4 KB
-
-/**
- * Read varint from iterator, the iterator will point to next available byte.
- * Return the number of bytes of the varint.
- */
-static uint32_t
-read_raw_varint(FdBuffer::iterator* it)
-{
- uint32_t val = 0;
- int i = 0;
- bool hasNext = true;
- while (hasNext) {
- hasNext = ((**it & 0x80) != 0);
- val += (**it & 0x7F) << (7*i);
- (*it)++;
- i++;
- }
- return val;
-}
-
-/**
- * Write the field to buf based on the wire type, iterator will point to next field.
- * If skip is set to true, no data will be written to buf. Return number of bytes written.
- */
-static size_t
-write_field_or_skip(FdBuffer::iterator* iter, vector<uint8_t>* buf, uint8_t wireType, bool skip)
-{
- FdBuffer::iterator snapshot = iter->snapshot();
- size_t bytesToWrite = 0;
- uint32_t varint = 0;
- switch (wireType) {
- case WIRE_TYPE_VARINT:
- varint = read_raw_varint(iter);
- if(!skip) return write_raw_varint(buf, varint);
- break;
- case WIRE_TYPE_FIXED64:
- bytesToWrite = 8;
- break;
- case WIRE_TYPE_LENGTH_DELIMITED:
- bytesToWrite = read_raw_varint(iter);
- if(!skip) write_raw_varint(buf, bytesToWrite);
- break;
- case WIRE_TYPE_FIXED32:
- bytesToWrite = 4;
- break;
- }
- if (skip) {
- *iter += bytesToWrite;
- } else {
- for (size_t i=0; i<bytesToWrite; i++) {
- buf->push_back(**iter);
- (*iter)++;
- }
- }
- return skip ? 0 : *iter - snapshot;
-}
-
-/**
- * Strip next field based on its private policy and request spec, then stores data in buf.
- * Return NO_ERROR if succeeds, otherwise BAD_VALUE is returned to indicate bad data in FdBuffer.
- *
- * The iterator must point to the head of a protobuf formatted field for successful operation.
- * After exit with NO_ERROR, iterator points to the next protobuf field's head.
- */
-static status_t
-stripField(FdBuffer::iterator* iter, vector<uint8_t>* buf, const Privacy* parentPolicy, const PrivacySpec& spec)
-{
- if (iter->outOfBound() || parentPolicy == NULL) return BAD_VALUE;
-
- uint32_t varint = read_raw_varint(iter);
- uint8_t wireType = read_wire_type(varint);
- uint32_t fieldId = read_field_id(varint);
- const Privacy* policy = parentPolicy->lookup(fieldId);
-
- if (policy == NULL || !policy->IsMessageType() || !policy->HasChildren()) {
- bool skip = !spec.CheckPremission(policy);
- size_t amt = buf->size();
- if (!skip) amt += write_header(buf, fieldId, wireType);
- amt += write_field_or_skip(iter, buf, wireType, skip); // point to head of next field
- return buf->size() != amt ? BAD_VALUE : NO_ERROR;
- }
- // current field is message type and its sub-fields have extra privacy policies
- deque<vector<uint8_t>> q;
- uint32_t msgSize = read_raw_varint(iter);
- size_t finalSize = 0;
- FdBuffer::iterator start = iter->snapshot();
- while ((*iter - start) != (int)msgSize) {
- vector<uint8_t> v;
- status_t err = stripField(iter, &v, policy, spec);
- if (err != NO_ERROR) return err;
- if (v.empty()) continue;
- q.push_back(v);
- finalSize += v.size();
- }
-
- write_header(buf, fieldId, wireType);
- write_raw_varint(buf, finalSize);
- buf->reserve(finalSize); // reserve the size of the field
- while (!q.empty()) {
- vector<uint8_t> subField = q.front();
- for (vector<uint8_t>::iterator it = subField.begin(); it != subField.end(); it++) {
- buf->push_back(*it);
- }
- q.pop_front();
- }
- return NO_ERROR;
-}
-
-// ================================================================================
-EncodedBuffer::EncodedBuffer(const FdBuffer& buffer, const Privacy* policy)
- : mFdBuffer(buffer),
- mPolicy(policy),
- mBuffers(),
- mSize(0)
-{
-}
-
-EncodedBuffer::~EncodedBuffer()
-{
-}
-
-status_t
-EncodedBuffer::strip(const PrivacySpec& spec)
-{
- // optimization when no strip happens
- if (mPolicy == NULL || !mPolicy->HasChildren() || spec.RequireAll()) {
- if (spec.CheckPremission(mPolicy)) mSize = mFdBuffer.size();
- return NO_ERROR;
- }
-
- FdBuffer::iterator it = mFdBuffer.begin();
- vector<uint8_t> field;
- field.reserve(BUFFER_SIZE);
-
- while (it != mFdBuffer.end()) {
- status_t err = stripField(&it, &field, mPolicy, spec);
- if (err != NO_ERROR) return err;
- if (field.size() > BUFFER_SIZE) { // rotate to another chunk if buffer size exceeds
- mBuffers.push_back(field);
- mSize += field.size();
- field.clear();
- }
- }
- if (!field.empty()) {
- mBuffers.push_back(field);
- mSize += field.size();
- }
- return NO_ERROR;
-}
-
-void
-EncodedBuffer::clear()
-{
- mSize = 0;
- mBuffers.clear();
-}
-
-size_t
-EncodedBuffer::size() const { return mSize; }
-
-status_t
-EncodedBuffer::flush(int fd)
-{
- if (size() == mFdBuffer.size()) return mFdBuffer.flush(fd);
-
- for (vector<vector<uint8_t>>::iterator it = mBuffers.begin(); it != mBuffers.end(); it++) {
- status_t err = write_all(fd, it->data(), it->size());
- if (err != NO_ERROR) return err;
- }
- return NO_ERROR;
-}
-
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index bb399b5..b7633a4 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "incidentd"
#include "FdBuffer.h"
-#include "io_util.h"
#include <cutils/log.h>
#include <utils/SystemClock.h>
@@ -31,10 +30,9 @@
const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max
FdBuffer::FdBuffer()
- :mBuffers(),
+ :mBuffer(BUFFER_SIZE),
mStartTime(-1),
mFinishTime(-1),
- mCurrentWritten(-1),
mTimedOut(false),
mTruncated(false)
{
@@ -42,11 +40,6 @@
FdBuffer::~FdBuffer()
{
- const int N = mBuffers.size();
- for (int i=0; i<N; i++) {
- uint8_t* buf = mBuffers[i];
- free(buf);
- }
}
status_t
@@ -60,20 +53,12 @@
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
- uint8_t* buf = NULL;
while (true) {
- if (mCurrentWritten >= BUFFER_SIZE || mCurrentWritten < 0) {
- if (mBuffers.size() == MAX_BUFFER_COUNT) {
- mTruncated = true;
- break;
- }
- buf = (uint8_t*)malloc(BUFFER_SIZE);
- if (buf == NULL) {
- return NO_MEMORY;
- }
- mBuffers.push_back(buf);
- mCurrentWritten = 0;
+ if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+ mTruncated = true;
+ break;
}
+ if (mBuffer.writeBuffer() == NULL) return NO_MEMORY;
int64_t remainingTime = (mStartTime + timeout) - uptimeMillis();
if (remainingTime <= 0) {
@@ -91,7 +76,7 @@
if ((pfds.revents & POLLERR) != 0) {
return errno != 0 ? -errno : UNKNOWN_ERROR;
} else {
- ssize_t amt = ::read(fd, buf + mCurrentWritten, BUFFER_SIZE - mCurrentWritten);
+ ssize_t amt = ::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
if (amt < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
continue;
@@ -101,11 +86,10 @@
} else if (amt == 0) {
break;
}
- mCurrentWritten += amt;
+ mBuffer.wp()->move(amt);
}
}
}
-
mFinishTime = uptimeMillis();
return NO_ERROR;
}
@@ -132,20 +116,12 @@
int rpos = 0, wpos = 0;
// This is the buffer used to store processed data
- uint8_t* buf = NULL;
while (true) {
- if (mCurrentWritten >= BUFFER_SIZE || mCurrentWritten < 0) {
- if (mBuffers.size() == MAX_BUFFER_COUNT) {
- mTruncated = true;
- break;
- }
- buf = (uint8_t*)malloc(BUFFER_SIZE);
- if (buf == NULL) {
- return NO_MEMORY;
- }
- mBuffers.push_back(buf);
- mCurrentWritten = 0;
+ if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+ mTruncated = true;
+ break;
}
+ if (mBuffer.writeBuffer() == NULL) return NO_MEMORY;
int64_t remainingTime = (mStartTime + timeoutMs) - uptimeMillis();
if (remainingTime <= 0) {
@@ -223,7 +199,7 @@
}
// read from parsing process
- ssize_t amt = ::read(fromFd, buf + mCurrentWritten, BUFFER_SIZE - mCurrentWritten);
+ ssize_t amt = ::read(fromFd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
if (amt < 0) {
if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
return -errno;
@@ -231,7 +207,7 @@
} else if (amt == 0) {
break;
} else {
- mCurrentWritten += amt;
+ mBuffer.wp()->move(amt);
}
}
@@ -242,105 +218,11 @@
size_t
FdBuffer::size() const
{
- if (mBuffers.empty()) return 0;
- return ((mBuffers.size() - 1) * BUFFER_SIZE) + mCurrentWritten;
+ return mBuffer.size();
}
-status_t
-FdBuffer::flush(int fd) const
+EncodedBuffer::iterator
+FdBuffer::data() const
{
- size_t i=0;
- status_t err = NO_ERROR;
- for (i=0; i<mBuffers.size()-1; i++) {
- err = write_all(fd, mBuffers[i], BUFFER_SIZE);
- if (err != NO_ERROR) return err;
- }
- return write_all(fd, mBuffers[i], mCurrentWritten);
-}
-
-FdBuffer::iterator
-FdBuffer::begin() const
-{
- return iterator(*this, 0, 0);
-}
-
-FdBuffer::iterator
-FdBuffer::end() const
-{
- if (mBuffers.empty() || mCurrentWritten < 0) return begin();
- if (mCurrentWritten == BUFFER_SIZE)
- // FdBuffer doesn't allocate another buf since no more bytes to read.
- return FdBuffer::iterator(*this, mBuffers.size(), 0);
- return FdBuffer::iterator(*this, mBuffers.size() - 1, mCurrentWritten);
-}
-
-// ===============================================================================
-FdBuffer::iterator::iterator(const FdBuffer& buffer, ssize_t index, ssize_t offset)
- : mFdBuffer(buffer),
- mIndex(index),
- mOffset(offset)
-{
-}
-
-FdBuffer::iterator&
-FdBuffer::iterator::operator=(iterator& other) const { return other; }
-
-FdBuffer::iterator&
-FdBuffer::iterator::operator+(size_t offset)
-{
- size_t newOffset = mOffset + offset;
- while (newOffset >= BUFFER_SIZE) {
- mIndex++;
- newOffset -= BUFFER_SIZE;
- }
- mOffset = newOffset;
- return *this;
-}
-
-FdBuffer::iterator&
-FdBuffer::iterator::operator+=(size_t offset) { return *this + offset; }
-
-FdBuffer::iterator&
-FdBuffer::iterator::operator++() { return *this + 1; }
-
-FdBuffer::iterator
-FdBuffer::iterator::operator++(int) { return *this + 1; }
-
-bool
-FdBuffer::iterator::operator==(iterator other) const
-{
- return mIndex == other.mIndex && mOffset == other.mOffset;
-}
-
-bool
-FdBuffer::iterator::operator!=(iterator other) const { return !(*this == other); }
-
-int
-FdBuffer::iterator::operator-(iterator other) const
-{
- return (int)bytesRead() - (int)other.bytesRead();
-}
-
-FdBuffer::iterator::reference
-FdBuffer::iterator::operator*() const
-{
- return mFdBuffer.mBuffers[mIndex][mOffset];
-}
-
-FdBuffer::iterator
-FdBuffer::iterator::snapshot() const
-{
- return FdBuffer::iterator(mFdBuffer, mIndex, mOffset);
-}
-
-size_t
-FdBuffer::iterator::bytesRead() const
-{
- return mIndex * BUFFER_SIZE + mOffset;
-}
-
-bool
-FdBuffer::iterator::outOfBound() const
-{
- return bytesRead() > mFdBuffer.size();
+ return mBuffer.begin();
}
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index dfe39c6..8857ae7 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -17,11 +17,11 @@
#ifndef FD_BUFFER_H
#define FD_BUFFER_H
+#include <android/util/EncodedBuffer.h>
#include <utils/Errors.h>
-#include <vector>
-
using namespace android;
+using namespace android::util;
using namespace std;
/**
@@ -71,52 +71,19 @@
size_t size() const;
/**
- * Flush all the data to given file descriptor;
- */
- status_t flush(int fd) const;
-
- /**
* How long the read took in milliseconds.
*/
int64_t durationMs() const { return mFinishTime - mStartTime; }
/**
- * Read data stored in FdBuffer
+ * Reader API for data stored in FdBuffer
*/
- class iterator;
- friend class iterator;
- class iterator : public std::iterator<std::random_access_iterator_tag, uint8_t> {
- public:
- iterator(const FdBuffer& buffer, ssize_t index, ssize_t offset);
- iterator& operator=(iterator& other) const;
- iterator& operator+(size_t offset);
- iterator& operator+=(size_t offset);
- iterator& operator++();
- iterator operator++(int);
- bool operator==(iterator other) const;
- bool operator!=(iterator other) const;
- int operator-(iterator other) const;
- reference operator*() const;
-
- // return the snapshot of the current iterator
- iterator snapshot() const;
- // how many bytes are read
- size_t bytesRead() const;
- // random access could make the iterator out of bound
- bool outOfBound() const;
- private:
- const FdBuffer& mFdBuffer;
- size_t mIndex;
- size_t mOffset;
- };
- iterator begin() const;
- iterator end() const;
+ EncodedBuffer::iterator data() const;
private:
- vector<uint8_t*> mBuffers;
+ EncodedBuffer mBuffer;
int64_t mStartTime;
int64_t mFinishTime;
- ssize_t mCurrentWritten;
bool mTimedOut;
bool mTruncated;
};
diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp
new file mode 100644
index 0000000..07a064cf
--- /dev/null
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+
+
+#include "PrivacyBuffer.h"
+#include "io_util.h"
+
+#include <android/util/protobuf.h>
+#include <deque>
+
+using namespace android::util;
+
+/**
+ * Write the field to buf based on the wire type, iterator will point to next field.
+ * If skip is set to true, no data will be written to buf. Return number of bytes written.
+ */
+static size_t
+write_field_or_skip(EncodedBuffer::iterator* iter, EncodedBuffer* buf, uint8_t wireType, bool skip)
+{
+ EncodedBuffer::Pointer snapshot = iter->rp()->copy();
+ size_t bytesToWrite = 0;
+ uint32_t varint = 0;
+ switch (wireType) {
+ case WIRE_TYPE_VARINT:
+ varint = iter->readRawVarint();
+ if(!skip) return buf->writeRawVarint(varint);
+ break;
+ case WIRE_TYPE_FIXED64:
+ bytesToWrite = 8;
+ break;
+ case WIRE_TYPE_LENGTH_DELIMITED:
+ bytesToWrite = iter->readRawVarint();
+ if(!skip) buf->writeRawVarint(bytesToWrite);
+ break;
+ case WIRE_TYPE_FIXED32:
+ bytesToWrite = 4;
+ break;
+ }
+ if (skip) {
+ iter->rp()->move(bytesToWrite);
+ } else {
+ for (size_t i=0; i<bytesToWrite; i++) {
+ *buf->writeBuffer() = iter->next();
+ buf->wp()->move();
+ }
+ }
+ return skip ? 0 : iter->rp()->pos() - snapshot.pos();
+}
+
+/**
+ * Strip next field based on its private policy and request spec, then stores data in buf.
+ * Return NO_ERROR if succeeds, otherwise BAD_VALUE is returned to indicate bad data in FdBuffer.
+ *
+ * The iterator must point to the head of a protobuf formatted field for successful operation.
+ * After exit with NO_ERROR, iterator points to the next protobuf field's head.
+ */
+static status_t
+stripField(EncodedBuffer::iterator* iter, EncodedBuffer* buf, const Privacy* parentPolicy, const PrivacySpec& spec)
+{
+ if (!iter->hasNext() || parentPolicy == NULL) return BAD_VALUE;
+ uint32_t varint = iter->readRawVarint();
+ uint8_t wireType = read_wire_type(varint);
+ uint32_t fieldId = read_field_id(varint);
+ const Privacy* policy = parentPolicy->lookup(fieldId);
+
+ if (policy == NULL || !policy->IsMessageType() || !policy->HasChildren()) {
+ bool skip = !spec.CheckPremission(policy);
+ size_t amt = buf->size();
+ if (!skip) amt += buf->writeHeader(fieldId, wireType);
+ amt += write_field_or_skip(iter, buf, wireType, skip); // point to head of next field
+ return buf->size() != amt ? BAD_VALUE : NO_ERROR;
+ }
+ // current field is message type and its sub-fields have extra privacy policies
+ deque<EncodedBuffer*> q;
+ uint32_t msgSize = iter->readRawVarint();
+ size_t finalSize = 0;
+ EncodedBuffer::Pointer start = iter->rp()->copy();
+ while (iter->rp()->pos() - start.pos() != msgSize) {
+ EncodedBuffer* v = new EncodedBuffer();
+ status_t err = stripField(iter, v, policy, spec);
+ if (err != NO_ERROR) return err;
+ if (v->size() == 0) continue;
+ q.push_back(v);
+ finalSize += v->size();
+ }
+
+ buf->writeHeader(fieldId, wireType);
+ buf->writeRawVarint(finalSize);
+ while (!q.empty()) {
+ EncodedBuffer* subField = q.front();
+ EncodedBuffer::iterator it = subField->begin();
+ while (it.hasNext()) {
+ *buf->writeBuffer() = it.next();
+ buf->wp()->move();
+ }
+ q.pop_front();
+ delete subField;
+ }
+ return NO_ERROR;
+}
+
+// ================================================================================
+PrivacyBuffer::PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator& data)
+ :mPolicy(policy),
+ mData(data),
+ mBuffer(0),
+ mSize(0)
+{
+}
+
+PrivacyBuffer::~PrivacyBuffer()
+{
+}
+
+status_t
+PrivacyBuffer::strip(const PrivacySpec& spec)
+{
+ // optimization when no strip happens
+ if (mPolicy == NULL || !mPolicy->HasChildren() || spec.RequireAll()) {
+ if (spec.CheckPremission(mPolicy)) mSize = mData.size();
+ return NO_ERROR;
+ }
+ while (mData.hasNext()) {
+ status_t err = stripField(&mData, &mBuffer, mPolicy, spec);
+ if (err != NO_ERROR) return err;
+ }
+ if (mData.bytesRead() != mData.size()) return BAD_VALUE;
+ mSize = mBuffer.size();
+ mData.rp()->rewind(); // rewind the read pointer back to beginning after the strip.
+ return NO_ERROR;
+}
+
+void
+PrivacyBuffer::clear()
+{
+ mSize = 0;
+ mBuffer.wp()->rewind();
+}
+
+size_t
+PrivacyBuffer::size() const { return mSize; }
+
+status_t
+PrivacyBuffer::flush(int fd)
+{
+ status_t err = NO_ERROR;
+ EncodedBuffer::iterator iter = size() == mData.size() ? mData : mBuffer.begin();
+ while (iter.readBuffer() != NULL) {
+ err = write_all(fd, iter.readBuffer(), iter.currentToRead());
+ iter.rp()->move(iter.currentToRead());
+ if (err != NO_ERROR) return err;
+ }
+ return NO_ERROR;
+}
diff --git a/cmds/incidentd/src/EncodedBuffer.h b/cmds/incidentd/src/PrivacyBuffer.h
similarity index 69%
rename from cmds/incidentd/src/EncodedBuffer.h
rename to cmds/incidentd/src/PrivacyBuffer.h
index ea8603a..720b38e 100644
--- a/cmds/incidentd/src/EncodedBuffer.h
+++ b/cmds/incidentd/src/PrivacyBuffer.h
@@ -14,25 +14,27 @@
* limitations under the License.
*/
-#ifndef ENCODED_BUFFER_H
-#define ENCODED_BUFFER_H
+#ifndef PRIVACY_BUFFER_H
+#define PRIVACY_BUFFER_H
-#include "FdBuffer.h"
#include "Privacy.h"
+#include <android/util/EncodedBuffer.h>
#include <stdint.h>
-#include <vector>
+#include <utils/Errors.h>
+
+using namespace android;
+using namespace android::util;
/**
- * EncodedBuffer is constructed from FdBuffer which holds original protobuf formatted data and
- * its privacy policy in its tagged proto message. The class strips PII-sensitive fields
- * based on the request and holds stripped data in its buffer for output.
+ * PrivacyBuffer holds the original protobuf data and strips PII-sensitive fields
+ * based on the request and holds stripped data in its own buffer for output.
*/
-class EncodedBuffer
+class PrivacyBuffer
{
public:
- EncodedBuffer(const FdBuffer& buffer, const Privacy* policy);
- ~EncodedBuffer();
+ PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator& data);
+ ~PrivacyBuffer();
/**
* Strip based on the request and hold data in its own buffer. Return NO_ERROR if strip succeeds.
@@ -55,10 +57,11 @@
status_t flush(int fd);
private:
- const FdBuffer& mFdBuffer;
const Privacy* mPolicy;
- vector<vector<uint8_t>> mBuffers;
+ EncodedBuffer::iterator& mData;
+
+ EncodedBuffer mBuffer;
size_t mSize;
};
-#endif // ENCODED_BUFFER_H
\ No newline at end of file
+#endif // PRIVACY_BUFFER_H
\ No newline at end of file
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index 11347e2..917b70d 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -179,8 +179,8 @@
// Execute - go get the data and write it into the file descriptors.
err = (*section)->Execute(&batch);
if (err != NO_ERROR) {
- ALOGW("Incident section %s (%d) failed. Stopping report.",
- (*section)->name.string(), id);
+ ALOGW("Incident section %s (%d) failed: %s. Stopping report.",
+ (*section)->name.string(), id, strerror(-err));
goto DONE;
}
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 166fef0..892bcca 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -16,15 +16,15 @@
#define LOG_TAG "incidentd"
-#include "EncodedBuffer.h"
#include "FdBuffer.h"
#include "Privacy.h"
+#include "PrivacyBuffer.h"
#include "Section.h"
#include "io_util.h"
-#include "protobuf.h"
#include "section_list.h"
+#include <android/util/protobuf.h>
#include <private/android_filesystem_config.h>
#include <binder/IServiceManager.h>
#include <map>
@@ -32,8 +32,13 @@
#include <wait.h>
#include <unistd.h>
+using namespace android::util;
using namespace std;
+// special section ids
+const int FIELD_ID_INCIDENT_HEADER = 1;
+
+// incident section parameters
const int WAIT_MAX = 5;
const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};
const char* INCIDENT_HELPER = "/system/bin/incident_helper";
@@ -127,7 +132,8 @@
write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* requests)
{
status_t err = -EBADF;
- EncodedBuffer encodedBuffer(buffer, get_privacy_of_section(id));
+ EncodedBuffer::iterator data = buffer.data();
+ PrivacyBuffer privacyBuffer(get_privacy_of_section(id), data);
int writeable = 0;
// The streaming ones, group requests by spec in order to save unnecessary strip operations
@@ -143,34 +149,34 @@
for (map<PrivacySpec, vector<sp<ReportRequest>>>::iterator mit = requestsBySpec.begin(); mit != requestsBySpec.end(); mit++) {
PrivacySpec spec = mit->first;
- err = encodedBuffer.strip(spec);
- if (err != NO_ERROR) return err; // it means the encodedBuffer data is corrupted.
- if (encodedBuffer.size() == 0) continue;
+ err = privacyBuffer.strip(spec);
+ if (err != NO_ERROR) return err; // it means the privacyBuffer data is corrupted.
+ if (privacyBuffer.size() == 0) continue;
for (vector<sp<ReportRequest>>::iterator it = mit->second.begin(); it != mit->second.end(); it++) {
sp<ReportRequest> request = *it;
- err = write_section_header(request->fd, id, encodedBuffer.size());
+ err = write_section_header(request->fd, id, privacyBuffer.size());
if (err != NO_ERROR) { request->err = err; continue; }
- err = encodedBuffer.flush(request->fd);
+ err = privacyBuffer.flush(request->fd);
if (err != NO_ERROR) { request->err = err; continue; }
writeable++;
- ALOGD("Section %d flushed %zu bytes to fd %d with spec %d", id, encodedBuffer.size(), request->fd, spec.dest);
+ ALOGD("Section %d flushed %zu bytes to fd %d with spec %d", id, privacyBuffer.size(), request->fd, spec.dest);
}
- encodedBuffer.clear();
+ privacyBuffer.clear();
}
// The dropbox file
if (requests->mainFd() >= 0) {
- err = encodedBuffer.strip(get_default_dropbox_spec());
+ err = privacyBuffer.strip(get_default_dropbox_spec());
if (err != NO_ERROR) return err; // the buffer data is corrupted.
- if (encodedBuffer.size() == 0) goto DONE;
+ if (privacyBuffer.size() == 0) goto DONE;
- err = write_section_header(requests->mainFd(), id, encodedBuffer.size());
+ err = write_section_header(requests->mainFd(), id, privacyBuffer.size());
if (err != NO_ERROR) { requests->setMainFd(-1); goto DONE; }
- err = encodedBuffer.flush(requests->mainFd());
+ err = privacyBuffer.flush(requests->mainFd());
if (err != NO_ERROR) { requests->setMainFd(-1); goto DONE; }
writeable++;
- ALOGD("Section %d flushed %zu bytes to dropbox %d", id, encodedBuffer.size(), requests->mainFd());
+ ALOGD("Section %d flushed %zu bytes to dropbox %d", id, privacyBuffer.size(), requests->mainFd());
}
DONE:
diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp
index d1436b2..2afa778 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -49,12 +49,11 @@
void AssertBufferContent(const char* expected) {
int i=0;
- FdBuffer::iterator it = buffer.begin();
- while (expected[i] != '\0') {
- ASSERT_EQ(*it, expected[i++]);
- it++;
+ EncodedBuffer::iterator it = buffer.data();
+ while (it.hasNext()) {
+ ASSERT_EQ(it.next(), expected[i++]);
}
- ASSERT_EQ(it, buffer.end());
+ EXPECT_EQ(expected[i], '\0');
}
bool DoDataStream(int rFd, int wFd) {
@@ -92,20 +91,8 @@
}
TEST_F(FdBufferTest, IterateEmpty) {
- FdBuffer::iterator it = buffer.begin();
- EXPECT_EQ(it, buffer.end());
- it += 1;
- EXPECT_TRUE(it.outOfBound());
-}
-
-TEST_F(FdBufferTest, IteratorSnapshot) {
- FdBuffer::iterator it = buffer.begin();
- it += 4;
- FdBuffer::iterator snapshot = it.snapshot();
- it += 5;
- EXPECT_TRUE(snapshot != it);
- EXPECT_EQ(it - snapshot, 5);
- EXPECT_EQ(snapshot - it, -5);
+ EncodedBuffer::iterator it = buffer.data();
+ EXPECT_FALSE(it.hasNext());
}
TEST_F(FdBufferTest, ReadAndIterate) {
@@ -114,15 +101,15 @@
ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
int i=0;
- for (FdBuffer::iterator it = buffer.begin(); it != buffer.end(); ++it) {
- EXPECT_EQ(*it, (uint8_t)testdata[i++]);
+ EncodedBuffer::iterator it = buffer.data();
+ while (it.hasNext()) {
+ EXPECT_EQ(it.next(), (uint8_t)testdata[i++]);
}
- FdBuffer::iterator it = buffer.begin();
- it += buffer.size();
- EXPECT_EQ(it, buffer.end());
+ it.rp()->rewind();
+ it.rp()->move(buffer.size());
EXPECT_EQ(it.bytesRead(), testdata.size());
- EXPECT_FALSE(it.outOfBound());
+ EXPECT_FALSE(it.hasNext());
}
TEST_F(FdBufferTest, ReadTimeout) {
@@ -258,13 +245,15 @@
EXPECT_FALSE(buffer.timedOut());
EXPECT_TRUE(buffer.truncated());
wait(&pid);
- FdBuffer::iterator it = buffer.begin();
- it += fourMB;
+ EncodedBuffer::iterator it = buffer.data();
+ it.rp()->move(fourMB);
EXPECT_EQ(it.bytesRead(), fourMB);
- EXPECT_EQ(it, buffer.end());
- for (FdBuffer::iterator it = buffer.begin(); it != buffer.end(); it++) {
+ EXPECT_FALSE(it.hasNext());
+
+ it.rp()->rewind();
+ while (it.hasNext()) {
char c = 'A' + (it.bytesRead() % 64 / 8);
- ASSERT_TRUE(*it == c);
+ ASSERT_TRUE(it.next() == c);
}
}
}
diff --git a/cmds/incidentd/tests/EncodedBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
similarity index 77%
rename from cmds/incidentd/tests/EncodedBuffer_test.cpp
rename to cmds/incidentd/tests/PrivacyBuffer_test.cpp
index 37a938a..8f6e355 100644
--- a/cmds/incidentd/tests/EncodedBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "EncodedBuffer.h"
+#include "FdBuffer.h"
+#include "PrivacyBuffer.h"
#include <android-base/file.h>
#include <android-base/test_utils.h>
@@ -42,9 +43,10 @@
const string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff"; // -1
const string MESSAGE_FIELD_5 = "\x2a\x10" + VARINT_FIELD_1 + STRING_FIELD_2;
-class EncodedBufferTest : public Test {
+
+class PrivacyBufferTest : public Test {
public:
- virtual ~EncodedBufferTest() {
+ virtual ~PrivacyBufferTest() {
// Delete in reverse order of construction, to be consistent with
// regular allocation/deallocation.
while (!privacies.empty()) {
@@ -60,9 +62,10 @@
void writeToFdBuffer(string str) {
ASSERT_TRUE(WriteStringToFile(str, tf.path, false));
ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, 10000));
+ ASSERT_EQ(str.size(), buffer.size());
}
- void assertBuffer(EncodedBuffer& buf, string expected) {
+ void assertBuffer(PrivacyBuffer& buf, string expected) {
ASSERT_EQ(buf.size(), expected.size());
CaptureStdout();
ASSERT_EQ(buf.flush(STDOUT_FILENO), NO_ERROR);
@@ -71,9 +74,10 @@
void assertStrip(uint8_t dest, string expected, Privacy* policy) {
PrivacySpec spec(dest);
- EncodedBuffer encodedBuf(buffer, policy);
- ASSERT_EQ(encodedBuf.strip(spec), NO_ERROR);
- assertBuffer(encodedBuf, expected);
+ EncodedBuffer::iterator bufData = buffer.data();
+ PrivacyBuffer privacyBuf(policy, bufData);
+ ASSERT_EQ(privacyBuf.strip(spec), NO_ERROR);
+ assertBuffer(privacyBuf, expected);
}
void assertStripByFields(uint8_t dest, string expected, int size, Privacy* privacy, ...) {
@@ -134,67 +138,67 @@
}
};
-TEST_F(EncodedBufferTest, NullFieldPolicy) {
+TEST_F(PrivacyBufferTest, NullFieldPolicy) {
writeToFdBuffer(STRING_FIELD_0);
assertStrip(EXPLICIT, STRING_FIELD_0, create_string_privacy(300, AUTOMATIC, NULL));
}
-TEST_F(EncodedBufferTest, StripSpecNotAllowed) {
+TEST_F(PrivacyBufferTest, StripSpecNotAllowed) {
writeToFdBuffer(STRING_FIELD_0);
assertStripByFields(AUTOMATIC, "", 1, create_privacy(0, STRING_TYPE, EXPLICIT));
}
-TEST_F(EncodedBufferTest, StripVarintField) {
+TEST_F(PrivacyBufferTest, StripVarintField) {
writeToFdBuffer(VARINT_FIELD_1);
assertStripByFields(EXPLICIT, "", 1, create_privacy(1, OTHER_TYPE, LOCAL));
}
-TEST_F(EncodedBufferTest, StripLengthDelimitedField_String) {
+TEST_F(PrivacyBufferTest, StripLengthDelimitedField_String) {
writeToFdBuffer(STRING_FIELD_2);
assertStripByFields(EXPLICIT, "", 1, create_privacy(2, STRING_TYPE, LOCAL));
}
-TEST_F(EncodedBufferTest, StripFixed64Field) {
+TEST_F(PrivacyBufferTest, StripFixed64Field) {
writeToFdBuffer(FIX64_FIELD_3);
assertStripByFields(EXPLICIT, "", 1, create_privacy(3, OTHER_TYPE, LOCAL));
}
-TEST_F(EncodedBufferTest, StripFixed32Field) {
+TEST_F(PrivacyBufferTest, StripFixed32Field) {
writeToFdBuffer(FIX32_FIELD_4);
assertStripByFields(EXPLICIT, "", 1, create_privacy(4, OTHER_TYPE, LOCAL));
}
-TEST_F(EncodedBufferTest, StripLengthDelimitedField_Message) {
+TEST_F(PrivacyBufferTest, StripLengthDelimitedField_Message) {
writeToFdBuffer(MESSAGE_FIELD_5);
assertStripByFields(EXPLICIT, "", 1, create_privacy(5, MESSAGE_TYPE, LOCAL));
}
-TEST_F(EncodedBufferTest, NoStripVarintField) {
+TEST_F(PrivacyBufferTest, NoStripVarintField) {
writeToFdBuffer(VARINT_FIELD_1);
assertStripByFields(EXPLICIT, VARINT_FIELD_1, 1, create_privacy(1, OTHER_TYPE, AUTOMATIC));
}
-TEST_F(EncodedBufferTest, NoStripLengthDelimitedField_String) {
+TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_String) {
writeToFdBuffer(STRING_FIELD_2);
assertStripByFields(EXPLICIT, STRING_FIELD_2, 1, create_privacy(2, STRING_TYPE, AUTOMATIC));
}
-TEST_F(EncodedBufferTest, NoStripFixed64Field) {
+TEST_F(PrivacyBufferTest, NoStripFixed64Field) {
writeToFdBuffer(FIX64_FIELD_3);
assertStripByFields(EXPLICIT, FIX64_FIELD_3, 1, create_privacy(3, OTHER_TYPE, AUTOMATIC));
}
-TEST_F(EncodedBufferTest, NoStripFixed32Field) {
+TEST_F(PrivacyBufferTest, NoStripFixed32Field) {
writeToFdBuffer(FIX32_FIELD_4);
assertStripByFields(EXPLICIT, FIX32_FIELD_4, 1, create_privacy(4, OTHER_TYPE, AUTOMATIC));
}
-TEST_F(EncodedBufferTest, NoStripLengthDelimitedField_Message) {
+TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_Message) {
writeToFdBuffer(MESSAGE_FIELD_5);
assertStripByFields(EXPLICIT, MESSAGE_FIELD_5, 1, create_privacy(5, MESSAGE_TYPE, AUTOMATIC));
}
-TEST_F(EncodedBufferTest, StripVarintAndString) {
+TEST_F(PrivacyBufferTest, StripVarintAndString) {
writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2
+ FIX64_FIELD_3 + FIX32_FIELD_4);
string expected = STRING_FIELD_0 + FIX64_FIELD_3 + FIX32_FIELD_4;
@@ -202,7 +206,7 @@
create_privacy(1, OTHER_TYPE, LOCAL), create_privacy(2, STRING_TYPE, LOCAL));
}
-TEST_F(EncodedBufferTest, StripVarintAndFixed64) {
+TEST_F(PrivacyBufferTest, StripVarintAndFixed64) {
writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2
+ FIX64_FIELD_3 + FIX32_FIELD_4);
string expected = STRING_FIELD_0 + STRING_FIELD_2 + FIX32_FIELD_4;
@@ -210,46 +214,49 @@
create_privacy(1, OTHER_TYPE, LOCAL), create_privacy(3, OTHER_TYPE, LOCAL));
}
-TEST_F(EncodedBufferTest, StripVarintInNestedMessage) {
+TEST_F(PrivacyBufferTest, StripVarintInNestedMessage) {
writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5);
Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
assertStripByFields(EXPLICIT, expected, 1, create_message_privacy(5, list));
}
-TEST_F(EncodedBufferTest, StripFix64AndVarintInNestedMessage) {
+TEST_F(PrivacyBufferTest, StripFix64AndVarintInNestedMessage) {
writeToFdBuffer(STRING_FIELD_0 + FIX64_FIELD_3 + MESSAGE_FIELD_5);
Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
assertStripByFields(EXPLICIT, expected, 2, create_privacy(3, OTHER_TYPE, LOCAL), create_message_privacy(5, list));
}
-TEST_F(EncodedBufferTest, ClearAndStrip) {
+TEST_F(PrivacyBufferTest, ClearAndStrip) {
string data = STRING_FIELD_0 + VARINT_FIELD_1;
writeToFdBuffer(data);
Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
- EncodedBuffer encodedBuf(buffer, create_message_privacy(300, list));
+ EncodedBuffer::iterator bufData = buffer.data();
+ PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData);
PrivacySpec spec1(EXPLICIT), spec2(LOCAL);
- ASSERT_EQ(encodedBuf.strip(spec1), NO_ERROR);
- assertBuffer(encodedBuf, STRING_FIELD_0);
- ASSERT_EQ(encodedBuf.strip(spec2), NO_ERROR);
- assertBuffer(encodedBuf, data);
+ ASSERT_EQ(privacyBuf.strip(spec1), NO_ERROR);
+ assertBuffer(privacyBuf, STRING_FIELD_0);
+ ASSERT_EQ(privacyBuf.strip(spec2), NO_ERROR);
+ assertBuffer(privacyBuf, data);
}
-TEST_F(EncodedBufferTest, BadDataInFdBuffer) {
+TEST_F(PrivacyBufferTest, BadDataInFdBuffer) {
writeToFdBuffer("iambaddata");
Privacy* list[] = { create_privacy(4, OTHER_TYPE, AUTOMATIC), NULL };
- EncodedBuffer encodedBuf(buffer, create_message_privacy(300, list));
+ EncodedBuffer::iterator bufData = buffer.data();
+ PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData);
PrivacySpec spec;
- ASSERT_EQ(encodedBuf.strip(spec), BAD_VALUE);
+ ASSERT_EQ(privacyBuf.strip(spec), BAD_VALUE);
}
-TEST_F(EncodedBufferTest, BadDataInNestedMessage) {
+TEST_F(PrivacyBufferTest, BadDataInNestedMessage) {
writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5 + "aoeoe");
Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
Privacy* field5[] = { create_message_privacy(5, list), NULL };
- EncodedBuffer encodedBuf(buffer, create_message_privacy(300, field5));
+ EncodedBuffer::iterator bufData = buffer.data();
+ PrivacyBuffer privacyBuf(create_message_privacy(300, field5), bufData);
PrivacySpec spec;
- ASSERT_EQ(encodedBuf.strip(spec), BAD_VALUE);
+ ASSERT_EQ(privacyBuf.strip(spec), BAD_VALUE);
}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 79faa1b..c5c38f5 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -416,7 +416,7 @@
PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
null, null);
params.sessionParams.setSize(
- PackageHelper.calculateInstalledSize(pkgLite, false,
+ PackageHelper.calculateInstalledSize(pkgLite,
params.sessionParams.abiOverride));
} catch (PackageParserException | IOException e) {
System.err.println("Error: Failed to parse APK file: " + e);
diff --git a/cmds/screencap/Android.mk b/cmds/screencap/Android.mk
index b0dc422..72e3c56 100644
--- a/cmds/screencap/Android.mk
+++ b/cmds/screencap/Android.mk
@@ -8,7 +8,7 @@
libcutils \
libutils \
libbinder \
- libskia \
+ libhwui \
libui \
libgui
diff --git a/cmds/statsd/.clang-format b/cmds/statsd/.clang-format
new file mode 100644
index 0000000..3d64bee
--- /dev/null
+++ b/cmds/statsd/.clang-format
@@ -0,0 +1,14 @@
+BasedOnStyle: Google
+AllowShortIfStatementsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: false
+AllowShortLoopsOnASingleLine: true
+BinPackArguments: true
+BinPackParameters: true
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 8
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+AccessModifierOffset: -4
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index b9ee7ff..56f9512 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -44,6 +44,7 @@
../../core/java/android/os/IStatsManager.aidl \
src/StatsService.cpp \
src/AnomalyMonitor.cpp \
+ src/StatsPuller.cpp \
src/LogEntryPrinter.cpp \
src/LogReader.cpp \
src/main.cpp \
@@ -52,8 +53,11 @@
src/StatsLogProcessor.cpp \
src/stats_log.proto \
src/statsd_config.proto \
- src/stats_constants.proto \
src/DropboxReader.cpp \
+ src/matchers/LogEntryMatcherManager.cpp \
+ src/metrics/CountMetricProducer.cpp \
+ src/metrics/ConditionTracker.cpp \
+ src/metrics/MetricsManager.cpp \
LOCAL_CFLAGS += \
@@ -107,6 +111,9 @@
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_MODULE_TAGS := tests
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/src \
+ STATSD_PROTO_INCLUDES
+
LOCAL_CFLAGS += \
-Wall \
-Werror \
@@ -115,21 +122,23 @@
-Wno-unused-function \
-Wno-unused-parameter
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src \
- STATSD_PROTO_INCLUDES
-
LOCAL_SRC_FILES := \
+ src/stats_log.proto \
+ src/statsd_config.proto \
../../core/java/android/os/IStatsCompanionService.aidl \
../../core/java/android/os/IStatsManager.aidl \
src/StatsService.cpp \
tests/indexed_priority_queue_test.cpp \
+ src/parse_util.cpp \
src/LogEntryPrinter.cpp \
src/LogReader.cpp \
+ src/matchers/LogEntryMatcherManager.cpp \
tests/LogReader_test.cpp \
+ tests/LogEntryMatcher_test.cpp \
LOCAL_STATIC_LIBRARIES := \
libgmock \
- statsd_proto
+ statsd_proto \
LOCAL_SHARED_LIBRARIES := \
libbase \
diff --git a/cmds/statsd/src/AnomalyMonitor.cpp b/cmds/statsd/src/AnomalyMonitor.cpp
index 2d3454a..92fe844 100644
--- a/cmds/statsd/src/AnomalyMonitor.cpp
+++ b/cmds/statsd/src/AnomalyMonitor.cpp
@@ -26,8 +26,7 @@
namespace statsd {
AnomalyMonitor::AnomalyMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec)
- : mRegisteredAlarmTimeSec(0),
- mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec) {
+ : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec) {
}
AnomalyMonitor::~AnomalyMonitor() {
@@ -63,7 +62,7 @@
if (DEBUG) ALOGD("Adding alarm with time %u", alarm->timestampSec);
mPq.push(alarm);
if (mRegisteredAlarmTimeSec < 1 ||
- alarm->timestampSec + mMinUpdateTimeSec < mRegisteredAlarmTimeSec) {
+ alarm->timestampSec + mMinUpdateTimeSec < mRegisteredAlarmTimeSec) {
updateRegisteredAlarmTime_l(alarm->timestampSec);
}
}
@@ -100,9 +99,9 @@
}
int64_t AnomalyMonitor::secToMs(uint32_t timeSec) {
- return ((int64_t) timeSec) * 1000;
+ return ((int64_t)timeSec) * 1000;
}
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/AnomalyMonitor.h b/cmds/statsd/src/AnomalyMonitor.h
index e89afa8..d78be54 100644
--- a/cmds/statsd/src/AnomalyMonitor.h
+++ b/cmds/statsd/src/AnomalyMonitor.h
@@ -17,8 +17,8 @@
#ifndef ANOMALY_MONITOR_H
#define ANOMALY_MONITOR_H
-#include <indexed_priority_queue.h>
#include <android/os/IStatsCompanionService.h>
+#include <indexed_priority_queue.h>
#include <utils/RefBase.h>
#include <queue>
@@ -56,7 +56,7 @@
* Manages alarms for Anomaly Detection.
*/
class AnomalyMonitor : public RefBase {
- public:
+public:
/**
* @param minDiffToUpdateRegisteredAlarmTimeSec If the soonest alarm differs
* from the registered alarm by more than this amount, update the registered
@@ -94,7 +94,7 @@
return mRegisteredAlarmTimeSec;
}
- private:
+private:
std::mutex mLock;
/**
@@ -131,8 +131,8 @@
int64_t secToMs(uint32_t timeSec);
};
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
-#endif // ANOMALY_MONITOR_H
\ No newline at end of file
+#endif // ANOMALY_MONITOR_H
\ No newline at end of file
diff --git a/cmds/statsd/src/DropboxReader.cpp b/cmds/statsd/src/DropboxReader.cpp
index 307e771..430e7af 100644
--- a/cmds/statsd/src/DropboxReader.cpp
+++ b/cmds/statsd/src/DropboxReader.cpp
@@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include <android/os/DropBoxManager.h>
#include <android-base/file.h>
+#include <android/os/DropBoxManager.h>
#include <androidfw/ZipUtils.h>
#include "DropboxReader.h"
-using android::sp;
using android::String16;
-using android::binder::Status;
-using android::base::unique_fd;
-using android::os::DropBoxManager;
using android::ZipUtils;
+using android::base::unique_fd;
+using android::binder::Status;
+using android::os::DropBoxManager;
+using android::sp;
using std::vector;
namespace android {
@@ -37,10 +37,9 @@
long timestamp = msec;
// instead of while(true), put a hard limit 1000. Dropbox won't have more than 1000 files.
- for(int i = 0; i < 1000; i++ ) {
+ for (int i = 0; i < 1000; i++) {
DropBoxManager::Entry entry;
- Status status = dropbox->getNextEntry(String16(tag.c_str()),
- timestamp, &entry);
+ Status status = dropbox->getNextEntry(String16(tag.c_str()), timestamp, &entry);
if (!status.isOk()) {
ALOGD("No more entries, or failed to read. We can't tell unfortunately.");
return android::OK;
@@ -69,15 +68,14 @@
}
bool DropboxReader::parseFromGzipFile(const unique_fd& fd, StatsLogReport& logReport) {
- FILE *file = fdopen(fd, "r");
+ FILE* file = fdopen(fd, "r");
bool result = false;
bool scanResult;
int method;
long compressedLen;
long uncompressedLen;
unsigned long crc32;
- scanResult = ZipUtils::examineGzip(file, &method, &uncompressedLen,
- &compressedLen, &crc32);
+ scanResult = ZipUtils::examineGzip(file, &method, &uncompressedLen, &compressedLen, &crc32);
if (scanResult && method == kCompressDeflated) {
vector<uint8_t> buf(uncompressedLen);
if (ZipUtils::inflateToBuffer(file, &buf[0], uncompressedLen, compressedLen)) {
@@ -107,20 +105,16 @@
}
void DropboxReader::printLog(FILE* out, const StatsLogReport& logReport) {
- fprintf(out, "start_time_msec=%lld, end_time_msec=%lld, ",
- logReport.start_report_millis(), logReport.end_report_millis());
+ fprintf(out, "start_time_ns=%lld, end_time_ns=%lld, ", logReport.start_report_nanos(),
+ logReport.end_report_nanos());
for (int i = 0; i < logReport.event_metrics().data_size(); i++) {
EventMetricData eventMetricData = logReport.event_metrics().data(i);
- for (int j = 0; j < eventMetricData.key_value_pair_size(); j++) {
- fprintf(out, "key=%d, ", eventMetricData.key_value_pair(j).key());
- fprintf(out, "value_str=%s ", eventMetricData.key_value_pair(j).value_str().c_str());
- fprintf(out, "value_int=%lld ", eventMetricData.key_value_pair(j).value_int());
- fprintf(out, "value_float=%f ", eventMetricData.key_value_pair(j).value_float());
- }
+ // TODO: Pretty-print the proto.
+ // fprintf(out, "EventMetricData=%s", eventMetricData.SerializeAsString().c_str());
}
fprintf(out, "\n");
}
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/DropboxReader.h b/cmds/statsd/src/DropboxReader.h
index b7f8739..a5a28d9 100644
--- a/cmds/statsd/src/DropboxReader.h
+++ b/cmds/statsd/src/DropboxReader.h
@@ -40,13 +40,13 @@
static bool parseFromGzipFile(const unique_fd& fd, StatsLogReport& logReport);
static void printLog(FILE* out, const StatsLogReport& logReport);
enum {
- kCompressStored = 0, // no compression
- kCompressDeflated = 8, // standard deflate
+ kCompressStored = 0, // no compression
+ kCompressDeflated = 8, // standard deflate
};
};
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
-#endif //DROPBOX_READER_H
+#endif // DROPBOX_READER_H
diff --git a/cmds/statsd/src/DropboxWriter.cpp b/cmds/statsd/src/DropboxWriter.cpp
index b9d48fa..b72e530 100644
--- a/cmds/statsd/src/DropboxWriter.cpp
+++ b/cmds/statsd/src/DropboxWriter.cpp
@@ -18,32 +18,30 @@
#include "DropboxWriter.h"
+using android::String16;
using android::binder::Status;
using android::os::DropBoxManager;
using android::sp;
-using android::String16;
using std::vector;
namespace android {
namespace os {
namespace statsd {
-DropboxWriter::DropboxWriter(const string& tag)
- : mTag(tag), mLogReport(), mBufferSize(0) {
+DropboxWriter::DropboxWriter(const string& tag) : mTag(tag), mLogReport(), mBufferSize(0) {
}
-void DropboxWriter::addStatsLogReport(const StatsLogReport& log) {
- mLogReport = log;
- flushIfNecessary(log);
- mBufferSize += log.ByteSize();
+void DropboxWriter::addEventMetricData(const EventMetricData& eventMetricData) {
+ flushIfNecessary(eventMetricData);
+ EventMetricData* newEntry = mLogReport.mutable_event_metrics()->add_data();
+ newEntry->CopyFrom(eventMetricData);
+ mBufferSize += eventMetricData.ByteSize();
}
-void DropboxWriter::flushIfNecessary(const StatsLogReport& log) {
- // TODO: Decide to flush depending on the serialized size of the StatsLogReport.
- // if (entry.ByteSize() + mBufferSize > kMaxSerializedBytes) {
- // flush();
- // }
- flush();
+void DropboxWriter::flushIfNecessary(const EventMetricData& eventMetricData) {
+ if (eventMetricData.ByteSize() + mBufferSize > kMaxSerializedBytes) {
+ flush();
+ }
}
void DropboxWriter::flush() {
@@ -52,16 +50,15 @@
vector<uint8_t> buffer(numBytes);
sp<DropBoxManager> dropbox = new DropBoxManager();
mLogReport.SerializeToArray(&buffer[0], numBytes);
- Status status = dropbox->addData(String16(mTag.c_str()), &buffer[0],
- numBytes, 0 /* no flag */);
+ Status status = dropbox->addData(String16(mTag.c_str()), &buffer[0], numBytes, 0 /* no flag */);
if (!status.isOk()) {
ALOGE("failed to write to dropbox");
- //TODO: What to do if flush fails??
+ // TODO: What to do if flush fails??
}
mLogReport.Clear();
mBufferSize = 0;
}
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/DropboxWriter.h b/cmds/statsd/src/DropboxWriter.h
index 59629fb..d72f103 100644
--- a/cmds/statsd/src/DropboxWriter.h
+++ b/cmds/statsd/src/DropboxWriter.h
@@ -17,7 +17,8 @@
#ifndef DROPBOX_WRITER_H
#define DROPBOX_WRITER_H
-#include <frameworks/base/cmds/statsd/src/stats_log.pb.h>
+#include <utils/RefBase.h>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
using std::string;
@@ -25,14 +26,14 @@
namespace os {
namespace statsd {
-class DropboxWriter {
+class DropboxWriter : public virtual RefBase {
public:
/* tag will be part of the file name, and used as the key to build the file index inside
DropBoxManagerService.
*/
DropboxWriter(const string& tag);
- void addStatsLogReport(const StatsLogReport& log);
+ void addEventMetricData(const EventMetricData& eventMetricData);
/* Request a flush to dropbox. */
void flush();
@@ -52,21 +53,20 @@
StatsLogReport mLogReport;
/* Current *serialized* size of the logs kept in memory.
- To save computation, we will not calculate the size of the StatsLogReport every time when a new
- entry is added, which would recursively call ByteSize() on every log entry. Instead, we keep
- the sum of all individual stats log entry sizes. The size of a proto is approximately the sum
- of the size of all member protos.
+ To save computation, we will not calculate the size of the StatsLogReport every time when a
+ new entry is added, which would recursively call ByteSize() on every log entry. Instead, we
+ keep the sum of all individual stats log entry sizes. The size of a proto is approximately
+ the sum of the size of all member protos.
*/
size_t mBufferSize = 0;
/* Check if the buffer size exceeds the max buffer size when the new entry is added, and flush
the logs to dropbox if true. */
- void flushIfNecessary(const StatsLogReport& log);
-
+ void flushIfNecessary(const EventMetricData& eventMetricData);
};
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
-#endif //DROPBOX_WRITER_H
+#endif // DROPBOX_WRITER_H
diff --git a/cmds/statsd/src/LogEntryPrinter.cpp b/cmds/statsd/src/LogEntryPrinter.cpp
index c877b05..63465b0 100644
--- a/cmds/statsd/src/LogEntryPrinter.cpp
+++ b/cmds/statsd/src/LogEntryPrinter.cpp
@@ -26,9 +26,7 @@
namespace os {
namespace statsd {
-LogEntryPrinter::LogEntryPrinter(int out)
- :m_out(out)
-{
+LogEntryPrinter::LogEntryPrinter(int out) : m_out(out) {
// Initialize the EventTagMap, which is how we know the names of the numeric event tags.
// If this fails, we can't print well, but something will print.
m_tags = android_openEventTagMap(NULL);
@@ -38,23 +36,20 @@
android_log_setPrintFormat(m_format, FORMAT_THREADTIME);
}
-LogEntryPrinter::~LogEntryPrinter()
-{
+LogEntryPrinter::~LogEntryPrinter() {
if (m_tags != NULL) {
android_closeEventTagMap(m_tags);
}
android_log_format_free(m_format);
}
-void
-LogEntryPrinter::OnLogEvent(const log_msg& msg)
-{
+void LogEntryPrinter::OnLogEvent(const log_msg& msg) {
status_t err;
AndroidLogEntry entry;
char buf[1024];
- err = android_log_processBinaryLogBuffer(&(const_cast<log_msg*>(&msg)->entry_v1),
- &entry, m_tags, buf, sizeof(buf));
+ err = android_log_processBinaryLogBuffer(&(const_cast<log_msg*>(&msg)->entry_v1), &entry,
+ m_tags, buf, sizeof(buf));
if (err == NO_ERROR) {
android_log_printLogLine(m_format, m_out, &entry);
} else {
@@ -63,7 +58,6 @@
}
}
-} // namespace statsd
-} // namespace os
-} // namespace android
-
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/LogEntryPrinter.h b/cmds/statsd/src/LogEntryPrinter.h
index ed720dc..4f79028 100644
--- a/cmds/statsd/src/LogEntryPrinter.h
+++ b/cmds/statsd/src/LogEntryPrinter.h
@@ -30,8 +30,7 @@
/**
* Decodes the log entry and prints it to the supplied file descriptor.
*/
-class LogEntryPrinter : public LogListener
-{
+class LogEntryPrinter : public LogListener {
public:
LogEntryPrinter(int out);
virtual ~LogEntryPrinter();
@@ -55,8 +54,8 @@
AndroidLogFormat* m_format;
};
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
-#endif // LOG_ENTRY_PRINTER_H
+#endif // LOG_ENTRY_PRINTER_H
diff --git a/cmds/statsd/src/LogReader.cpp b/cmds/statsd/src/LogReader.cpp
index c9164f9..c4ac337 100644
--- a/cmds/statsd/src/LogReader.cpp
+++ b/cmds/statsd/src/LogReader.cpp
@@ -31,37 +31,27 @@
namespace statsd {
#define SNOOZE_INITIAL_MS 100
-#define SNOOZE_MAX_MS (10 * 60 * 1000) // Ten minutes
-
+#define SNOOZE_MAX_MS (10 * 60 * 1000) // Ten minutes
// ================================================================================
-LogListener::LogListener()
-{
+LogListener::LogListener() {
}
-LogListener::~LogListener()
-{
+LogListener::~LogListener() {
}
-
// ================================================================================
-LogReader::LogReader()
-{
+LogReader::LogReader() {
}
-LogReader::~LogReader()
-{
+LogReader::~LogReader() {
}
-void
-LogReader::AddListener(const sp<LogListener>& listener)
-{
+void LogReader::AddListener(const sp<LogListener>& listener) {
m_listeners.push_back(listener);
}
-void
-LogReader::Run()
-{
+void LogReader::Run() {
int nextSnoozeMs = SNOOZE_INITIAL_MS;
// In an ideal world, this outer loop will only ever run one iteration, but it
@@ -100,9 +90,7 @@
}
}
-int
-LogReader::connect_and_read()
-{
+int LogReader::connect_and_read() {
int lineCount = 0;
status_t err;
logger_list* loggers;
@@ -110,8 +98,8 @@
// Prepare the logging context
loggers = android_logger_list_alloc(ANDROID_LOG_RDONLY,
- /* don't stop after N lines */ 0,
- /* no pid restriction */ 0);
+ /* don't stop after N lines */ 0,
+ /* no pid restriction */ 0);
// Open the buffer(s)
eventLogger = android_logger_open(loggers, LOG_ID_STATS);
@@ -133,7 +121,7 @@
// Call the listeners
for (vector<sp<LogListener> >::iterator it = m_listeners.begin();
- it != m_listeners.end(); it++) {
+ it != m_listeners.end(); it++) {
(*it)->OnLogEvent(msg);
}
}
@@ -145,6 +133,6 @@
return lineCount;
}
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/LogReader.h b/cmds/statsd/src/LogReader.h
index 4c2afe8..fc19585 100644
--- a/cmds/statsd/src/LogReader.h
+++ b/cmds/statsd/src/LogReader.h
@@ -27,10 +27,9 @@
namespace statsd {
/**
- * Callback for LogReader
+ * Callback for LogReader
*/
-class LogListener : public virtual android::RefBase
-{
+class LogListener : public virtual android::RefBase {
public:
LogListener();
virtual ~LogListener();
@@ -43,8 +42,7 @@
/**
* Class to read logs from logd.
*/
-class LogReader : public virtual android::RefBase
-{
+class LogReader : public virtual android::RefBase {
public:
/**
* Construct the LogReader with a pointer back to the StatsService
@@ -61,9 +59,9 @@
*/
void AddListener(const android::sp<LogListener>& listener);
- /**
- * Run the main LogReader loop
- */
+ /**
+ * Run the main LogReader loop
+ */
void Run();
private:
@@ -81,8 +79,8 @@
int connect_and_read();
};
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
-#endif // LOGREADER_H
+#endif // LOGREADER_H
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 1ae23ef..117fb5e 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -16,64 +16,76 @@
#include <StatsLogProcessor.h>
+#include <cutils/log.h>
+#include <frameworks/base/cmds/statsd/src/stats_log.pb.h>
#include <log/log_event_list.h>
-#include <utils/Errors.h>
+#include <metrics/CountMetricProducer.h>
#include <parse_util.h>
+#include <utils/Errors.h>
using namespace android;
+using std::make_unique;
+using std::unique_ptr;
+using std::vector;
namespace android {
namespace os {
namespace statsd {
-StatsLogProcessor::StatsLogProcessor() : m_dropbox_writer("all-logs")
-{
- // Initialize the EventTagMap, which is how we know the names of the numeric event tags.
- // If this fails, we can't print well, but something will print.
- m_tags = android_openEventTagMap(NULL);
-
- // Printing format
- m_format = android_log_format_new();
- android_log_setPrintFormat(m_format, FORMAT_THREADTIME);
+StatsLogProcessor::StatsLogProcessor() : m_dropbox_writer("all-logs") {
+ // hardcoded config
+ // this should be called from StatsService when it receives a statsd_config
+ UpdateConfig(0, buildFakeConfig());
}
-StatsLogProcessor::~StatsLogProcessor()
-{
- if (m_tags != NULL) {
- android_closeEventTagMap(m_tags);
- }
- android_log_format_free(m_format);
+StatsLogProcessor::~StatsLogProcessor() {
}
-void
-StatsLogProcessor::OnLogEvent(const log_msg& msg)
-{
- status_t err;
- AndroidLogEntry entry;
- char buf[1024];
+StatsdConfig StatsLogProcessor::buildFakeConfig() {
+ // HACK: Hard code a test metric for counting screen on events...
+ StatsdConfig config;
+ config.set_config_id(12345L);
- err = android_log_processBinaryLogBuffer(&(const_cast<log_msg*>(&msg)->entry_v1),
- &entry, m_tags, buf, sizeof(buf));
+ CountMetric* metric = config.add_count_metric();
+ metric->set_metric_id(20150717L);
+ metric->set_what("SCREEN_IS_ON");
+ metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
- // dump all statsd logs to dropbox for now.
- // TODO: Add filtering, aggregation, etc.
- if (err == NO_ERROR) {
- StatsLogReport logReport;
- logReport.set_start_report_millis(entry.tv_sec / 1000 + entry.tv_nsec / 1000 / 1000);
- EventMetricData *eventMetricData = logReport.mutable_event_metrics()->add_data();
- *eventMetricData = parse(msg);
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_ON");
- m_dropbox_writer.addStatsLogReport(logReport);
+ SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()
+ ->set_key(1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)
+ ->set_eq_int(2/*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+ return config;
+}
+
+// TODO: what if statsd service restarts? How do we know what logs are already processed before?
+void StatsLogProcessor::OnLogEvent(const log_msg& msg) {
+ // TODO: Use EventMetric to filter the events we want to log.
+ EventMetricData eventMetricData = parse(msg);
+ m_dropbox_writer.addEventMetricData(eventMetricData);
+
+ // pass the event to metrics managers.
+ for (auto& pair : mMetricsManagers) {
+ pair.second->onLogEvent(msg);
}
}
-void
-StatsLogProcessor::UpdateConfig(const int config_source, StatsdConfig config)
-{
- m_configs[config_source] = config;
+void StatsLogProcessor::UpdateConfig(const int config_source, const StatsdConfig& config) {
+ auto it = mMetricsManagers.find(config_source);
+ if (it != mMetricsManagers.end()) {
+ it->second->finish();
+ }
+
ALOGD("Updated configuration for source %i", config_source);
+
+ mMetricsManagers.insert({config_source, std::make_unique<MetricsManager>(config)});
}
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index a6d182c..88c63fa 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -16,46 +16,40 @@
#ifndef STATS_LOG_PROCESSOR_H
#define STATS_LOG_PROCESSOR_H
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "DropboxWriter.h"
+#include "LogReader.h"
+#include "metrics/MetricsManager.h"
#include "parse_util.h"
+#include <log/logprint.h>
+#include <stdio.h>
#include <unordered_map>
namespace android {
namespace os {
namespace statsd {
-class StatsLogProcessor : public LogListener
-{
+class StatsLogProcessor : public LogListener {
public:
StatsLogProcessor();
virtual ~StatsLogProcessor();
virtual void OnLogEvent(const log_msg& msg);
- virtual void UpdateConfig(const int config_source, StatsdConfig config);
+ void UpdateConfig(const int config_source, const StatsdConfig& config);
private:
- /**
- * Numeric to string tag name mapping.
- */
- EventTagMap* m_tags;
-
- /**
- * Pretty printing format.
- */
- AndroidLogFormat* m_format;
-
+ // TODO: use EventMetrics to log the events.
DropboxWriter m_dropbox_writer;
- /**
- * Configs that have been specified, keyed by the source. This allows us to over-ride the config
- * from a source later.
- */
- std::unordered_map<int, StatsdConfig> m_configs;
+ std::unordered_map<int, std::unique_ptr<MetricsManager>> mMetricsManagers;
+
+ static StatsdConfig buildFakeConfig();
};
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
-#endif //STATS_LOG_PROCESSOR_H
+#endif // STATS_LOG_PROCESSOR_H
diff --git a/cmds/statsd/src/StatsPuller.cpp b/cmds/statsd/src/StatsPuller.cpp
new file mode 100644
index 0000000..94e8361
--- /dev/null
+++ b/cmds/statsd/src/StatsPuller.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "StatsPuller"
+#define DEBUG true
+
+#include "StatsPuller.h"
+#include "StatsService.h"
+#include <android/os/IStatsCompanionService.h>
+#include <cutils/log.h>
+
+using namespace android;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+String16 StatsPuller::pull(int pullCode) {
+ if (DEBUG) ALOGD("Initiating pulling %d", pullCode);
+
+ switch (pullCode) {
+ // All stats_companion_service cases go here with fallthroughs
+ case PULL_CODE_KERNEL_WAKELOCKS: {
+ // TODO: Consider caching the statsCompanion service
+ sp <IStatsCompanionService>
+ statsCompanion = StatsService::getStatsCompanionService();
+ String16 returned_value("");
+ Status status = statsCompanion->pullData(pullCode, &returned_value);
+ if (DEBUG) ALOGD("Finished pulling the data");
+ if (!status.isOk()) {
+ ALOGW("error pulling data of type %d", pullCode);
+ }
+ return returned_value;
+ }
+
+ // case OTHER_TYPES: etc.
+
+ default: {
+ ALOGE("invalid pull code %d", pullCode);
+ return String16("");
+ }
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/StatsPuller.h b/cmds/statsd/src/StatsPuller.h
new file mode 100644
index 0000000..05343b5
--- /dev/null
+++ b/cmds/statsd/src/StatsPuller.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef STATSD_STATSPULLER_H
+#define STATSD_STATSPULLER_H
+
+#include <utils/String16.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StatsPuller {
+public:
+ // Enums of pulled data types (pullCodes)
+ // These values must be kept in sync with com/android/server/stats/StatsCompanionService.java.
+ // TODO: pull the constant from stats_events.proto instead
+ const static int PULL_CODE_KERNEL_WAKELOCKS = 20;
+
+ StatsPuller();
+ ~StatsPuller();
+
+ static String16 pull(int pullCode);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif //STATSD_STATSPULLER_H
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 965c9b7..ae7d66b 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -29,9 +29,9 @@
#include <utils/Looper.h>
#include <utils/String16.h>
-#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
+#include <unistd.h>
using namespace android;
@@ -40,17 +40,15 @@
namespace statsd {
StatsService::StatsService(const sp<Looper>& handlerLooper)
- : mAnomalyMonitor(new AnomalyMonitor(2)) // TODO: Change this based on the config
+ : mAnomalyMonitor(new AnomalyMonitor(2)) // TODO: Change this based on the config
{
ALOGD("stats service constructed");
}
-StatsService::~StatsService()
-{
+StatsService::~StatsService() {
}
-status_t
-StatsService::setProcessor(const sp<StatsLogProcessor>& main_processor) {
+status_t StatsService::setProcessor(const sp<StatsLogProcessor>& main_processor) {
m_processor = main_processor;
ALOGD("stats service set to processor %p", m_processor.get());
return NO_ERROR;
@@ -58,9 +56,8 @@
// Implement our own because the default binder implementation isn't
// properly handling SHELL_COMMAND_TRANSACTION
-status_t
-StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
+status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
status_t err;
switch (code) {
@@ -73,10 +70,9 @@
for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
args.add(String8(data.readString16()));
}
- sp<IShellCallback> shellCallback = IShellCallback::asInterface(
- data.readStrongBinder());
- sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(
- data.readStrongBinder());
+ sp<IShellCallback> shellCallback = IShellCallback::asInterface(data.readStrongBinder());
+ sp<IResultReceiver> resultReceiver =
+ IResultReceiver::asInterface(data.readStrongBinder());
FILE* fin = fdopen(in, "r");
FILE* fout = fdopen(out, "w");
@@ -104,15 +100,11 @@
return NO_ERROR;
}
- default: {
- return BnStatsManager::onTransact(code, data, reply, flags);
- }
+ default: { return BnStatsManager::onTransact(code, data, reply, flags); }
}
}
-status_t
-StatsService::dump(int fd, const Vector<String16>& args)
-{
+status_t StatsService::dump(int fd, const Vector<String16>& args) {
FILE* out = fdopen(fd, "w");
if (out == NULL) {
return NO_MEMORY; // the fd is already open
@@ -121,7 +113,7 @@
fprintf(out, "StatsService::dump:");
ALOGD("StatsService::dump:");
const int N = args.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
fprintf(out, " %s", String8(args[i]).string());
ALOGD(" %s", String8(args[i]).string());
}
@@ -131,9 +123,7 @@
return NO_ERROR;
}
-status_t
-StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& args)
-{
+status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
if (args.size() > 0) {
if (!args[0].compare(String8("print-stats-log")) && args.size() > 1) {
return doPrintStatsLog(out, args);
@@ -147,9 +137,7 @@
return NO_ERROR;
}
-status_t
-StatsService::doLoadConfig(FILE* in)
-{
+status_t StatsService::doLoadConfig(FILE* in) {
string content;
if (!android::base::ReadFdToString(fileno(in), &content)) {
return UNKNOWN_ERROR;
@@ -165,14 +153,12 @@
}
}
-Status
-StatsService::informAnomalyAlarmFired()
-{
+Status StatsService::informAnomalyAlarmFired() {
if (DEBUG) ALOGD("StatsService::informAnomalyAlarmFired was called");
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
return Status::fromExceptionCode(Status::EX_SECURITY,
- "Only system uid can call informAnomalyAlarmFired");
+ "Only system uid can call informAnomalyAlarmFired");
}
if (DEBUG) ALOGD("StatsService::informAnomalyAlarmFired succeeded");
@@ -181,28 +167,27 @@
return Status::ok();
}
-Status
-StatsService::informPollAlarmFired()
-{
+Status StatsService::informPollAlarmFired() {
if (DEBUG) ALOGD("StatsService::informPollAlarmFired was called");
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
return Status::fromExceptionCode(Status::EX_SECURITY,
- "Only system uid can call informPollAlarmFired");
+ "Only system uid can call informPollAlarmFired");
}
if (DEBUG) ALOGD("StatsService::informPollAlarmFired succeeded");
// TODO: determine what services to poll and poll (or ask StatsCompanionService to poll) them.
+ String16 output = StatsPuller::pull(StatsPuller::PULL_CODE_KERNEL_WAKELOCKS);
+ // TODO: do something useful with the output instead of writing a string to screen.
+ ALOGD("%s", String8(output).string());
return Status::ok();
}
-Status
-StatsService::systemRunning()
-{
+Status StatsService::systemRunning() {
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
return Status::fromExceptionCode(Status::EX_SECURITY,
- "Only system uid can call systemRunning");
+ "Only system uid can call systemRunning");
}
// When system_server is up and running, schedule the dropbox task to run.
@@ -213,10 +198,9 @@
return Status::ok();
}
-void
-StatsService::sayHiToStatsCompanion()
-{
- // TODO: This method needs to be private. It is temporarily public and unsecured for testing purposes.
+void StatsService::sayHiToStatsCompanion() {
+ // TODO: This method needs to be private. It is temporarily public and unsecured for testing
+ // purposes.
sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
if (statsCompanion != nullptr) {
if (DEBUG) ALOGD("Telling statsCompanion that statsd is ready");
@@ -226,8 +210,7 @@
}
}
-sp<IStatsCompanionService>
-StatsService::getStatsCompanionService() {
+sp<IStatsCompanionService> StatsService::getStatsCompanionService() {
sp<IStatsCompanionService> statsCompanion = nullptr;
// Get statscompanion service from service manager
const sp<IServiceManager> sm(defaultServiceManager());
@@ -242,9 +225,7 @@
return statsCompanion;
}
-Status
-StatsService::statsCompanionReady()
-{
+Status StatsService::statsCompanionReady() {
if (DEBUG) ALOGD("StatsService::statsCompanionReady was called");
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
@@ -254,8 +235,9 @@
sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
if (statsCompanion == nullptr) {
- return Status::fromExceptionCode(Status::EX_NULL_POINTER,
- "statscompanion unavailable despite it contacting statsd!");
+ return Status::fromExceptionCode(
+ Status::EX_NULL_POINTER,
+ "statscompanion unavailable despite it contacting statsd!");
}
if (DEBUG) ALOGD("StatsService::statsCompanionReady linking to statsCompanion.");
IInterface::asBinder(statsCompanion)->linkToDeath(new StatsdDeathRecipient(mAnomalyMonitor));
@@ -264,14 +246,12 @@
return Status::ok();
}
-void
-StatsdDeathRecipient::binderDied(const wp<IBinder>& who) {
+void StatsdDeathRecipient::binderDied(const wp<IBinder>& who) {
ALOGW("statscompanion service died");
mAnmlyMntr->setStatsCompanionService(nullptr);
}
-status_t
-StatsService::doPrintStatsLog(FILE* out, const Vector<String8>& args) {
+status_t StatsService::doPrintStatsLog(FILE* out, const Vector<String8>& args) {
long msec = 0;
if (args.size() > 2) {
@@ -280,13 +260,14 @@
return DropboxReader::readStatsLogs(out, args[1].string(), msec);
}
-void
-StatsService::printCmdHelp(FILE* out) {
+void StatsService::printCmdHelp(FILE* out) {
fprintf(out, "Usage:\n");
fprintf(out, "\t print-stats-log [tag_required] [timestamp_nsec_optional]\n");
- fprintf(out, "\t config\t Loads a new config from command-line (must be proto in wire-encoded format).\n");
+ fprintf(out,
+ "\t config\t Loads a new config from command-line (must be proto in wire-encoded "
+ "format).\n");
}
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 57276d2..a16b115 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -19,12 +19,12 @@
#include "AnomalyMonitor.h"
#include "StatsLogProcessor.h"
+#include "StatsPuller.h"
#include <android/os/BnStatsManager.h>
#include <android/os/IStatsCompanionService.h>
#include <binder/IResultReceiver.h>
#include <binder/IShellCallback.h>
-#include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
#include <utils/Looper.h>
#include <deque>
@@ -62,30 +62,32 @@
virtual status_t setProcessor(const sp<StatsLogProcessor>& main_processor);
- // TODO: public for testing since statsd doesn't run when system starts. Change to private later.
+ // TODO: public for testing since statsd doesn't run when system starts. Change to private
+ // later.
/** Inform statsCompanion that statsd is ready. */
virtual void sayHiToStatsCompanion();
-private:
- sp<StatsLogProcessor> m_processor; // Reference to the processor for updating configs.
+ // TODO: Move this to a more logical file/class
+ // TODO: Should be private. Temporarily public for testing purposes only.
+ const sp<AnomalyMonitor> mAnomalyMonitor;
- const sp<AnomalyMonitor> mAnomalyMonitor; // TODO: Move this to a more logical file/class
+ /** Fetches and returns the StatsCompanionService. */
+ static sp<IStatsCompanionService> getStatsCompanionService();
+
+ private:
+ sp<StatsLogProcessor> m_processor; // Reference to the processor for updating configs.
status_t doPrintStatsLog(FILE* out, const Vector<String8>& args);
void printCmdHelp(FILE* out);
status_t doLoadConfig(FILE* in);
-
- /** Fetches the StatsCompanionService. */
- sp<IStatsCompanionService> getStatsCompanionService();
};
// --- StatsdDeathRecipient ---
class StatsdDeathRecipient : public IBinder::DeathRecipient {
public:
- StatsdDeathRecipient(sp<AnomalyMonitor> anomalyMonitor)
- : mAnmlyMntr(anomalyMonitor) {
+ StatsdDeathRecipient(sp<AnomalyMonitor> anomalyMonitor) : mAnmlyMntr(anomalyMonitor) {
}
virtual void binderDied(const wp<IBinder>& who);
@@ -94,8 +96,8 @@
const sp<AnomalyMonitor> mAnmlyMntr;
};
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
-#endif // STATS_SERVICE_H
+#endif // STATS_SERVICE_H
diff --git a/cmds/statsd/src/indexed_priority_queue.h b/cmds/statsd/src/indexed_priority_queue.h
index 76409c07..c749c3e 100644
--- a/cmds/statsd/src/indexed_priority_queue.h
+++ b/cmds/statsd/src/indexed_priority_queue.h
@@ -20,11 +20,11 @@
// ALOGE can be called from this file. If header loaded by another class, use their LOG_TAG instead.
#ifndef LOG_TAG
#define LOG_TAG "statsd(indexed_priority_queue)"
-#endif //LOG_TAG
+#endif // LOG_TAG
#include <cutils/log.h>
-#include <unordered_map>
#include <utils/RefBase.h>
+#include <unordered_map>
#include <vector>
using namespace android;
@@ -49,7 +49,7 @@
*/
template <class AA, class Comparator>
class indexed_priority_queue {
- public:
+public:
indexed_priority_queue();
/** Adds a into the priority queue. If already present or a==nullptr, does nothing. */
void push(sp<const AA> a);
@@ -62,11 +62,15 @@
/** Returns min element. Returns nullptr iff empty(). */
sp<const AA> top() const;
/** Returns number of elements in priority queue. */
- size_t size() const { return pq.size() - 1; } // pq is 1-indexed
+ size_t size() const {
+ return pq.size() - 1;
+ } // pq is 1-indexed
/** Returns true iff priority queue is empty. */
- bool empty() const { return size() < 1; }
+ bool empty() const {
+ return size() < 1;
+ }
- private:
+private:
/** Vector representing a min-heap (1-indexed, with nullptr at 0). */
std::vector<sp<const AA>> pq;
/** Mapping of each element in pq to its index in pq (i.e. the inverse of a=pq[i]). */
@@ -83,22 +87,22 @@
// Implementation must be done in this file due to use of template.
template <class AA, class Comparator>
-indexed_priority_queue<AA,Comparator>::indexed_priority_queue() {
+indexed_priority_queue<AA, Comparator>::indexed_priority_queue() {
init();
}
template <class AA, class Comparator>
-void indexed_priority_queue<AA,Comparator>::push(sp<const AA> a) {
+void indexed_priority_queue<AA, Comparator>::push(sp<const AA> a) {
if (a == nullptr) return;
if (contains(a)) return;
pq.push_back(a);
- size_t idx = size(); // index of last element since 1-indexed
+ size_t idx = size(); // index of last element since 1-indexed
indices.insert({a, idx});
- sift_up(idx); // get the pq back in order
+ sift_up(idx); // get the pq back in order
}
template <class AA, class Comparator>
-void indexed_priority_queue<AA,Comparator>::remove(sp<const AA> a) {
+void indexed_priority_queue<AA, Comparator>::remove(sp<const AA> a) {
if (a == nullptr) return;
if (!contains(a)) return;
size_t idx = indices[a];
@@ -106,7 +110,7 @@
ALOGE("indexed_priority_queue: Invalid index in map of indices.");
return;
}
- if (idx == size()) { // if a is the last element, i.e. at index idx == size() == (pq.size()-1)
+ if (idx == size()) { // if a is the last element, i.e. at index idx == size() == (pq.size()-1)
pq.pop_back();
indices.erase(a);
return;
@@ -124,62 +128,66 @@
}
template <class AA, class Comparator>
-void indexed_priority_queue<AA,Comparator>::clear() {
+void indexed_priority_queue<AA, Comparator>::clear() {
pq.clear();
indices.clear();
init();
}
template <class AA, class Comparator>
-sp<const AA> indexed_priority_queue<AA,Comparator>::top() const {
+sp<const AA> indexed_priority_queue<AA, Comparator>::top() const {
if (empty()) return nullptr;
return pq[1];
}
template <class AA, class Comparator>
-void indexed_priority_queue<AA,Comparator>::init() {
- pq.push_back(nullptr); // so that pq is 1-indexed.
- indices.insert({nullptr, 0}); // just to be consistent with pq.
+void indexed_priority_queue<AA, Comparator>::init() {
+ pq.push_back(nullptr); // so that pq is 1-indexed.
+ indices.insert({nullptr, 0}); // just to be consistent with pq.
}
template <class AA, class Comparator>
-void indexed_priority_queue<AA,Comparator>::sift_up(size_t idx) {
+void indexed_priority_queue<AA, Comparator>::sift_up(size_t idx) {
while (idx > 1) {
- size_t parent = idx/2;
- if (higher(idx, parent)) swap_indices(idx, parent);
- else break;
+ size_t parent = idx / 2;
+ if (higher(idx, parent))
+ swap_indices(idx, parent);
+ else
+ break;
idx = parent;
}
}
template <class AA, class Comparator>
-void indexed_priority_queue<AA,Comparator>::sift_down(size_t idx) {
- while (2*idx <= size()) {
+void indexed_priority_queue<AA, Comparator>::sift_down(size_t idx) {
+ while (2 * idx <= size()) {
size_t child = 2 * idx;
- if (child < size() && higher(child+1, child)) child++;
- if (higher(child, idx)) swap_indices(child, idx);
- else break;
+ if (child < size() && higher(child + 1, child)) child++;
+ if (higher(child, idx))
+ swap_indices(child, idx);
+ else
+ break;
idx = child;
}
}
template <class AA, class Comparator>
-bool indexed_priority_queue<AA,Comparator>::higher(size_t idx1, size_t idx2) const {
+bool indexed_priority_queue<AA, Comparator>::higher(size_t idx1, size_t idx2) const {
if (!(0u < idx1 && idx1 < pq.size() && 0u < idx2 && idx2 < pq.size())) {
ALOGE("indexed_priority_queue: Attempting to access invalid index");
- return false; // got to do something.
+ return false; // got to do something.
}
return Comparator()(pq[idx1], pq[idx2]);
}
template <class AA, class Comparator>
-bool indexed_priority_queue<AA,Comparator>::contains(sp<const AA> a) const {
- if (a == nullptr) return false; // publicly, we pretend that nullptr is not actually in pq.
+bool indexed_priority_queue<AA, Comparator>::contains(sp<const AA> a) const {
+ if (a == nullptr) return false; // publicly, we pretend that nullptr is not actually in pq.
return indices.count(a) > 0;
}
template <class AA, class Comparator>
-void indexed_priority_queue<AA,Comparator>::swap_indices(size_t i, size_t j) {
+void indexed_priority_queue<AA, Comparator>::swap_indices(size_t i, size_t j) {
if (!(0u < i && i < pq.size() && 0u < j && j < pq.size())) {
ALOGE("indexed_priority_queue: Attempting to swap invalid index");
return;
@@ -192,8 +200,8 @@
indices[val_j] = i;
}
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace statsd
+} // namespace os
+} // namespace android
-#endif //STATSD_INDEXED_PRIORITY_QUEUE_H
+#endif // STATSD_INDEXED_PRIORITY_QUEUE_H
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index c1dad4f..b303321 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -18,8 +18,8 @@
#include "LogEntryPrinter.h"
#include "LogReader.h"
-#include "StatsService.h"
#include "StatsLogProcessor.h"
+#include "StatsService.h"
#include <binder/IInterface.h>
#include <binder/IPCThreadState.h>
@@ -31,8 +31,8 @@
#include <utils/StrongPointer.h>
#include <stdio.h>
-#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
using namespace android;
@@ -49,9 +49,7 @@
/**
* Thread func for where the log reader runs.
*/
-static void*
-log_reader_thread_func(void* cookie)
-{
+static void* log_reader_thread_func(void* cookie) {
log_reader_thread_data* data = static_cast<log_reader_thread_data*>(cookie);
sp<LogReader> reader = new LogReader();
@@ -75,9 +73,7 @@
/**
* Creates and starts the thread to own the LogReader.
*/
-static status_t
-start_log_reader_thread(const sp<StatsService>& service)
-{
+static status_t start_log_reader_thread(const sp<StatsService>& service) {
status_t err;
pthread_attr_t attr;
pthread_t thread;
@@ -108,9 +104,7 @@
}
// ================================================================================
-int
-main(int /*argc*/, char** /*argv*/)
-{
+int main(int /*argc*/, char** /*argv*/) {
status_t err;
// Set up the looper
@@ -118,7 +112,7 @@
// Set up the binder
sp<ProcessState> ps(ProcessState::self());
- ps->setThreadPoolMaxThreadCount(1); // everything is oneway, let it queue and save ram
+ ps->setThreadPoolMaxThreadCount(1); // everything is oneway, let it queue and save ram
ps->startThreadPool();
ps->giveThreadPoolName();
IPCThreadState::self()->disableBackgroundScheduling(true);
diff --git a/cmds/statsd/src/matchers/LogEntryMatcherManager.cpp b/cmds/statsd/src/matchers/LogEntryMatcherManager.cpp
new file mode 100644
index 0000000..ab7b2b1d
--- /dev/null
+++ b/cmds/statsd/src/matchers/LogEntryMatcherManager.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "LogEntryMatcherManager.h"
+#include <cutils/log.h>
+#include <log/event_tag_map.h>
+#include <log/log_event_list.h>
+#include <log/logprint.h>
+#include <utils/Errors.h>
+#include <unordered_map>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "parse_util.h"
+
+using std::set;
+using std::string;
+using std::unordered_map;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+LogEventWrapper LogEntryMatcherManager::parseLogEvent(log_msg msg) {
+ LogEventWrapper wrapper;
+ wrapper.timestamp_ns = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
+ wrapper.tagId = getTagId(msg);
+
+ // start iterating k,v pairs.
+ android_log_context context =
+ create_android_log_parser(const_cast<log_msg*>(&msg)->msg() + sizeof(uint32_t),
+ const_cast<log_msg*>(&msg)->len() - sizeof(uint32_t));
+ android_log_list_element elem;
+
+ if (context) {
+ memset(&elem, 0, sizeof(elem));
+ size_t index = 0;
+ int32_t key = -1;
+ do {
+ elem = android_log_read_next(context);
+ switch ((int)elem.type) {
+ case EVENT_TYPE_INT:
+ if (index % 2 == 0) {
+ key = elem.data.int32;
+ } else {
+ wrapper.intMap[key] = elem.data.int32;
+ }
+ index++;
+ break;
+ case EVENT_TYPE_FLOAT:
+ if (index % 2 == 1) {
+ wrapper.floatMap[key] = elem.data.float32;
+ }
+ index++;
+ break;
+ case EVENT_TYPE_STRING:
+ if (index % 2 == 1) {
+ wrapper.strMap[key] = elem.data.string;
+ }
+ index++;
+ break;
+ case EVENT_TYPE_LONG:
+ if (index % 2 == 1) {
+ wrapper.intMap[key] = elem.data.int64;
+ }
+ index++;
+ break;
+ case EVENT_TYPE_LIST:
+ break;
+ case EVENT_TYPE_LIST_STOP:
+ break;
+ case EVENT_TYPE_UNKNOWN:
+ break;
+ default:
+ elem.complete = true;
+ break;
+ }
+
+ if (elem.complete) {
+ break;
+ }
+ } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
+
+ android_log_destroy(&context);
+ }
+
+ return wrapper;
+}
+
+bool LogEntryMatcherManager::matches(const LogEntryMatcher& matcher, const LogEventWrapper& event) {
+ const int tagId = event.tagId;
+ const unordered_map<int, long>& intMap = event.intMap;
+ const unordered_map<int, string>& strMap = event.strMap;
+ const unordered_map<int, float>& floatMap = event.floatMap;
+ const unordered_map<int, bool>& boolMap = event.boolMap;
+
+ if (matcher.has_combination()) { // Need to evaluate composite matching
+ switch (matcher.combination().operation()) {
+ case LogicalOperation::AND:
+ for (auto nestedMatcher : matcher.combination().matcher()) {
+ if (!matches(nestedMatcher, event)) {
+ return false; // return false if any nested matcher is false;
+ }
+ }
+ return true; // Otherwise, return true.
+ case LogicalOperation::OR:
+ for (auto nestedMatcher : matcher.combination().matcher()) {
+ if (matches(nestedMatcher, event)) {
+ return true; // return true if any nested matcher is true;
+ }
+ }
+ return false;
+ case LogicalOperation::NOT:
+ return !matches(matcher.combination().matcher(0), event);
+
+ // Case NAND is just inverting the return statement of AND
+ case LogicalOperation::NAND:
+ for (auto nestedMatcher : matcher.combination().matcher()) {
+ auto simple = nestedMatcher.simple_log_entry_matcher();
+ if (!matches(nestedMatcher, event)) {
+ return true; // return false if any nested matcher is false;
+ }
+ }
+ return false; // Otherwise, return true.
+ case LogicalOperation::NOR:
+ for (auto nestedMatcher : matcher.combination().matcher()) {
+ if (matches(nestedMatcher, event)) {
+ return false; // return true if any nested matcher is true;
+ }
+ }
+ return true;
+ }
+ return false;
+ } else {
+ return matchesSimple(matcher.simple_log_entry_matcher(), event);
+ }
+}
+
+bool LogEntryMatcherManager::matchesSimple(const SimpleLogEntryMatcher& simpleMatcher,
+ const LogEventWrapper& event) {
+ const int tagId = event.tagId;
+ const unordered_map<int, long>& intMap = event.intMap;
+ const unordered_map<int, string>& strMap = event.strMap;
+ const unordered_map<int, float>& floatMap = event.floatMap;
+ const unordered_map<int, bool>& boolMap = event.boolMap;
+
+ for (int i = 0; i < simpleMatcher.tag_size(); i++) {
+ if (simpleMatcher.tag(i) != tagId) {
+ continue;
+ }
+
+ // now see if this event is interesting to us -- matches ALL the matchers
+ // defined in the metrics.
+ bool allMatched = true;
+ for (int j = 0; j < simpleMatcher.key_value_matcher_size(); j++) {
+ auto cur = simpleMatcher.key_value_matcher(j);
+
+ // TODO: Check if this key is a magic key (eg package name).
+ int key = cur.key_matcher().key();
+
+ switch (cur.value_matcher_case()) {
+ case KeyValueMatcher::ValueMatcherCase::kEqString: {
+ auto it = strMap.find(key);
+ if (it == strMap.end() || cur.eq_string().compare(it->second) != 0) {
+ allMatched = false;
+ }
+ break;
+ }
+ case KeyValueMatcher::ValueMatcherCase::kEqInt: {
+ auto it = intMap.find(key);
+ if (it == intMap.end() || cur.eq_int() != it->second) {
+ allMatched = false;
+ }
+ break;
+ }
+ case KeyValueMatcher::ValueMatcherCase::kEqBool: {
+ auto it = boolMap.find(key);
+ if (it == boolMap.end() || cur.eq_bool() != it->second) {
+ allMatched = false;
+ }
+ break;
+ }
+ // Begin numeric comparisons
+ case KeyValueMatcher::ValueMatcherCase::kLtInt: {
+ auto it = intMap.find(key);
+ if (it == intMap.end() || cur.lt_int() <= it->second) {
+ allMatched = false;
+ }
+ break;
+ }
+ case KeyValueMatcher::ValueMatcherCase::kGtInt: {
+ auto it = intMap.find(key);
+ if (it == intMap.end() || cur.gt_int() >= it->second) {
+ allMatched = false;
+ }
+ break;
+ }
+ case KeyValueMatcher::ValueMatcherCase::kLtFloat: {
+ auto it = floatMap.find(key);
+ if (it == floatMap.end() || cur.lt_float() <= it->second) {
+ allMatched = false;
+ }
+ break;
+ }
+ case KeyValueMatcher::ValueMatcherCase::kGtFloat: {
+ auto it = floatMap.find(key);
+ if (it == floatMap.end() || cur.gt_float() >= it->second) {
+ allMatched = false;
+ }
+ break;
+ }
+ // Begin comparisons with equality
+ case KeyValueMatcher::ValueMatcherCase::kLteInt: {
+ auto it = intMap.find(key);
+ if (it == intMap.end() || cur.lte_int() < it->second) {
+ allMatched = false;
+ }
+ break;
+ }
+ case KeyValueMatcher::ValueMatcherCase::kGteInt: {
+ auto it = intMap.find(key);
+ if (it == intMap.end() || cur.gte_int() > it->second) {
+ allMatched = false;
+ }
+ break;
+ }
+ case KeyValueMatcher::ValueMatcherCase::VALUE_MATCHER_NOT_SET:
+ // If value matcher is not present, assume that we match.
+ break;
+ }
+ }
+
+ if (allMatched) {
+ return true;
+ }
+ }
+ return false;
+}
+
+set<int> LogEntryMatcherManager::getTagIdsFromMatcher(const LogEntryMatcher& matcher) {
+ set<int> result;
+ switch (matcher.contents_case()) {
+ case LogEntryMatcher::kCombination:
+ for (auto sub_matcher : matcher.combination().matcher()) {
+ set<int> tagSet = getTagIdsFromMatcher(sub_matcher);
+ result.insert(tagSet.begin(), tagSet.end());
+ }
+ break;
+ case LogEntryMatcher::kSimpleLogEntryMatcher:
+ for (int i = 0; i < matcher.simple_log_entry_matcher().tag_size(); i++) {
+ result.insert(matcher.simple_log_entry_matcher().tag(i));
+ }
+ break;
+ case LogEntryMatcher::CONTENTS_NOT_SET:
+ break;
+ }
+ return result;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/matchers/LogEntryMatcherManager.h b/cmds/statsd/src/matchers/LogEntryMatcherManager.h
new file mode 100644
index 0000000..fc8e6a1
--- /dev/null
+++ b/cmds/statsd/src/matchers/LogEntryMatcherManager.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef LOG_ENTRY_MATCHER_MANAGER_H
+#define LOG_ENTRY_MATCHER_MANAGER_H
+
+#include <log/log_read.h>
+#include <log/logprint.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+using std::string;
+using std::unordered_map;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+typedef struct {
+ int tagId;
+ long timestamp_ns;
+ std::unordered_map<int, long> intMap;
+ std::unordered_map<int, std::string> strMap;
+ std::unordered_map<int, bool> boolMap;
+ std::unordered_map<int, float> floatMap;
+} LogEventWrapper;
+
+/**
+ * Keeps track per log entry which simple log entry matchers match.
+ */
+class LogEntryMatcherManager {
+public:
+ LogEntryMatcherManager();
+
+ ~LogEntryMatcherManager(){};
+
+ static LogEventWrapper parseLogEvent(log_msg msg);
+
+ static std::set<int> getTagIdsFromMatcher(const LogEntryMatcher& matcher);
+
+ static bool matches(const LogEntryMatcher& matcher, const LogEventWrapper& wrapper);
+
+ static bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher,
+ const LogEventWrapper& wrapper);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#endif // LOG_ENTRY_MATCHER_MANAGER_H
diff --git a/cmds/statsd/src/metrics/ConditionTracker.cpp b/cmds/statsd/src/metrics/ConditionTracker.cpp
new file mode 100644
index 0000000..684ffdb
--- /dev/null
+++ b/cmds/statsd/src/metrics/ConditionTracker.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "ConditionTracker"
+#define DEBUG true // STOPSHIP if true
+#define VLOG(...) \
+ if (DEBUG) ALOGD(__VA_ARGS__);
+
+#include "ConditionTracker.h"
+#include <cutils/log.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+ConditionTracker::ConditionTracker() : mIsConditionMet(true) {
+ VLOG("ConditionTracker()");
+}
+
+ConditionTracker::ConditionTracker(const Condition& condition)
+ : mCondition(condition), mIsConditionMet(true) {
+ VLOG("ConditionTracker()");
+}
+
+ConditionTracker::~ConditionTracker() {
+ VLOG("~ConditionTracker()");
+}
+
+void ConditionTracker::evaluateCondition(const LogEventWrapper& event) {
+ // modify condition.
+ VLOG("evaluateCondition");
+}
+
+bool ConditionTracker::isConditionMet() const {
+ VLOG("isConditionMet() %d", mIsConditionMet);
+ return mIsConditionMet;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/ConditionTracker.h b/cmds/statsd/src/metrics/ConditionTracker.h
new file mode 100644
index 0000000..b94d5ab
--- /dev/null
+++ b/cmds/statsd/src/metrics/ConditionTracker.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef CONDITION_TRACKER_H
+#define CONDITION_TRACKER_H
+
+#include <utils/RefBase.h>
+#include "../matchers/LogEntryMatcherManager.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class ConditionTracker : public RefBase {
+public:
+ ConditionTracker();
+
+ ConditionTracker(const Condition& condition);
+
+ ~ConditionTracker();
+
+ void evaluateCondition(const LogEventWrapper& event);
+
+ bool isConditionMet() const;
+
+private:
+ // this is the definition of the Condition.
+ Condition mCondition;
+
+ bool mIsConditionMet;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // CONDITION_TRACKER_H
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
new file mode 100644
index 0000000..fbd013e
--- /dev/null
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "CountMetric"
+#define DEBUG true // STOPSHIP if true
+#define VLOG(...) \
+ if (DEBUG) ALOGD(__VA_ARGS__);
+
+#include "CountMetricProducer.h"
+#include "parse_util.h"
+
+#include <cutils/log.h>
+#include <limits.h>
+#include <stdlib.h>
+
+using std::unordered_map;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+CountMetricProducer::CountMetricProducer(const CountMetric& metric,
+ const sp<ConditionTracker> condition)
+ : mMetric(metric),
+ mConditionTracker(condition),
+ mStartTime(std::time(nullptr)),
+ mCounter(0),
+ mCurrentBucketStartTime(mStartTime) {
+ // TODO: evaluate initial conditions. and set mConditionMet.
+ if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
+ mBucketSize_sec = metric.bucket().bucket_size_millis() / 1000;
+ } else {
+ mBucketSize_sec = LONG_MAX;
+ }
+
+ VLOG("created. bucket size %lu start_time: %lu", mBucketSize_sec, mStartTime);
+}
+
+CountMetricProducer::CountMetricProducer(const CountMetric& metric)
+ : CountMetricProducer(metric, new ConditionTracker()) {
+}
+
+CountMetricProducer::~CountMetricProducer() {
+ VLOG("~CountMetricProducer() called");
+}
+
+void CountMetricProducer::finish() {
+ // TODO: write the StatsLogReport to dropbox using
+ // DropboxWriter.
+ onDumpReport();
+}
+
+void CountMetricProducer::onDumpReport() {
+ VLOG("dump report now...");
+}
+
+void CountMetricProducer::onMatchedLogEvent(const LogEventWrapper& event) {
+ time_t eventTime = event.timestamp_ns / 1000000000;
+
+ // this is old event, maybe statsd restarted?
+ if (eventTime < mStartTime) {
+ return;
+ }
+
+ if (mConditionTracker->isConditionMet()) {
+ flushCounterIfNeeded(eventTime);
+ mCounter++;
+ }
+}
+
+// When a new matched event comes in, we check if it falls into the current bucket. And flush the
+// counter to the StatsLogReport and adjust the bucket if needed.
+void CountMetricProducer::flushCounterIfNeeded(const time_t& eventTime) {
+ if (mCurrentBucketStartTime + mBucketSize_sec > eventTime) {
+ return;
+ }
+
+ // TODO: add a KeyValuePair to StatsLogReport.
+ ALOGD("CountMetric: dump counter %d", mCounter);
+
+ // reset counter
+ mCounter = 0;
+
+ // adjust the bucket start time
+ mCurrentBucketStartTime =
+ mCurrentBucketStartTime +
+ ((eventTime - mCurrentBucketStartTime) / mBucketSize_sec) * mBucketSize_sec;
+
+ VLOG("new bucket start time: %lu", mCurrentBucketStartTime);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
new file mode 100644
index 0000000..7665791
--- /dev/null
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef COUNT_METRIC_PRODUCER_H
+#define COUNT_METRIC_PRODUCER_H
+
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+#include "../matchers/LogEntryMatcherManager.h"
+#include "ConditionTracker.h"
+#include "DropboxWriter.h"
+#include "MetricProducer.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class CountMetricProducer : public MetricProducer {
+public:
+ CountMetricProducer(const CountMetric& countMetric, const sp<ConditionTracker> condition);
+
+ CountMetricProducer(const CountMetric& countMetric);
+
+ virtual ~CountMetricProducer();
+
+ void onMatchedLogEvent(const LogEventWrapper& event) override;
+
+ void finish() override;
+
+ void onDumpReport() override;
+
+private:
+ const CountMetric mMetric;
+
+ const sp<ConditionTracker> mConditionTracker;
+
+ const time_t mStartTime;
+ // TODO: Add dimensions.
+ int mCounter;
+
+ time_t mCurrentBucketStartTime;
+
+ long mBucketSize_sec;
+
+ void flushCounterIfNeeded(const time_t& newEventTime);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#endif // COUNT_METRIC_PRODUCER_H
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
new file mode 100644
index 0000000..44a778b
--- /dev/null
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef METRIC_PRODUCER_H
+#define METRIC_PRODUCER_H
+
+#include <log/logprint.h>
+#include "../matchers/LogEntryMatcherManager.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// A MetricProducer is responsible for compute one single metrics, creating stats log report, and
+// writing the report to dropbox.
+class MetricProducer {
+public:
+ virtual ~MetricProducer(){};
+
+ // Consume the stats log if it's interesting to this metric.
+ virtual void onMatchedLogEvent(const LogEventWrapper& event) = 0;
+
+ // This is called when the metric collecting is done, e.g., when there is a new configuration
+ // coming. MetricProducer should do the clean up, and dump existing data to dropbox.
+ virtual void finish() = 0;
+
+ virtual void onDumpReport() = 0;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#endif // METRIC_PRODUCER_H
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
new file mode 100644
index 0000000..cb74206
--- /dev/null
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#define LOG_TAG "MetricManager"
+#define DEBUG true // STOPSHIP if true
+#define VLOG(...) \
+ if (DEBUG) ALOGD(__VA_ARGS__);
+
+#include "MetricsManager.h"
+#include <cutils/log.h>
+#include <log/logprint.h>
+#include "CountMetricProducer.h"
+#include "parse_util.h"
+
+using std::make_unique;
+using std::set;
+using std::string;
+using std::unique_ptr;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+MetricsManager::MetricsManager(const StatsdConfig& config) : mConfig(config), mLogMatchers() {
+ std::unordered_map<string, LogEntryMatcher> matcherMap;
+ std::unordered_map<string, sp<ConditionTracker>> conditionMap;
+
+ for (int i = 0; i < config.log_entry_matcher_size(); i++) {
+ const LogEntryMatcher& logMatcher = config.log_entry_matcher(i);
+ mMatchers.push_back(logMatcher);
+
+ matcherMap[config.log_entry_matcher(i).name()] = logMatcher;
+
+ mLogMatchers[logMatcher.name()] = vector<unique_ptr<MetricProducer>>();
+ // Collect all the tag ids that are interesting
+ set<int> tagIds = LogEntryMatcherManager::getTagIdsFromMatcher(logMatcher);
+
+ mTagIds.insert(tagIds.begin(), tagIds.end());
+ }
+
+ for (int i = 0; i < config.condition_size(); i++) {
+ const Condition& condition = config.condition(i);
+ conditionMap[condition.name()] = new ConditionTracker(condition);
+ }
+
+ // Build MetricProducers for each metric defined in config.
+ // (1) build CountMetricProducer
+ for (int i = 0; i < config.count_metric_size(); i++) {
+ const CountMetric& metric = config.count_metric(i);
+ auto it = mLogMatchers.find(metric.what());
+ if (it == mLogMatchers.end()) {
+ ALOGW("cannot find the LogEntryMatcher %s in config", metric.what().c_str());
+ continue;
+ }
+
+ if (metric.has_condition()) {
+ auto condition_it = conditionMap.find(metric.condition());
+ if (condition_it == conditionMap.end()) {
+ ALOGW("cannot find the Condition %s in the config", metric.condition().c_str());
+ continue;
+ }
+ it->second.push_back(make_unique<CountMetricProducer>(metric, condition_it->second));
+ } else {
+ it->second.push_back(make_unique<CountMetricProducer>(metric));
+ }
+ }
+
+ // TODO: build other types of metrics too.
+}
+
+MetricsManager::~MetricsManager() {
+ VLOG("~MetricManager()");
+}
+
+void MetricsManager::finish() {
+ for (auto const& entryPair : mLogMatchers) {
+ for (auto const& metric : entryPair.second) {
+ metric->finish();
+ }
+ }
+}
+
+// Consume the stats log if it's interesting to this metric.
+void MetricsManager::onLogEvent(const log_msg& logMsg) {
+ int tagId = getTagId(logMsg);
+ if (mTagIds.find(tagId) == mTagIds.end()) {
+ // not interesting...
+ return;
+ }
+ // Since at least one of the metrics is interested in this event, we parse it now.
+ LogEventWrapper event = LogEntryMatcherManager::parseLogEvent(logMsg);
+
+ // Evaluate the conditions. Order matters, this should happen
+ // before sending the event to metrics
+ for (auto& condition : mConditionTracker) {
+ condition->evaluateCondition(event);
+ }
+
+ // Now find out which LogMatcher matches this event, and let relevant metrics know.
+ for (auto matcher : mMatchers) {
+ if (LogEntryMatcherManager::matches(matcher, event)) {
+ auto it = mLogMatchers.find(matcher.name());
+ if (it != mLogMatchers.end()) {
+ for (auto const& it2 : it->second) {
+ // Only metrics that matches this event get notified.
+ it2->onMatchedLogEvent(event);
+ }
+ } else {
+ // TODO: we should remove any redundant matchers that the config provides.
+ ALOGW("Matcher not used by any metrics.");
+ }
+ }
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
new file mode 100644
index 0000000..77d7535
--- /dev/null
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef METRICS_MANAGER_H
+#define METRICS_MANAGER_H
+
+#include <cutils/log.h>
+#include <log/logprint.h>
+#include <unordered_map>
+#include "../matchers/LogEntryMatcherManager.h"
+#include "ConditionTracker.h"
+#include "MetricProducer.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// A MetricsManager is responsible for managing metrics from one single config source.
+class MetricsManager {
+public:
+ MetricsManager(const StatsdConfig& config);
+
+ ~MetricsManager();
+
+ // Consume the stats log if it's interesting to this metric.
+ void onLogEvent(const log_msg& logMsg);
+
+ void finish();
+
+private:
+ const StatsdConfig mConfig;
+
+ // All event tags that are interesting to my metrics.
+ std::set<int> mTagIds;
+
+ // The matchers that my metrics share.
+ std::vector<LogEntryMatcher> mMatchers;
+
+ // The conditions that my metrics share.
+ std::vector<sp<ConditionTracker>> mConditionTracker;
+
+ // the map from LogEntryMatcher names to the metrics that use this matcher.
+ std::unordered_map<std::string, std::vector<std::unique_ptr<MetricProducer>>> mLogMatchers;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // METRICS_MANAGER_H
diff --git a/cmds/statsd/src/parse_util.cpp b/cmds/statsd/src/parse_util.cpp
index 9caeacf..61421880 100644
--- a/cmds/statsd/src/parse_util.cpp
+++ b/cmds/statsd/src/parse_util.cpp
@@ -14,79 +14,95 @@
* limitations under the License.
*/
-#include <parse_util.h>
#include <log/log_event_list.h>
+#include <parse_util.h>
-using android::os::statsd::EventMetricData;
-using android::os::statsd::KeyId;
-using android::os::statsd::KeyId_IsValid;
-using android::os::statsd::KeyValuePair;
-using android::os::statsd::TagId;
-using android::os::statsd::TagId_IsValid;
+namespace android {
+namespace os {
+namespace statsd {
-EventMetricData parse(log_msg msg)
-{
+static inline uint32_t get4LE(const char* src) {
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+int getTagId(log_msg msg) {
+ return get4LE(msg.msg());
+}
+
+EventMetricData parse(log_msg msg) {
// dump all statsd logs to dropbox for now.
// TODO: Add filtering, aggregation, etc.
EventMetricData eventMetricData;
- android_log_context context = create_android_log_parser(const_cast<log_msg*>(&msg)->msg()
- + sizeof(uint32_t),
- const_cast<log_msg*>(&msg)->len()
- - sizeof(uint32_t));
+
+ // set tag.
+ int tag = getTagId(msg);
+ // TODO: Replace the following line when we can serialize on the fly.
+ //eventMetricData.set_tag(tag);
+
+ // set timestamp of the event.
+ eventMetricData.set_timestamp_nanos(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec);
+
+ // start iterating k,v pairs.
+ android_log_context context =
+ create_android_log_parser(const_cast<log_msg*>(&msg)->msg() + sizeof(uint32_t),
+ const_cast<log_msg*>(&msg)->len() - sizeof(uint32_t));
android_log_list_element elem;
if (context) {
memset(&elem, 0, sizeof(elem));
size_t index = 0;
int32_t key = -1;
- int32_t tag = -1;
do {
elem = android_log_read_next(context);
switch ((int)elem.type) {
case EVENT_TYPE_INT:
- if (index == 0) {
- tag = elem.data.int32;
- if (TagId_IsValid(tag)) {
- eventMetricData.set_tag(static_cast<TagId>(tag));
- } else {
- break;
- }
- } else if (index % 2 == 1) {
+ if (index % 2 == 0) {
key = elem.data.int32;
- } else if (KeyId_IsValid(key)) {
- int32_t val = elem.data.int32;
- KeyValuePair *keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(static_cast<KeyId>(key));
- keyValuePair->set_value_int(val);
} else {
+ // TODO: Fix the following lines when we can serialize on the fly.
+ /*
+ int32_t val = elem.data.int32;
+ KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+ keyValuePair->set_key(key);
+ keyValuePair->set_value_int(val);
+ */
}
index++;
break;
case EVENT_TYPE_FLOAT:
- if (index % 2 == 0 && KeyId_IsValid(key)) {
+ if (index % 2 == 1) {
+ // TODO: Fix the following lines when we can serialize on the fly.
+ /*
float val = elem.data.float32;
- KeyValuePair *keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(static_cast<KeyId>(key));
+ KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+ keyValuePair->set_key(key);
keyValuePair->set_value_float(val);
+ */
}
index++;
break;
case EVENT_TYPE_STRING:
- if (index % 2 == 0 && KeyId_IsValid(key)) {
+ if (index % 2 == 1) {
+ // TODO: Fix the following lines when we can serialize on the fly.
+ /*
char* val = elem.data.string;
- KeyValuePair *keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(static_cast<KeyId>(key));
+ KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+ keyValuePair->set_key(key);
keyValuePair->set_value_str(val);
+ */
}
index++;
break;
case EVENT_TYPE_LONG:
- if (index % 2 == 0 && KeyId_IsValid(key)) {
+ if (index % 2 == 1) {
+ // TODO: Fix the following lines when we can serialize on the fly.
+ /*
int64_t val = elem.data.int64;
- KeyValuePair *keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(static_cast<KeyId>(key));
+ KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+ keyValuePair->set_key(key);
keyValuePair->set_value_int(val);
+ */
}
index++;
break;
@@ -111,3 +127,6 @@
return eventMetricData;
}
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/parse_util.h b/cmds/statsd/src/parse_util.h
index 8750f82..8b82e7b 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/cmds/statsd/src/parse_util.h
@@ -16,13 +16,21 @@
#ifndef PARSE_UTIL_H
#define PARSE_UTIL_H
-#include "LogReader.h"
#include "DropboxWriter.h"
+#include "LogReader.h"
#include <log/logprint.h>
-using android::os::statsd::EventMetricData;
+namespace android {
+namespace os {
+namespace statsd {
-EventMetricData parse(const log_msg msg);
+EventMetricData parse(log_msg msg);
-#endif // PARSE_UTIL_H
+int getTagId(log_msg msg);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // PARSE_UTIL_H
diff --git a/cmds/statsd/src/stats_constants.proto b/cmds/statsd/src/stats_constants.proto
deleted file mode 100644
index 3f8bd1c..0000000
--- a/cmds/statsd/src/stats_constants.proto
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-syntax = "proto2";
-
-package android.os.statsd;
-
-option optimize_for = LITE_RUNTIME;
-
-option java_package = "com.android.internal.logging";
-option java_outer_classname = "StatsConstantsProto";
-
-enum TagId {
- WAKELOCK = 1;
- SCREEN = 1003;
-}
-
-enum KeyId {
- STATE = 1;
- ANOTHER_STATE = 2;
- EVENT_TIMESTAMP = 1001;
- PACKAGE_NAME = 1002;
- PACKAGE_VERSION = 1003;
- PACKAGE_VERSION_STRING = 1004;
- ATTRIBUTION_CHAIN = 1005;
-}
diff --git a/cmds/statsd/src/stats_events.proto b/cmds/statsd/src/stats_events.proto
new file mode 100644
index 0000000..1e17895
--- /dev/null
+++ b/cmds/statsd/src/stats_events.proto
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package android.os.statsd;
+
+option java_package = "com.android.os";
+option java_outer_classname = "StatsEventProto";
+
+message StatsEvent {
+ oneof event {
+ // Screen state change.
+ ScreenStateChange screen_state_change = 2;
+ // Process state change.
+ ProcessStateChange process_state_change = 1112;
+ }
+}
+
+// Logs changes in screen state. This event is logged in
+// frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
+message ScreenStateChange {
+ // Screen state enums follow the values defined in below file.
+ // frameworks/base/core/java/android/view/Display.java
+ enum State {
+ STATE_UNKNOWN = 0;
+ STATE_OFF = 1;
+ STATE_ON = 2;
+ STATE_DOZE = 3;
+ STATE_DOZE_SUSPEND = 4;
+ STATE_VR = 5;
+ }
+ // New screen state.
+ optional State display_state = 1;
+}
+
+// Logs changes in process state. This event is logged in
+// frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
+message ProcessStateChange {
+ // Type of process event.
+ enum State {
+ START = 1;
+ CRASH = 2;
+ }
+ optional State state = 1;
+
+ // UID associated with the package.
+ optional int32 uid = 2;
+}
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 2c66ded..2dc0cc7 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -13,20 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
package android.os.statsd;
-option optimize_for = LITE_RUNTIME;
-
option java_package = "com.android.os";
option java_outer_classname = "StatsLog";
-import "frameworks/base/cmds/statsd/src/statsd_config.proto";
-import "frameworks/base/cmds/statsd/src/stats_constants.proto";
+import "frameworks/base/cmds/statsd/src/stats_events.proto";
message KeyValuePair {
- optional KeyId key = 1;
+ optional int32 key = 1;
oneof value {
string value_str = 2;
@@ -37,15 +36,15 @@
}
message EventMetricData {
- optional TagId tag = 1;
+ optional int64 timestamp_nanos = 1;
- repeated KeyValuePair key_value_pair = 2;
+ optional StatsEvent stats_events = 2;
}
message CountBucketInfo {
- optional int64 start_bucket_millis = 1;
+ optional int64 start_bucket_nanos = 1;
- optional int64 end_bucket_millis = 2;
+ optional int64 end_bucket_nanos = 2;
optional int64 count = 3;
}
@@ -59,9 +58,9 @@
message StatsLogReport {
optional int32 metric_id = 1;
- optional int64 start_report_millis = 2;
+ optional int64 start_report_nanos = 2;
- optional int64 end_report_millis = 3;
+ optional int64 end_report_nanos = 3;
message EventMetricDataWrapper {
repeated EventMetricData data = 1;
@@ -69,7 +68,6 @@
message CountMetricDataWrapper {
repeated CountMetricData data = 1;
}
-
oneof data {
EventMetricDataWrapper event_metrics = 4;
CountMetricDataWrapper count_metrics = 5;
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index c6119df..3e4ebaf 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -1,15 +1,30 @@
-syntax = "proto2";
-package android.os.statsd;
+/*
+ * Copyright (C) 2017 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.
+ */
+syntax = "proto2";
option optimize_for = LITE_RUNTIME;
+package android.os.statsd;
+
option java_package = "com.android.internal.os";
option java_outer_classname = "StatsdConfigProto";
-import "frameworks/base/cmds/statsd/src/stats_constants.proto";
-
message KeyMatcher {
- optional KeyId key = 1;
+ optional int32 key = 1;
+
optional bool as_package_name = 2 [ default = false ];
}
@@ -19,14 +34,15 @@
oneof value_matcher {
bool eq_bool = 2;
string eq_string = 3;
- int32 eq_int32 = 4;
- int64 eq_int64 = 5;
- int32 lt_int32 = 6;
- int32 gt_int32 = 7;
- int64 lt_int64 = 8;
- int64 gt_int64 = 9;
- float lt_float = 10;
- float gt_float = 11;
+ int32 eq_int = 4;
+
+ int64 lt_int = 5;
+ int64 gt_int = 6;
+ float lt_float = 7;
+ float gt_float = 8;
+
+ int64 lte_int = 9;
+ int64 gte_int = 10;
}
}
@@ -39,7 +55,7 @@
}
message SimpleLogEntryMatcher {
- repeated TagId tag = 1;
+ repeated int32 tag = 1;
repeated KeyValueMatcher key_value_matcher = 2;
}
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
new file mode 100644
index 0000000..473704a
--- /dev/null
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -0,0 +1,315 @@
+// Copyright (C) 2017 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.
+
+#define LOG_TAG "statsd_test"
+
+#include <gtest/gtest.h>
+#include <log/log_event_list.h>
+#include <log/log_read.h>
+#include <log/logprint.h>
+#include "../src/matchers/LogEntryMatcherManager.h"
+#include "../src/parse_util.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+#include <stdio.h>
+
+using namespace android::os::statsd;
+using std::unordered_map;
+
+const int kTagIdWakelock = 123;
+const int kKeyIdState = 45;
+const int kKeyIdPackageVersion = 67;
+
+#ifdef __ANDROID__
+TEST(LogEntryMatcherTest, TestSimpleMatcher) {
+ // Set up the matcher
+ LogEntryMatcher matcher;
+ auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+ simpleMatcher->add_tag(kTagIdWakelock);
+
+ LogEventWrapper wrapper;
+ wrapper.tagId = kTagIdWakelock;
+
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+}
+
+TEST(LogEntryMatcherTest, TestBoolMatcher) {
+ // Set up the matcher
+ LogEntryMatcher matcher;
+ auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+ simpleMatcher->add_tag(kTagIdWakelock);
+ auto keyValue = simpleMatcher->add_key_value_matcher();
+ keyValue->mutable_key_matcher()->set_key(kKeyIdState);
+
+ LogEventWrapper wrapper;
+ wrapper.tagId = kTagIdWakelock;
+
+ keyValue->set_eq_bool(true);
+ wrapper.boolMap[kKeyIdState] = true;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+
+ keyValue->set_eq_bool(false);
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+
+ wrapper.boolMap[kTagIdWakelock] = false;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+}
+
+TEST(LogEntryMatcherTest, TestStringMatcher) {
+ // Set up the matcher
+ LogEntryMatcher matcher;
+ auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+ simpleMatcher->add_tag(kTagIdWakelock);
+ auto keyValue = simpleMatcher->add_key_value_matcher();
+ keyValue->mutable_key_matcher()->set_key(kKeyIdState);
+ keyValue->set_eq_string("wakelock_name");
+
+ LogEventWrapper wrapper;
+ wrapper.tagId = kTagIdWakelock;
+
+ wrapper.strMap[kKeyIdState] = "wakelock_name";
+
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+}
+
+TEST(LogEntryMatcherTest, TestIntComparisonMatcher) {
+ // Set up the matcher
+ LogEntryMatcher matcher;
+ auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+ simpleMatcher->add_tag(kTagIdWakelock);
+ auto keyValue = simpleMatcher->add_key_value_matcher();
+ keyValue->mutable_key_matcher()->set_key(kKeyIdState);
+
+ LogEventWrapper wrapper;
+ wrapper.tagId = kTagIdWakelock;
+
+ keyValue->set_lt_int(10);
+ wrapper.intMap[kKeyIdState] = 11;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[kKeyIdState] = 10;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[kKeyIdState] = 9;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+
+ keyValue->set_gt_int(10);
+ wrapper.intMap[kKeyIdState] = 11;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[kKeyIdState] = 10;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[kKeyIdState] = 9;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+}
+
+TEST(LogEntryMatcherTest, TestIntWithEqualityComparisonMatcher) {
+ // Set up the matcher
+ LogEntryMatcher matcher;
+ auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+ simpleMatcher->add_tag(kTagIdWakelock);
+ auto keyValue = simpleMatcher->add_key_value_matcher();
+ keyValue->mutable_key_matcher()->set_key(kKeyIdState);
+
+ LogEventWrapper wrapper;
+ wrapper.tagId = kTagIdWakelock;
+
+ keyValue->set_lte_int(10);
+ wrapper.intMap[kKeyIdState] = 11;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[kKeyIdState] = 10;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[kKeyIdState] = 9;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+
+ keyValue->set_gte_int(10);
+ wrapper.intMap[kKeyIdState] = 11;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[kKeyIdState] = 10;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[kKeyIdState] = 9;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+}
+
+TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) {
+ // Set up the matcher
+ LogEntryMatcher matcher;
+ auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+ simpleMatcher->add_tag(kTagIdWakelock);
+ auto keyValue = simpleMatcher->add_key_value_matcher();
+ keyValue->mutable_key_matcher()->set_key(kKeyIdState);
+
+ LogEventWrapper wrapper;
+ wrapper.tagId = kTagIdWakelock;
+
+ keyValue->set_lt_float(10.0);
+ wrapper.floatMap[kKeyIdState] = 10.1;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.floatMap[kKeyIdState] = 9.9;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+
+ keyValue->set_gt_float(10.0);
+ wrapper.floatMap[kKeyIdState] = 10.1;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.floatMap[kKeyIdState] = 9.9;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+}
+
+// Helper for the composite matchers.
+void addSimpleMatcher(SimpleLogEntryMatcher* simpleMatcher, int tag, int key, int val) {
+ simpleMatcher->add_tag(tag);
+ auto keyValue = simpleMatcher->add_key_value_matcher();
+ keyValue->mutable_key_matcher()->set_key(key);
+ keyValue->set_eq_int(val);
+}
+
+TEST(LogEntryMatcherTest, TestAndMatcher) {
+ // Set up the matcher
+ LogEntryMatcher matcher;
+ auto combination = matcher.mutable_combination();
+ combination->set_operation(LogicalOperation::AND);
+
+ addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+ kTagIdWakelock, kKeyIdState, 3);
+ addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+ kTagIdWakelock, kKeyIdPackageVersion, 4);
+
+ LogEventWrapper wrapper;
+ wrapper.tagId = kTagIdWakelock;
+
+ wrapper.intMap[1003] = 4;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap.clear();
+ wrapper.intMap[1] = 3;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap.clear();
+ wrapper.intMap[1] = 3;
+ wrapper.intMap[1003] = 4;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+}
+
+TEST(LogEntryMatcherTest, TestOrMatcher) {
+ // Set up the matcher
+ LogEntryMatcher matcher;
+ auto combination = matcher.mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+
+ addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+ kTagIdWakelock, kKeyIdState, 3);
+ addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+ kTagIdWakelock, kKeyIdPackageVersion, 4);
+
+ LogEventWrapper wrapper;
+ wrapper.tagId = kTagIdWakelock;
+
+ // Don't set any key-value pairs.
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[1003] = 4;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap.clear();
+ wrapper.intMap[1] = 3;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap.clear();
+ wrapper.intMap[1] = 3;
+ wrapper.intMap[1003] = 4;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+}
+
+TEST(LogEntryMatcherTest, TestNotMatcher) {
+ // Set up the matcher
+ LogEntryMatcher matcher;
+ auto combination = matcher.mutable_combination();
+ combination->set_operation(LogicalOperation::NOT);
+
+ // Define first simpleMatcher
+ addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+ kTagIdWakelock, kKeyIdState, 3);
+
+ LogEventWrapper wrapper;
+ wrapper.tagId = kTagIdWakelock;
+
+ // Don't set any key-value pairs.
+ wrapper.intMap[kKeyIdState] = 3;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+}
+
+TEST(LogEntryMatcherTest, TestNANDMatcher) {
+ // Set up the matcher
+ LogEntryMatcher matcher;
+ auto combination = matcher.mutable_combination();
+ combination->set_operation(LogicalOperation::NAND);
+
+ addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+ kTagIdWakelock, kKeyIdState, 3);
+ addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+ kTagIdWakelock, kKeyIdPackageVersion, 4);
+
+ LogEventWrapper wrapper;
+ wrapper.tagId = kTagIdWakelock;
+
+ // Don't set any key-value pairs.
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[kKeyIdState] = 3;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[kKeyIdPackageVersion] = 4;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+}
+
+TEST(LogEntryMatcherTest, TestNORMatcher) {
+ // Set up the matcher
+ LogEntryMatcher matcher;
+ auto combination = matcher.mutable_combination();
+ combination->set_operation(LogicalOperation::NOR);
+
+ addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+ kTagIdWakelock, kKeyIdState, 3);
+ addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+ kTagIdWakelock, kKeyIdPackageVersion, 4);
+
+ LogEventWrapper wrapper;
+ wrapper.tagId = kTagIdWakelock;
+
+ // Don't set any key-value pairs.
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[kKeyIdState] = 3;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[kKeyIdPackageVersion] = 4;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+}
+
+// Tests that a NOT on top of AND is the same as NAND
+TEST(LogEntryMatcherTest, TestMultipleLayerMatcher) {
+ LogEntryMatcher matcher;
+ auto not_combination = matcher.mutable_combination();
+ not_combination->set_operation(LogicalOperation::NOT);
+
+ // Now add the AND
+ auto combination = not_combination->add_matcher()->mutable_combination();
+ combination->set_operation(LogicalOperation::AND);
+ addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+ kTagIdWakelock, kKeyIdState, 3);
+ addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+ kTagIdWakelock, kKeyIdPackageVersion, 4);
+
+ LogEventWrapper wrapper;
+ wrapper.tagId = kTagIdWakelock;
+
+ // Don't set any key-value pairs.
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[kKeyIdState] = 3;
+ EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.intMap[kKeyIdPackageVersion] = 4;
+ EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/LogReader_test.cpp b/cmds/statsd/tests/LogReader_test.cpp
index ca538b0..2002143 100644
--- a/cmds/statsd/tests/LogReader_test.cpp
+++ b/cmds/statsd/tests/LogReader_test.cpp
@@ -21,4 +21,3 @@
TEST(LogReaderTest, TestNothingAtAll) {
printf("yay!");
}
-
diff --git a/cmds/statsd/tests/indexed_priority_queue_test.cpp b/cmds/statsd/tests/indexed_priority_queue_test.cpp
index 1aad089..e4d4d25 100644
--- a/cmds/statsd/tests/indexed_priority_queue_test.cpp
+++ b/cmds/statsd/tests/indexed_priority_queue_test.cpp
@@ -133,7 +133,6 @@
EXPECT_TRUE(ipq.contains(aa4_b));
}
-
TEST(indexed_priority_queue, remove_nonexistant) {
indexed_priority_queue<AATest, AATest::Smaller> ipq;
sp<const AATest> aa4 = new AATest{4};
diff --git a/config/compiled-classes-phone b/config/compiled-classes-phone
index df68f14..42e6ecf 100644
--- a/config/compiled-classes-phone
+++ b/config/compiled-classes-phone
@@ -5022,8 +5022,6 @@
android.widget.RemoteViews$BitmapCache
android.widget.RemoteViews$BitmapReflectionAction
android.widget.RemoteViews$LayoutParamAction
-android.widget.RemoteViews$MemoryUsageCounter
-android.widget.RemoteViews$MutablePair
android.widget.RemoteViews$OnClickHandler
android.widget.RemoteViews$OnViewAppliedListener
android.widget.RemoteViews$ReflectionAction
@@ -5031,7 +5029,7 @@
android.widget.RemoteViews$RemoteViewsContextWrapper
android.widget.RemoteViews$RunnableAction
android.widget.RemoteViews$RuntimeAction
-android.widget.RemoteViews$SetDrawableParameters
+android.widget.RemoteViews$SetDrawableTint
android.widget.RemoteViews$SetOnClickPendingIntent
android.widget.RemoteViews$SetOnClickPendingIntent$1
android.widget.RemoteViews$ViewGroupAction
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 337d7a0..1b86724 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -2193,6 +2193,7 @@
android.util.Log
android.util.Log$1
android.util.Log$ImmediateLogWriter
+android.util.Log$PreloadHolder
android.util.Log$TerribleFailureHandler
android.util.LogPrinter
android.util.LongArray
@@ -2723,13 +2724,11 @@
android.widget.RemoteViews$Action
android.widget.RemoteViews$BitmapCache
android.widget.RemoteViews$LayoutParamAction
-android.widget.RemoteViews$MemoryUsageCounter
-android.widget.RemoteViews$MutablePair
android.widget.RemoteViews$OnClickHandler
android.widget.RemoteViews$ReflectionAction
android.widget.RemoteViews$RemoteView
android.widget.RemoteViews$RuntimeAction
-android.widget.RemoteViews$SetDrawableParameters
+android.widget.RemoteViews$SetDrawableTint
android.widget.RemoteViewsAdapter$RemoteAdapterConnectionCallback
android.widget.RtlSpacingHelper
android.widget.ScrollBarDrawable
diff --git a/config/preloaded-classes-extra b/config/preloaded-classes-extra
index 959fff5..09f393a 100644
--- a/config/preloaded-classes-extra
+++ b/config/preloaded-classes-extra
@@ -8,6 +8,7 @@
android.media.SoundPool
android.text.format.Formatter
android.text.Html$HtmlParser
+android.util.Log$PreloadHolder
com.android.org.conscrypt.TrustedCertificateStore
org.ccil.cowan.tagsoup.HTMLScanner
sun.security.jca.Providers
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 00d6657..1a2dc5c 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -843,7 +843,7 @@
// Assumes forward playing from here on.
for (int i = 0; i < mEvents.size(); i++) {
AnimationEvent event = mEvents.get(i);
- if (event.getTime() > currentPlayTime) {
+ if (event.getTime() > currentPlayTime || event.getTime() == DURATION_INFINITE) {
break;
}
@@ -1264,7 +1264,8 @@
} else {
for (int i = mLastEventId + 1; i < size; i++) {
AnimationEvent event = mEvents.get(i);
- if (event.getTime() <= currentPlayTime) {
+ // TODO: need a function that accounts for infinite duration to compare time
+ if (event.getTime() != DURATION_INFINITE && event.getTime() <= currentPlayTime) {
latestId = i;
}
}
diff --git a/core/java/android/annotation/NavigationRes.java b/core/java/android/annotation/NavigationRes.java
new file mode 100644
index 0000000..3af5ecf
--- /dev/null
+++ b/core/java/android/annotation/NavigationRes.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that an integer parameter, field or method return value is expected
+ * to be a navigation resource reference (e.g. {@code R.navigation.flow}).
+ *
+ * {@hide}
+ */
+@Documented
+@Retention(SOURCE)
+@Target({METHOD, PARAMETER, FIELD})
+public @interface NavigationRes {
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 4e258a3..e0ac911 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -114,6 +114,7 @@
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillManager.AutofillClient;
import android.view.autofill.AutofillPopupWindow;
import android.view.autofill.IAutofillWindowPresenter;
import android.widget.AdapterView;
@@ -947,6 +948,18 @@
return mAutofillManager;
}
+ @Override
+ protected void attachBaseContext(Context newBase) {
+ super.attachBaseContext(newBase);
+ newBase.setAutofillClient(this);
+ }
+
+ /** @hide */
+ @Override
+ public final AutofillClient getAutofillClient() {
+ return this;
+ }
+
/**
* Called when the activity is starting. This is where most initialization
* should go: calling {@link #setContentView(int)} to inflate the
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 78d05f5..26f96fb 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,10 +16,6 @@
package android.app;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -46,6 +42,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -675,10 +672,7 @@
/** First static stack ID.
* @hide */
- public static final int FIRST_STATIC_STACK_ID = 0;
-
- /** Home activity stack ID. */
- public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID;
+ private static final int FIRST_STATIC_STACK_ID = 0;
/** ID of stack where fullscreen activities are normally launched into. */
public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
@@ -692,15 +686,9 @@
/** ID of stack that always on top (always visible) when it exist. */
public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
- /** ID of stack that contains the Recents activity. */
- public static final int RECENTS_STACK_ID = PINNED_STACK_ID + 1;
-
- /** ID of stack that contains activities launched by the assistant. */
- public static final int ASSISTANT_STACK_ID = RECENTS_STACK_ID + 1;
-
/** Last static stack stack ID.
* @hide */
- public static final int LAST_STATIC_STACK_ID = ASSISTANT_STACK_ID;
+ private static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID;
/** Start of ID range used by stacks that are created dynamically.
* @hide */
@@ -720,15 +708,6 @@
}
/**
- * Returns true if dynamic stacks are allowed to be visible behind the input stack.
- * @hide
- */
- // TODO: Figure-out a way to remove.
- public static boolean isDynamicStacksVisibleBehindAllowed(int stackId) {
- return stackId == PINNED_STACK_ID || stackId == ASSISTANT_STACK_ID;
- }
-
- /**
* Returns true if we try to maintain focus in the current stack when the top activity
* finishes.
* @hide
@@ -740,15 +719,6 @@
}
/**
- * Returns true if the input stack is affected by drag resizing.
- * @hide
- */
- public static boolean isStackAffectedByDragResizing(int stackId) {
- return isStaticStack(stackId) && stackId != PINNED_STACK_ID
- && stackId != ASSISTANT_STACK_ID;
- }
-
- /**
* Returns true if the windows of tasks being moved to the target stack from the source
* stack should be replaced, meaning that window manager will keep the old window around
* until the new is ready.
@@ -760,26 +730,6 @@
}
/**
- * Return whether a stackId is a stack that be a backdrop to a translucent activity. These
- * are generally fullscreen stacks.
- * @hide
- */
- public static boolean isBackdropToTranslucentActivity(int stackId) {
- return stackId == FULLSCREEN_WORKSPACE_STACK_ID
- || stackId == ASSISTANT_STACK_ID;
- }
-
- /**
- * Returns true if activities from stasks in the given {@param stackId} are allowed to
- * enter picture-in-picture.
- * @hide
- */
- public static boolean isAllowedToEnterPictureInPicture(int stackId) {
- return stackId != HOME_STACK_ID && stackId != ASSISTANT_STACK_ID &&
- stackId != RECENTS_STACK_ID;
- }
-
- /**
* Returns true if the top task in the task is allowed to return home when finished and
* there are other tasks in the stack.
* @hide
@@ -810,34 +760,18 @@
&& stackId != DOCKED_STACK_ID;
}
- /**
- * Returns true if the input stack id should only be present on a device that supports
- * multi-window mode.
- * @see android.app.ActivityManager#supportsMultiWindow
- * @hide
- */
- // TODO: What about the other side of docked stack if we move this to WindowConfiguration?
- public static boolean isMultiWindowStack(int stackId) {
- return stackId == PINNED_STACK_ID || stackId == FREEFORM_WORKSPACE_STACK_ID
- || stackId == DOCKED_STACK_ID;
- }
-
- /**
- * Returns true if the input {@param stackId} is HOME_STACK_ID or RECENTS_STACK_ID
- * @hide
- */
- public static boolean isHomeOrRecentsStack(int stackId) {
- return stackId == HOME_STACK_ID || stackId == RECENTS_STACK_ID;
- }
-
- /** Returns true if the input stack and its content can affect the device orientation.
+ /** Returns the stack id for the input windowing mode.
* @hide */
- public static boolean canSpecifyOrientation(int stackId) {
- return stackId == HOME_STACK_ID
- || stackId == RECENTS_STACK_ID
- || stackId == FULLSCREEN_WORKSPACE_STACK_ID
- || stackId == ASSISTANT_STACK_ID
- || isDynamicStack(stackId);
+ // TODO: To be removed once we are not using stack id for stuff...
+ public static int getStackIdForWindowingMode(int windowingMode) {
+ switch (windowingMode) {
+ case WINDOWING_MODE_PINNED: return PINNED_STACK_ID;
+ case WINDOWING_MODE_FREEFORM: return FREEFORM_WORKSPACE_STACK_ID;
+ case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return DOCKED_STACK_ID;
+ case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return FULLSCREEN_WORKSPACE_STACK_ID;
+ case WINDOWING_MODE_FULLSCREEN: return FULLSCREEN_WORKSPACE_STACK_ID;
+ default: return INVALID_STACK_ID;
+ }
}
/** Returns the windowing mode that should be used for this input stack id.
@@ -847,14 +781,9 @@
final int windowingMode;
switch (stackId) {
case FULLSCREEN_WORKSPACE_STACK_ID:
- case HOME_STACK_ID:
- case RECENTS_STACK_ID:
windowingMode = inSplitScreenMode
? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_FULLSCREEN;
break;
- case ASSISTANT_STACK_ID:
- windowingMode = WINDOWING_MODE_FULLSCREEN;
- break;
case PINNED_STACK_ID:
windowingMode = WINDOWING_MODE_PINNED;
break;
@@ -869,51 +798,6 @@
}
return windowingMode;
}
-
- /** Returns the stack id for the input windowing mode.
- * @hide */
- // TODO: To be removed once we are not using stack id for stuff...
- public static int getStackIdForWindowingMode(int windowingMode) {
- switch (windowingMode) {
- case WINDOWING_MODE_PINNED: return PINNED_STACK_ID;
- case WINDOWING_MODE_FREEFORM: return FREEFORM_WORKSPACE_STACK_ID;
- case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return DOCKED_STACK_ID;
- default: return INVALID_STACK_ID;
- }
- }
-
- /** Returns the activity type that should be used for this input stack id.
- * @hide */
- // TODO: To be removed once we are not using stack id for stuff...
- public static int getActivityTypeForStackId(int stackId) {
- final int activityType;
- switch (stackId) {
- case HOME_STACK_ID:
- activityType = ACTIVITY_TYPE_HOME;
- break;
- case RECENTS_STACK_ID:
- activityType = ACTIVITY_TYPE_RECENTS;
- break;
- case ASSISTANT_STACK_ID:
- activityType = ACTIVITY_TYPE_ASSISTANT;
- break;
- default :
- activityType = ACTIVITY_TYPE_STANDARD;
- }
- return activityType;
- }
-
- /** Returns the stack id for the input activity type.
- * @hide */
- // TODO: To be removed once we are not using stack id for stuff...
- public static int getStackIdForActivityType(int activityType) {
- switch (activityType) {
- case ACTIVITY_TYPE_HOME: return HOME_STACK_ID;
- case ACTIVITY_TYPE_RECENTS: return RECENTS_STACK_ID;
- case ACTIVITY_TYPE_ASSISTANT: return ASSISTANT_STACK_ID;
- default: return INVALID_STACK_ID;
- }
- }
}
/**
@@ -1152,6 +1036,7 @@
* E.g. freeform, split-screen, picture-in-picture.
* @hide
*/
+ @TestApi
static public boolean supportsMultiWindow(Context context) {
// On watches, multi-window is used to present essential system UI, and thus it must be
// supported regardless of device memory characteristics.
@@ -1166,6 +1051,7 @@
* Returns true if the system supports split screen multi-window.
* @hide
*/
+ @TestApi
static public boolean supportsSplitScreenMultiWindow(Context context) {
return supportsMultiWindow(context)
&& Resources.getSystem().getBoolean(
@@ -1908,6 +1794,12 @@
*/
public int resizeMode;
+ /**
+ * The full configuration the task is currently running in.
+ * @hide
+ */
+ public Configuration configuration = new Configuration();
+
public RunningTaskInfo() {
}
@@ -1932,6 +1824,7 @@
dest.writeInt(numRunning);
dest.writeInt(supportsSplitScreenMultiWindow ? 1 : 0);
dest.writeInt(resizeMode);
+ configuration.writeToParcel(dest, flags);
}
public void readFromParcel(Parcel source) {
@@ -1949,6 +1842,7 @@
numRunning = source.readInt();
supportsSplitScreenMultiWindow = source.readInt() != 0;
resizeMode = source.readInt();
+ configuration.readFromParcel(source);
}
public static final Creator<RunningTaskInfo> CREATOR = new Creator<RunningTaskInfo>() {
@@ -2131,6 +2025,35 @@
}
/**
+ * Removes stacks in the windowing modes from the system if they are of activity type
+ * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+ *
+ * @hide
+ */
+ @TestApi
+ public void removeStacksInWindowingModes(int[] windowingModes) throws SecurityException {
+ try {
+ getService().removeStacksInWindowingModes(windowingModes);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes stack of the activity types from the system.
+ *
+ * @hide
+ */
+ @TestApi
+ public void removeStacksWithActivityTypes(int[] activityTypes) throws SecurityException {
+ try {
+ getService().removeStacksWithActivityTypes(activityTypes);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Represents a task snapshot.
* @hide
*/
@@ -2608,6 +2531,11 @@
public boolean visible;
// Index of the stack in the display's stack list, can be used for comparison of stack order
public int position;
+ /**
+ * The full configuration the stack is currently running in.
+ * @hide
+ */
+ public Configuration configuration = new Configuration();
@Override
public int describeContents() {
@@ -2642,6 +2570,7 @@
} else {
dest.writeInt(0);
}
+ configuration.writeToParcel(dest, flags);
}
public void readFromParcel(Parcel source) {
@@ -2669,6 +2598,7 @@
if (source.readInt() > 0) {
topActivity = ComponentName.readFromParcel(source);
}
+ configuration.readFromParcel(source);
}
public static final Creator<StackInfo> CREATOR = new Creator<StackInfo>() {
@@ -2696,6 +2626,8 @@
sb.append(" displayId="); sb.append(displayId);
sb.append(" userId="); sb.append(userId);
sb.append("\n");
+ sb.append(" configuration="); sb.append(configuration);
+ sb.append("\n");
prefix = prefix + " ";
for (int i = 0; i < taskIds.length; ++i) {
sb.append(prefix); sb.append("taskId="); sb.append(taskIds[i]);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4e8d240..2516a3e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5281,7 +5281,7 @@
final ApplicationInfo aInfo =
sPackageManager.getApplicationInfo(
packageName,
- 0 /*flags*/,
+ PackageManager.GET_SHARED_LIBRARY_FILES,
UserHandle.myUserId());
if (mActivities.size() > 0) {
@@ -5780,7 +5780,7 @@
final int preloadedFontsResource = info.metaData.getInt(
ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
if (preloadedFontsResource != 0) {
- data.info.mResources.preloadFonts(preloadedFontsResource);
+ data.info.getResources().preloadFonts(preloadedFontsResource);
}
}
} catch (RemoteException e) {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index c48be77..5f34322 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -74,6 +74,7 @@
import android.util.Slog;
import android.view.Display;
import android.view.DisplayAdjustments;
+import android.view.autofill.AutofillManager.AutofillClient;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
@@ -185,6 +186,8 @@
// The name of the split this Context is representing. May be null.
private @Nullable String mSplitName = null;
+ private AutofillClient mAutofillClient = null;
+
private final Object mSync = new Object();
@GuardedBy("mSync")
@@ -2225,6 +2228,18 @@
return mUser.getIdentifier();
}
+ /** @hide */
+ @Override
+ public AutofillClient getAutofillClient() {
+ return mAutofillClient;
+ }
+
+ /** @hide */
+ @Override
+ public void setAutofillClient(AutofillClient client) {
+ mAutofillClient = client;
+ }
+
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index eccb264..955b463 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -380,7 +380,8 @@
boolean preserveWindows, boolean animate, int animationDuration);
List<ActivityManager.StackInfo> getAllStackInfos();
void setFocusedStack(int stackId);
- ActivityManager.StackInfo getStackInfo(int stackId);
+ ActivityManager.StackInfo getFocusedStackInfo();
+ ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType);
boolean convertFromTranslucent(in IBinder token);
boolean convertToTranslucent(in IBinder token, in Bundle options);
void notifyActivityDrawn(in IBinder token);
@@ -440,7 +441,6 @@
// Start of M transactions
void notifyCleartextNetwork(int uid, in byte[] firstPacket);
int createStackOnDisplay(int displayId);
- int getFocusedStackId();
void setTaskResizeable(int taskId, int resizeableMode);
boolean requestAssistContextExtras(int requestType, in IResultReceiver receiver,
in Bundle receiverExtras, in IBinder activityToken,
@@ -540,6 +540,13 @@
void notifyPinnedStackAnimationStarted();
void notifyPinnedStackAnimationEnded();
void removeStack(int stackId);
+ /**
+ * Removes stacks in the input windowing modes from the system if they are of activity type
+ * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+ */
+ void removeStacksInWindowingModes(in int[] windowingModes);
+ /** Removes stack of the activity types from the system. */
+ void removeStacksWithActivityTypes(in int[] activityTypes);
void makePackageIdle(String packageName, int userId);
int getMemoryTrimLevel();
/**
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index b5b1017..a56965b 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -30,7 +30,7 @@
void onTaskStackChanged();
/** Called whenever an Activity is moved to the pinned stack from another stack. */
- void onActivityPinned(String packageName, int taskId);
+ void onActivityPinned(String packageName, int userId, int taskId);
/** Called whenever an Activity is moved from the pinned stack to another stack. */
void onActivityUnpinned();
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 76643d6..54f74b1 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -174,7 +174,7 @@
*/
public Intent createConfirmFactoryResetCredentialIntent(
CharSequence title, CharSequence description, CharSequence alternateButtonLabel) {
- if (!LockPatternUtils.frpCredentialEnabled()) {
+ if (!LockPatternUtils.frpCredentialEnabled(mContext)) {
Log.w(TAG, "Factory reset credentials not supported.");
return null;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ee6c1cb..fee7d6c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -67,7 +67,6 @@
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
-import android.util.TypedValue;
import android.view.Gravity;
import android.view.NotificationHeaderView;
import android.view.View;
@@ -3840,8 +3839,8 @@
contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
if (isColorized()) {
- contentView.setDrawableParameters(R.id.profile_badge, false, -1,
- getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
+ contentView.setDrawableTint(R.id.profile_badge, false,
+ getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP);
}
}
}
@@ -3906,7 +3905,6 @@
if (p.title != null) {
contentView.setViewVisibility(R.id.title, View.VISIBLE);
contentView.setTextViewText(R.id.title, processTextSpans(p.title));
- updateTextSizePrimary(contentView, R.id.title);
if (!p.ambient) {
setTextViewColorPrimary(contentView, R.id.title);
}
@@ -3918,7 +3916,6 @@
int textId = showProgress ? com.android.internal.R.id.text_line_1
: com.android.internal.R.id.text;
contentView.setTextViewText(textId, processTextSpans(p.text));
- updateTextSizeSecondary(contentView, textId);
if (!p.ambient) {
setTextViewColorSecondary(contentView, textId);
}
@@ -3930,25 +3927,6 @@
return contentView;
}
- private void updateTextSizeSecondary(RemoteViews contentView, int textId) {
- updateTextSizeColorized(contentView, textId,
- com.android.internal.R.dimen.notification_text_size_colorized,
- com.android.internal.R.dimen.notification_text_size);
- }
-
- private void updateTextSizePrimary(RemoteViews contentView, int textId) {
- updateTextSizeColorized(contentView, textId,
- com.android.internal.R.dimen.notification_title_text_size_colorized,
- com.android.internal.R.dimen.notification_title_text_size);
- }
-
- private void updateTextSizeColorized(RemoteViews contentView, int textId,
- int colorizedDimen, int normalDimen) {
- int size = mContext.getResources().getDimensionPixelSize(isColorized()
- ? colorizedDimen : normalDimen);
- contentView.setTextViewTextSize(textId, TypedValue.COMPLEX_UNIT_PX, size);
- }
-
private CharSequence processTextSpans(CharSequence text) {
if (hasForegroundColor()) {
return NotificationColorUtil.clearColorSpans(text);
@@ -4152,18 +4130,14 @@
if (action != null) {
int contrastColor = resolveContrastColor();
- contentView.setDrawableParameters(R.id.reply_icon_action,
+ contentView.setDrawableTint(R.id.reply_icon_action,
true /* targetBackground */,
- -1,
- contrastColor,
- PorterDuff.Mode.SRC_ATOP, -1);
+ contrastColor, PorterDuff.Mode.SRC_ATOP);
int iconColor = NotificationColorUtil.isColorLight(contrastColor)
? Color.BLACK : Color.WHITE;
- contentView.setDrawableParameters(R.id.reply_icon_action,
+ contentView.setDrawableTint(R.id.reply_icon_action,
false /* targetBackground */,
- -1,
- iconColor,
- PorterDuff.Mode.SRC_ATOP, -1);
+ iconColor, PorterDuff.Mode.SRC_ATOP);
contentView.setOnClickPendingIntent(R.id.right_icon,
action.actionIntent);
contentView.setOnClickPendingIntent(R.id.reply_icon_action,
@@ -4207,8 +4181,8 @@
private void bindExpandButton(RemoteViews contentView) {
int color = getPrimaryHighlightColor();
- contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
- PorterDuff.Mode.SRC_ATOP, -1);
+ contentView.setDrawableTint(R.id.expand_button, false, color,
+ PorterDuff.Mode.SRC_ATOP);
contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
color);
}
@@ -4315,8 +4289,7 @@
mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
}
contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
- contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
- -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
+ contentView.setInt(R.id.icon, "setImageLevel", mN.iconLevel);
processSmallIconColor(mN.mSmallIcon, contentView, ambient);
}
@@ -4706,8 +4679,8 @@
bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
: R.color.notification_action_list_dark);
}
- button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
- PorterDuff.Mode.SRC_ATOP, -1);
+ button.setDrawableTint(R.id.button_holder, true,
+ bgColor, PorterDuff.Mode.SRC_ATOP);
CharSequence title = action.title;
ColorStateList[] outResultColor = null;
if (isLegacy()) {
@@ -4840,8 +4813,8 @@
boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
if (colorable) {
- contentView.setDrawableParameters(R.id.icon, false, -1, color,
- PorterDuff.Mode.SRC_ATOP, -1);
+ contentView.setDrawableTint(R.id.icon, false, color,
+ PorterDuff.Mode.SRC_ATOP);
}
contentView.setInt(R.id.notification_header, "setOriginalIconColor",
@@ -4857,8 +4830,8 @@
if (largeIcon != null && isLegacy()
&& getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
// resolve color will fall back to the default when legacy
- contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
- PorterDuff.Mode.SRC_ATOP, -1);
+ contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(),
+ PorterDuff.Mode.SRC_ATOP);
}
}
@@ -5874,7 +5847,6 @@
builder.setTextViewColorSecondary(contentView, R.id.big_text);
contentView.setViewVisibility(R.id.big_text,
TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
- builder.updateTextSizeSecondary(contentView, R.id.big_text);
contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
}
}
@@ -6208,7 +6180,6 @@
contentView.setViewVisibility(rowId, View.VISIBLE);
contentView.setTextViewText(rowId, mBuilder.processTextSpans(
makeMessageLine(m, mBuilder)));
- mBuilder.updateTextSizeSecondary(contentView, rowId);
mBuilder.setTextViewColorSecondary(contentView, rowId);
if (contractedMessage == m) {
@@ -6576,7 +6547,6 @@
contentView.setViewVisibility(rowIds[i], View.VISIBLE);
contentView.setTextViewText(rowIds[i],
mBuilder.processTextSpans(mBuilder.processLegacyText(str)));
- mBuilder.updateTextSizeSecondary(contentView, rowIds[i]);
mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
handleInboxImageMargin(contentView, rowIds[i], first);
@@ -6775,8 +6745,8 @@
: NotificationColorUtil.resolveColor(mBuilder.mContext,
Notification.COLOR_DEFAULT);
- button.setDrawableParameters(R.id.action0, false, -1, tintColor,
- PorterDuff.Mode.SRC_ATOP, -1);
+ button.setDrawableTint(R.id.action0, false, tintColor,
+ PorterDuff.Mode.SRC_ATOP);
if (!tombstone) {
button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
}
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 163a8dc..47063f0 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,8 +15,11 @@
*/
package android.app;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.NotificationManager.Importance;
+import android.content.ContentResolver;
+import android.content.Context;
import android.content.Intent;
import android.media.AudioAttributes;
import android.net.Uri;
@@ -25,6 +28,9 @@
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.Preconditions;
import org.json.JSONException;
import org.json.JSONObject;
@@ -135,12 +141,15 @@
private boolean mLights;
private int mLightColor = DEFAULT_LIGHT_COLOR;
private long[] mVibration;
+ // Bitwise representation of fields that have been changed by the user, preventing the app from
+ // making changes to these fields.
private int mUserLockedFields;
private boolean mVibrationEnabled;
private boolean mShowBadge = DEFAULT_SHOW_BADGE;
private boolean mDeleted = DEFAULT_DELETED;
private String mGroup;
private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
+ // If this is a blockable system notification channel.
private boolean mBlockableSystem = false;
/**
@@ -565,14 +574,35 @@
/**
* @hide
*/
+ public void populateFromXmlForRestore(XmlPullParser parser, Context context) {
+ populateFromXml(parser, true, context);
+ }
+
+ /**
+ * @hide
+ */
@SystemApi
public void populateFromXml(XmlPullParser parser) {
+ populateFromXml(parser, false, null);
+ }
+
+ /**
+ * If {@param forRestore} is true, {@param Context} MUST be non-null.
+ */
+ private void populateFromXml(XmlPullParser parser, boolean forRestore,
+ @Nullable Context context) {
+ Preconditions.checkArgument(!forRestore || context != null,
+ "forRestore is true but got null context");
+
// Name, id, and importance are set in the constructor.
setDescription(parser.getAttributeValue(null, ATT_DESC));
setBypassDnd(Notification.PRIORITY_DEFAULT
!= safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT));
setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY));
- setSound(safeUri(parser, ATT_SOUND), safeAudioAttributes(parser));
+
+ Uri sound = safeUri(parser, ATT_SOUND);
+ setSound(forRestore ? restoreSoundUri(context, sound) : sound, safeAudioAttributes(parser));
+
enableLights(safeBool(parser, ATT_LIGHTS, false));
setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR));
setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null));
@@ -584,11 +614,62 @@
setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
}
+ @Nullable
+ private Uri restoreSoundUri(Context context, @Nullable Uri uri) {
+ if (uri == null) {
+ return null;
+ }
+ ContentResolver contentResolver = context.getContentResolver();
+ // There are backups out there with uncanonical uris (because we fixed this after
+ // shipping). If uncanonical uris are given to MediaProvider.uncanonicalize it won't
+ // verify the uri against device storage and we'll possibly end up with a broken uri.
+ // We then canonicalize the uri to uncanonicalize it back, which means we properly check
+ // the uri and in the case of not having the resource we end up with the default - better
+ // than broken. As a side effect we'll canonicalize already canonicalized uris, this is fine
+ // according to the docs because canonicalize method has to handle canonical uris as well.
+ Uri canonicalizedUri = contentResolver.canonicalize(uri);
+ if (canonicalizedUri == null) {
+ // We got a null because the uri in the backup does not exist here, so we return default
+ return Settings.System.DEFAULT_NOTIFICATION_URI;
+ }
+ return contentResolver.uncanonicalize(canonicalizedUri);
+ }
+
/**
* @hide
*/
@SystemApi
public void writeXml(XmlSerializer out) throws IOException {
+ writeXml(out, false, null);
+ }
+
+ /**
+ * @hide
+ */
+ public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException {
+ writeXml(out, true, context);
+ }
+
+ private Uri getSoundForBackup(Context context) {
+ Uri sound = getSound();
+ if (sound == null) {
+ return null;
+ }
+ Uri canonicalSound = context.getContentResolver().canonicalize(sound);
+ if (canonicalSound == null) {
+ // The content provider does not support canonical uris so we backup the default
+ return Settings.System.DEFAULT_NOTIFICATION_URI;
+ }
+ return canonicalSound;
+ }
+
+ /**
+ * If {@param forBackup} is true, {@param Context} MUST be non-null.
+ */
+ private void writeXml(XmlSerializer out, boolean forBackup, @Nullable Context context)
+ throws IOException {
+ Preconditions.checkArgument(!forBackup || context != null,
+ "forBackup is true but got null context");
out.startTag(null, TAG_CHANNEL);
out.attribute(null, ATT_ID, getId());
if (getName() != null) {
@@ -609,8 +690,9 @@
out.attribute(null, ATT_VISIBILITY,
Integer.toString(getLockscreenVisibility()));
}
- if (getSound() != null) {
- out.attribute(null, ATT_SOUND, getSound().toString());
+ Uri sound = forBackup ? getSoundForBackup(context) : getSound();
+ if (sound != null) {
+ out.attribute(null, ATT_SOUND, sound.toString());
}
if (getAudioAttributes() != null) {
out.attribute(null, ATT_USAGE, Integer.toString(getAudioAttributes().getUsage()));
@@ -850,4 +932,35 @@
+ ", mBlockableSystem=" + mBlockableSystem
+ '}';
}
+
+ /** @hide */
+ public void toProto(ProtoOutputStream proto) {
+ proto.write(NotificationChannelProto.ID, mId);
+ proto.write(NotificationChannelProto.NAME, mName);
+ proto.write(NotificationChannelProto.DESCRIPTION, mDesc);
+ proto.write(NotificationChannelProto.IMPORTANCE, mImportance);
+ proto.write(NotificationChannelProto.CAN_BYPASS_DND, mBypassDnd);
+ proto.write(NotificationChannelProto.LOCKSCREEN_VISIBILITY, mLockscreenVisibility);
+ if (mSound != null) {
+ proto.write(NotificationChannelProto.SOUND, mSound.toString());
+ }
+ proto.write(NotificationChannelProto.USE_LIGHTS, mLights);
+ proto.write(NotificationChannelProto.LIGHT_COLOR, mLightColor);
+ if (mVibration != null) {
+ for (long v : mVibration) {
+ proto.write(NotificationChannelProto.VIBRATION, v);
+ }
+ }
+ proto.write(NotificationChannelProto.USER_LOCKED_FIELDS, mUserLockedFields);
+ proto.write(NotificationChannelProto.IS_VIBRATION_ENABLED, mVibrationEnabled);
+ proto.write(NotificationChannelProto.SHOW_BADGE, mShowBadge);
+ proto.write(NotificationChannelProto.IS_DELETED, mDeleted);
+ proto.write(NotificationChannelProto.GROUP, mGroup);
+ if (mAudioAttributes != null) {
+ long aToken = proto.start(NotificationChannelProto.AUDIO_ATTRIBUTES);
+ mAudioAttributes.toProto(proto);
+ proto.end(aToken);
+ }
+ proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem);
+ }
}
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 5173311..5cb7fb7 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -21,6 +21,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
import org.json.JSONException;
import org.json.JSONObject;
@@ -295,4 +296,15 @@
+ ", mChannels=" + mChannels
+ '}';
}
+
+ /** @hide */
+ public void toProto(ProtoOutputStream proto) {
+ proto.write(NotificationChannelGroupProto.ID, mId);
+ proto.write(NotificationChannelGroupProto.NAME, mName.toString());
+ proto.write(NotificationChannelGroupProto.DESCRIPTION, mDescription);
+ proto.write(NotificationChannelGroupProto.IS_BLOCKED, mBlocked);
+ for (NotificationChannel channel : mChannels) {
+ channel.toProto(proto);
+ }
+ }
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 8fa7d6c..eb52cb7 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -41,6 +41,7 @@
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenModeConfig;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -1061,6 +1062,27 @@
+ "]";
}
+ /** @hide */
+ public void toProto(ProtoOutputStream proto, long fieldId) {
+ final long pToken = proto.start(fieldId);
+
+ bitwiseToProtoEnum(proto, PolicyProto.PRIORITY_CATEGORIES, priorityCategories);
+ proto.write(PolicyProto.PRIORITY_CALL_SENDER, priorityCallSenders);
+ proto.write(PolicyProto.PRIORITY_MESSAGE_SENDER, priorityMessageSenders);
+ bitwiseToProtoEnum(
+ proto, PolicyProto.SUPPRESSED_VISUAL_EFFECTS, suppressedVisualEffects);
+
+ proto.end(pToken);
+ }
+
+ private static void bitwiseToProtoEnum(ProtoOutputStream proto, long fieldId, int data) {
+ for (int i = 1; data > 0; ++i, data >>>= 1) {
+ if ((data & 1) == 1) {
+ proto.write(fieldId, i);
+ }
+ }
+ }
+
public static String suppressedEffectsToString(int effects) {
if (effects <= 0) return "";
final StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/app/SharedElementCallback.java b/core/java/android/app/SharedElementCallback.java
index af13e69..80fb805 100644
--- a/core/java/android/app/SharedElementCallback.java
+++ b/core/java/android/app/SharedElementCallback.java
@@ -27,6 +27,7 @@
import android.os.Parcelable;
import android.transition.TransitionUtils;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
@@ -176,7 +177,7 @@
Drawable d = imageView.getDrawable();
Drawable bg = imageView.getBackground();
if (d != null && (bg == null || bg.getAlpha() == 0)) {
- Bitmap bitmap = TransitionUtils.createDrawableBitmap(d);
+ Bitmap bitmap = TransitionUtils.createDrawableBitmap(d, imageView);
if (bitmap != null) {
Bundle bundle = new Bundle();
if (bitmap.getConfig() != Bitmap.Config.HARDWARE) {
@@ -202,7 +203,8 @@
} else {
mTempMatrix.set(viewToGlobalMatrix);
}
- return TransitionUtils.createViewBitmap(sharedElement, mTempMatrix, screenBounds);
+ ViewGroup parent = (ViewGroup) sharedElement.getParent();
+ return TransitionUtils.createViewBitmap(sharedElement, mTempMatrix, screenBounds, parent);
}
/**
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index fe7afed..8987bc0 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -70,14 +70,18 @@
* Setting this flag disables quick settings completely, but does not disable expanding the
* notification shade.
*/
- public static final int DISABLE2_QUICK_SETTINGS = 0x00000001;
+ public static final int DISABLE2_QUICK_SETTINGS = 1;
+ public static final int DISABLE2_SYSTEM_ICONS = 1 << 1;
+ public static final int DISABLE2_NOTIFICATION_SHADE = 1 << 2;
public static final int DISABLE2_NONE = 0x00000000;
- public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS;
+ public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS | DISABLE2_SYSTEM_ICONS
+ | DISABLE2_NOTIFICATION_SHADE;
@IntDef(flag = true,
- value = {DISABLE2_NONE, DISABLE2_MASK, DISABLE2_QUICK_SETTINGS})
+ value = {DISABLE2_NONE, DISABLE2_MASK, DISABLE2_QUICK_SETTINGS, DISABLE2_SYSTEM_ICONS,
+ DISABLE2_NOTIFICATION_SHADE})
@Retention(RetentionPolicy.SOURCE)
public @interface Disable2Flags {}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index ab70f0e7..50f1f36 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -81,10 +81,10 @@
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
-import android.net.nsd.INsdManager;
-import android.net.nsd.NsdManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
+import android.net.nsd.INsdManager;
+import android.net.nsd.NsdManager;
import android.net.wifi.IRttManager;
import android.net.wifi.IWifiManager;
import android.net.wifi.IWifiScanner;
@@ -95,6 +95,8 @@
import android.net.wifi.aware.WifiAwareManager;
import android.net.wifi.p2p.IWifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager;
+import android.net.wifi.rtt.IWifiRttManager;
+import android.net.wifi.rtt.WifiRttManager;
import android.nfc.NfcManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -603,6 +605,16 @@
ConnectivityThread.getInstanceLooper());
}});
+ registerService(Context.WIFI_RTT2_SERVICE, WifiRttManager.class,
+ new CachedServiceFetcher<WifiRttManager>() {
+ @Override
+ public WifiRttManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT2_SERVICE);
+ IWifiRttManager service = IWifiRttManager.Stub.asInterface(b);
+ return new WifiRttManager(ctx.getOuterContext(), service);
+ }});
+
registerService(Context.ETHERNET_SERVICE, EthernetManager.class,
new CachedServiceFetcher<EthernetManager>() {
@Override
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index a52ca0a..4674c9c 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -31,7 +31,8 @@
}
@Override
- public void onActivityPinned(String packageName, int taskId) throws RemoteException {
+ public void onActivityPinned(String packageName, int userId, int taskId)
+ throws RemoteException {
}
@Override
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
index 5786238..5c6ffa3 100644
--- a/core/java/android/app/VrManager.java
+++ b/core/java/android/app/VrManager.java
@@ -4,6 +4,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.content.ComponentName;
import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
@@ -181,4 +182,20 @@
e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Set the component name of the compositor service to bind.
+ *
+ * @param componentName ComponentName of a Service in the application's compositor process to
+ * bind to, or null to clear the current binding.
+ */
+ @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
+ public void setAndBindVrCompositor(ComponentName componentName) {
+ try {
+ mService.setAndBindCompositor(
+ (componentName == null) ? null : componentName.flattenToString());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 0cb3804..6b40538 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -500,10 +500,15 @@
* @hide
*/
public boolean supportSplitScreenWindowingMode() {
- if (mActivityType == ACTIVITY_TYPE_ASSISTANT) {
+ return supportSplitScreenWindowingMode(mWindowingMode, mActivityType);
+ }
+
+ /** @hide */
+ public static boolean supportSplitScreenWindowingMode(int windowingMode, int activityType) {
+ if (activityType == ACTIVITY_TYPE_ASSISTANT) {
return false;
}
- return mWindowingMode != WINDOWING_MODE_FREEFORM && mWindowingMode != WINDOWING_MODE_PINNED;
+ return windowingMode != WINDOWING_MODE_FREEFORM && windowingMode != WINDOWING_MODE_PINNED;
}
private static String windowingModeToString(@WindowingMode int windowingMode) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6bccad9..3c53063 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -63,7 +63,9 @@
import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import com.android.org.conscrypt.TrustedCertificateStore;
import java.io.ByteArrayInputStream;
@@ -3142,6 +3144,7 @@
*/
public static final int WIPE_EUICC = 0x0004;
+
/**
* Ask that all user data be wiped. If called as a secondary user, the user will be removed and
* other users will remain unaffected. Calling from the primary user will cause the device to
@@ -3157,10 +3160,47 @@
* that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
*/
public void wipeData(int flags) {
- throwIfParentInstance("wipeData");
+ final String wipeReasonForUser = mContext.getString(
+ R.string.work_profile_deleted_description_dpm_wipe);
+ wipeDataInternal(flags, wipeReasonForUser);
+ }
+
+ /**
+ * Ask that all user data be wiped. If called as a secondary user, the user will be removed and
+ * other users will remain unaffected, the provided reason for wiping data can be shown to
+ * user. Calling from the primary user will cause the device to reboot, erasing all device data
+ * - including all the secondary users and their data - while booting up. In this case, we don't
+ * show the reason to the user since the device would be factory reset.
+ * <p>
+ * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to
+ * be able to call this method; if it has not, a security exception will be thrown.
+ *
+ * @param flags Bit mask of additional options: currently supported flags are
+ * {@link #WIPE_EXTERNAL_STORAGE} and {@link #WIPE_RESET_PROTECTION_DATA}.
+ * @param reason a string that contains the reason for wiping data, which can be
+ * presented to the user.
+ * @throws SecurityException if the calling application does not own an active administrator
+ * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
+ * @throws IllegalArgumentException if the input reason string is null or empty.
+ */
+ public void wipeDataWithReason(int flags, @NonNull CharSequence reason) {
+ Preconditions.checkNotNull(reason, "CharSequence is null");
+ wipeDataInternal(flags, reason.toString());
+ }
+
+ /**
+ * Internal function for both {@link #wipeData(int)} and
+ * {@link #wipeDataWithReason(int, CharSequence)} to call.
+ *
+ * @see #wipeData(int)
+ * @see #wipeDataWithReason(int, CharSequence)
+ * @hide
+ */
+ private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) {
+ throwIfParentInstance("wipeDataWithReason");
if (mService != null) {
try {
- mService.wipeData(flags);
+ mService.wipeDataWithReason(flags, wipeReasonForUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -7534,12 +7574,12 @@
}
/**
- * Called by the device owner or profile owner to set the name of the organization under
- * management.
- * <p>
- * If the organization name needs to be localized, it is the responsibility of the
- * {@link DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast
- * and set a new version of this string accordingly.
+ * Called by the device owner (since API 26) or profile owner (since API 24) to set the name of
+ * the organization under management.
+ *
+ * <p>If the organization name needs to be localized, it is the responsibility of the {@link
+ * DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast and set
+ * a new version of this string accordingly.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param title The organization name or {@code null} to clear a previously set name.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index acfb602..8865a05 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -95,7 +95,7 @@
void lockNow(int flags, boolean parent);
- void wipeData(int flags);
+ void wipeDataWithReason(int flags, String wipeReasonForUser);
ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList);
ComponentName getGlobalProxyAdmin(int userHandle);
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 55c22de..d9b7cd7 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -674,6 +674,7 @@
ViewNodeText mText;
int mInputType;
+ String mWebScheme;
String mWebDomain;
Bundle mExtras;
LocaleList mLocaleList;
@@ -751,6 +752,7 @@
mInputType = in.readInt();
}
if ((flags&FLAGS_HAS_URL) != 0) {
+ mWebScheme = in.readString();
mWebDomain = in.readString();
}
if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) {
@@ -813,7 +815,7 @@
if (mInputType != 0) {
flags |= FLAGS_HAS_INPUT_TYPE;
}
- if (mWebDomain != null) {
+ if (mWebScheme != null || mWebDomain != null) {
flags |= FLAGS_HAS_URL;
}
if (mLocaleList != null) {
@@ -908,6 +910,7 @@
out.writeInt(mInputType);
}
if ((flags&FLAGS_HAS_URL) != 0) {
+ out.writeString(mWebScheme);
out.writeString(mWebDomain);
}
if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) {
@@ -1260,18 +1263,31 @@
* <p>Typically used when the view associated with the view is a container for an HTML
* document.
*
- * <strong>WARNING:</strong> a {@link android.service.autofill.AutofillService} should only
- * use this domain for autofill purposes when it trusts the app generating it (i.e., the app
- * defined by {@link AssistStructure#getActivityComponent()}).
+ * <p><b>Warning:</b> an autofill service cannot trust the value reported by this method
+ * without verifing its authenticity—see the "Web security" section of
+ * {@link android.service.autofill.AutofillService} for more details.
*
* @return domain-only part of the document. For example, if the full URL is
- * {@code http://my.site/login?user=my_user}, it returns {@code my.site}.
+ * {@code https://example.com/login?user=my_user}, it returns {@code example.com}.
*/
@Nullable public String getWebDomain() {
return mWebDomain;
}
/**
+ * Returns the scheme of the HTML document represented by this view.
+ *
+ * <p>Typically used when the view associated with the view is a container for an HTML
+ * document.
+ *
+ * @return scheme-only part of the document. For example, if the full URL is
+ * {@code https://example.com/login?user=my_user}, it returns {@code https}.
+ */
+ @Nullable public String getWebScheme() {
+ return mWebScheme;
+ }
+
+ /**
* Returns the HTML properties associated with this view.
*
* <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
@@ -1767,10 +1783,13 @@
@Override
public void setWebDomain(@Nullable String domain) {
if (domain == null) {
+ mNode.mWebScheme = null;
mNode.mWebDomain = null;
return;
}
- mNode.mWebDomain = Uri.parse(domain).getHost();
+ Uri uri = Uri.parse(domain);
+ mNode.mWebScheme = uri.getScheme();
+ mNode.mWebDomain = uri.getHost();
}
@Override
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 87e516c..1434c9b 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -317,7 +317,8 @@
}
/**
- * Whether this job needs the device to be plugged in.
+ * Whether this job requires that the device be charging (or be a non-battery-powered
+ * device connected to permanent power, such as Android TV devices).
*/
public boolean isRequireCharging() {
return (constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0;
@@ -331,7 +332,10 @@
}
/**
- * Whether this job needs the device to be in an Idle maintenance window.
+ * Whether this job requires that the user <em>not</em> be interacting with the device.
+ *
+ * <p class="note">This is <em>not</em> the same as "doze" or "device idle";
+ * it is purely about the user's direct interactions.</p>
*/
public boolean isRequireDeviceIdle() {
return (constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0;
@@ -918,9 +922,19 @@
}
/**
- * Specify that to run this job, the device needs to be plugged in. This defaults to
- * false.
- * @param requiresCharging Whether or not the device is plugged in.
+ * Specify that to run this job, the device must be charging (or be a
+ * non-battery-powered device connected to permanent power, such as Android TV
+ * devices). This defaults to {@code false}.
+ *
+ * <p class="note">For purposes of running jobs, a battery-powered device
+ * "charging" is not quite the same as simply being connected to power. If the
+ * device is so busy that the battery is draining despite a power connection, jobs
+ * with this constraint will <em>not</em> run. This can happen during some
+ * common use cases such as video chat, particularly if the device is plugged in
+ * to USB rather than to wall power.
+ *
+ * @param requiresCharging Pass {@code true} to require that the device be
+ * charging in order to run the job.
*/
public Builder setRequiresCharging(boolean requiresCharging) {
mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING)
@@ -942,14 +956,22 @@
}
/**
- * Specify that to run, the job needs the device to be in idle mode. This defaults to
- * false.
- * <p>Idle mode is a loose definition provided by the system, which means that the device
- * is not in use, and has not been in use for some time. As such, it is a good time to
- * perform resource heavy jobs. Bear in mind that battery usage will still be attributed
- * to your application, and surfaced to the user in battery stats.</p>
- * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance
- * window.
+ * When set {@code true}, ensure that this job will not run if the device is in active use.
+ * The default state is {@code false}: that is, the for the job to be runnable even when
+ * someone is interacting with the device.
+ *
+ * <p>This state is a loose definition provided by the system. In general, it means that
+ * the device is not currently being used interactively, and has not been in use for some
+ * time. As such, it is a good time to perform resource heavy jobs. Bear in mind that
+ * battery usage will still be attributed to your application, and surfaced to the user in
+ * battery stats.</p>
+ *
+ * <p class="note">Despite the similar naming, this job constraint is <em>not</em>
+ * related to the system's "device idle" or "doze" states. This constraint only
+ * determines whether a job is allowed to run while the device is directly in use.
+ *
+ * @param requiresDeviceIdle Pass {@code true} to prevent the job from running
+ * while the device is being used interactively.
*/
public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE)
diff --git a/core/java/android/app/timezone/RulesUpdaterContract.java b/core/java/android/app/timezone/RulesUpdaterContract.java
index 9c62f46..74ed658 100644
--- a/core/java/android/app/timezone/RulesUpdaterContract.java
+++ b/core/java/android/app/timezone/RulesUpdaterContract.java
@@ -51,7 +51,7 @@
* applies.
*/
public static final String ACTION_TRIGGER_RULES_UPDATE_CHECK =
- "android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK";
+ "com.android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK";
/**
* The extra containing the {@code byte[]} that should be passed to
@@ -61,7 +61,7 @@
* {@link #ACTION_TRIGGER_RULES_UPDATE_CHECK} intent has been processed.
*/
public static final String EXTRA_CHECK_TOKEN =
- "android.intent.extra.timezone.CHECK_TOKEN";
+ "com.android.intent.extra.timezone.CHECK_TOKEN";
/**
* Creates an intent that would trigger a time zone rules update check.
@@ -83,8 +83,7 @@
Intent intent = createUpdaterIntent(updaterAppPackageName);
intent.putExtra(EXTRA_CHECK_TOKEN, checkTokenBytes);
context.sendBroadcastAsUser(
- intent,
- UserHandle.of(UserHandle.myUserId()),
+ intent, UserHandle.SYSTEM,
RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION);
}
}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 8a1eae2..dc9970a 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -23,17 +23,12 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
-import android.os.Parcel;
import android.os.Parcelable;
-import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -58,17 +53,17 @@
* {@link RemoteViews}.
*/
public class AppWidgetHostView extends FrameLayout {
+
static final String TAG = "AppWidgetHostView";
+ private static final String KEY_JAILED_ARRAY = "jail";
+
static final boolean LOGD = false;
- static final boolean CROSSFADE = false;
static final int VIEW_MODE_NOINIT = 0;
static final int VIEW_MODE_CONTENT = 1;
static final int VIEW_MODE_ERROR = 2;
static final int VIEW_MODE_DEFAULT = 3;
- static final int FADE_DURATION = 1000;
-
// When we're inflating the initialLayout for a AppWidget, we only allow
// views that are allowed in RemoteViews.
static final LayoutInflater.Filter sInflaterFilter = new LayoutInflater.Filter() {
@@ -85,9 +80,6 @@
View mView;
int mViewMode = VIEW_MODE_NOINIT;
int mLayoutId = -1;
- long mFadeStartTime = -1;
- Bitmap mOld;
- Paint mOldPaint = new Paint();
private OnClickHandler mOnClickHandler;
private Executor mAsyncExecutor;
@@ -212,9 +204,12 @@
@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
- final ParcelableSparseArray jail = new ParcelableSparseArray();
+ final SparseArray<Parcelable> jail = new SparseArray<>();
super.dispatchSaveInstanceState(jail);
- container.put(generateId(), jail);
+
+ Bundle bundle = new Bundle();
+ bundle.putSparseParcelableArray(KEY_JAILED_ARRAY, jail);
+ container.put(generateId(), bundle);
}
private int generateId() {
@@ -226,12 +221,12 @@
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
final Parcelable parcelable = container.get(generateId());
- ParcelableSparseArray jail = null;
- if (parcelable != null && parcelable instanceof ParcelableSparseArray) {
- jail = (ParcelableSparseArray) parcelable;
+ SparseArray<Parcelable> jail = null;
+ if (parcelable instanceof Bundle) {
+ jail = ((Bundle) parcelable).getSparseParcelableArray(KEY_JAILED_ARRAY);
}
- if (jail == null) jail = new ParcelableSparseArray();
+ if (jail == null) jail = new SparseArray<>();
try {
super.dispatchRestoreInstanceState(jail);
@@ -383,31 +378,10 @@
* @hide
*/
protected void applyRemoteViews(RemoteViews remoteViews, boolean useAsyncIfPossible) {
- if (LOGD) Log.d(TAG, "updateAppWidget called mOld=" + mOld);
-
boolean recycled = false;
View content = null;
Exception exception = null;
- // Capture the old view into a bitmap so we can do the crossfade.
- if (CROSSFADE) {
- if (mFadeStartTime < 0) {
- if (mView != null) {
- final int width = mView.getWidth();
- final int height = mView.getHeight();
- try {
- mOld = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- } catch (OutOfMemoryError e) {
- // we just won't do the fade
- mOld = null;
- }
- if (mOld != null) {
- //mView.drawIntoBitmap(mOld);
- }
- }
- }
- }
-
if (mLastExecutionSignal != null) {
mLastExecutionSignal.cancel();
mLastExecutionSignal = null;
@@ -484,16 +458,6 @@
removeView(mView);
mView = content;
}
-
- if (CROSSFADE) {
- if (mFadeStartTime < 0) {
- // if there is already an animation in progress, don't do anything --
- // the new view will pop in on top of the old one during the cross fade,
- // and that looks okay.
- mFadeStartTime = SystemClock.uptimeMillis();
- invalidate();
- }
- }
}
private void updateContentDescription(AppWidgetProviderInfo info) {
@@ -617,45 +581,6 @@
}
}
- @Override
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- if (CROSSFADE) {
- int alpha;
- int l = child.getLeft();
- int t = child.getTop();
- if (mFadeStartTime > 0) {
- alpha = (int)(((drawingTime-mFadeStartTime)*255)/FADE_DURATION);
- if (alpha > 255) {
- alpha = 255;
- }
- Log.d(TAG, "drawChild alpha=" + alpha + " l=" + l + " t=" + t
- + " w=" + child.getWidth());
- if (alpha != 255 && mOld != null) {
- mOldPaint.setAlpha(255-alpha);
- //canvas.drawBitmap(mOld, l, t, mOldPaint);
- }
- } else {
- alpha = 255;
- }
- int restoreTo = canvas.saveLayerAlpha(l, t, child.getWidth(), child.getHeight(), alpha,
- Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
- boolean rv = super.drawChild(canvas, child, drawingTime);
- canvas.restoreToCount(restoreTo);
- if (alpha < 255) {
- invalidate();
- } else {
- mFadeStartTime = -1;
- if (mOld != null) {
- mOld.recycle();
- mOld = null;
- }
- }
- return rv;
- } else {
- return super.drawChild(canvas, child, drawingTime);
- }
- }
-
/**
* Prepare the given view to be shown. This might include adjusting
* {@link FrameLayout.LayoutParams} before inserting.
@@ -740,36 +665,4 @@
super.onInitializeAccessibilityNodeInfoInternal(info);
info.setClassName(AppWidgetHostView.class.getName());
}
-
- private static class ParcelableSparseArray extends SparseArray<Parcelable> implements Parcelable {
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel dest, int flags) {
- final int count = size();
- dest.writeInt(count);
- for (int i = 0; i < count; i++) {
- dest.writeInt(keyAt(i));
- dest.writeParcelable(valueAt(i), 0);
- }
- }
-
- public static final Parcelable.Creator<ParcelableSparseArray> CREATOR =
- new Parcelable.Creator<ParcelableSparseArray>() {
- public ParcelableSparseArray createFromParcel(Parcel source) {
- final ParcelableSparseArray array = new ParcelableSparseArray();
- final ClassLoader loader = array.getClass().getClassLoader();
- final int count = source.readInt();
- for (int i = 0; i < count; i++) {
- array.put(source.readInt(), source.readParcelable(loader));
- }
- return array;
- }
-
- public ParcelableSparseArray[] newArray(int size) {
- return new ParcelableSparseArray[size];
- }
- };
- }
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 70591d4..84765f6 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1134,6 +1134,29 @@
}
/**
+ * Sets the {@link BluetoothClass} Bluetooth Class of Device (CoD) of
+ * the local Bluetooth adapter.
+ *
+ * @param bluetoothClass {@link BluetoothClass} to set the local Bluetooth adapter to.
+ * @return true if successful, false if unsuccessful.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean setBluetoothClass(BluetoothClass bluetoothClass) {
+ if (getState() != STATE_ON) return false;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.setBluetoothClass(bluetoothClass);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return false;
+ }
+
+ /**
* Get the current Bluetooth scan mode of the local Bluetooth adapter.
* <p>The Bluetooth scan mode determines if the local adapter is
* connectable and/or discoverable from remote Bluetooth devices.
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 57e4abb..f22ea6e 100755
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -19,6 +19,10 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
/**
* Represents a Bluetooth class, which describes general characteristics
* and capabilities of a device. For example, a Bluetooth class will
@@ -275,6 +279,48 @@
return (mClass & Device.BITMASK);
}
+ /**
+ * Return the Bluetooth Class of Device (CoD) value including the
+ * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and
+ * minor device fields.
+ *
+ * <p>This value is an integer representation of Bluetooth CoD as in
+ * Bluetooth specification.
+ *
+ * @see <a href="Bluetooth CoD">https://www.bluetooth.com/specifications/assigned-numbers/baseband</a>
+ *
+ * @hide
+ */
+ public int getClassOfDevice() {
+ return mClass;
+ }
+
+ /**
+ * Return the Bluetooth Class of Device (CoD) value including the
+ * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and
+ * minor device fields.
+ *
+ * <p>This value is a byte array representation of Bluetooth CoD as in
+ * Bluetooth specification.
+ *
+ * <p>Bluetooth COD information is 3 bytes, but stored as an int. Hence the
+ * MSB is useless and needs to be thrown away. The lower 3 bytes are
+ * converted into a byte array MSB to LSB. Hence, using BIG_ENDIAN.
+ *
+ * @see <a href="Bluetooth CoD">https://www.bluetooth.com/specifications/assigned-numbers/baseband</a>
+ *
+ * @hide
+ */
+ public byte[] getClassOfDeviceBytes() {
+ byte[] bytes = ByteBuffer.allocate(4)
+ .order(ByteOrder.BIG_ENDIAN)
+ .putInt(mClass)
+ .array();
+
+ // Discard the top byte
+ return Arrays.copyOfRange(bytes, 1, bytes.length);
+ }
+
/** @hide */
public static final int PROFILE_HEADSET = 0;
/** @hide */
diff --git a/core/java/android/bluetooth/BluetoothInputHost.java b/core/java/android/bluetooth/BluetoothInputHost.java
index 37f0427..e18d9d1 100644
--- a/core/java/android/bluetooth/BluetoothInputHost.java
+++ b/core/java/android/bluetooth/BluetoothInputHost.java
@@ -74,7 +74,7 @@
public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02;
public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03;
public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04;
- public static final byte SUBCLASS2_DIGITIZER_TABLED = (byte) 0x05;
+ public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05;
public static final byte SUBCLASS2_CARD_READER = (byte) 0x06;
/**
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 19f5198..a1a9347 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.annotation.SdkConstant;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -53,35 +54,32 @@
private static final boolean DBG = true;
private static final boolean VDBG = false;
- /** int extra for PBAP_STATE_CHANGED_ACTION */
- public static final String PBAP_STATE =
- "android.bluetooth.pbap.intent.PBAP_STATE";
- /** int extra for PBAP_STATE_CHANGED_ACTION */
- public static final String PBAP_PREVIOUS_STATE =
- "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE";
-
/**
- * Indicates the state of a pbap connection state has changed.
- * This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and
- * BluetoothIntent.ADDRESS extras.
+ * Intent used to broadcast the change in connection state of the PBAP
+ * profile.
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link BluetoothProfile#EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ * <p>{@link BluetoothProfile#EXTRA_STATE} or {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}
+ * can be any of {@link BluetoothProfile#STATE_DISCONNECTED},
+ * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED},
+ * {@link BluetoothProfile#STATE_DISCONNECTING}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
*/
- public static final String PBAP_STATE_CHANGED_ACTION =
- "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED";
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CONNECTION_STATE_CHANGED =
+ "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
private volatile IBluetoothPbap mService;
private final Context mContext;
private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
- /** There was an error trying to obtain the state */
- public static final int STATE_ERROR = -1;
- /** No client currently connected */
- public static final int STATE_DISCONNECTED = 0;
- /** Connection attempt in progress */
- public static final int STATE_CONNECTING = 1;
- /** Client is currently connected */
- public static final int STATE_CONNECTED = 2;
-
public static final int RESULT_FAILURE = 0;
public static final int RESULT_SUCCESS = 1;
/** Connection canceled before completion. */
@@ -209,8 +207,8 @@
/**
* Get the current state of the BluetoothPbap service.
*
- * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not
- * connected to the Pbap service.
+ * @return One of the STATE_ return codes, or {@link BluetoothProfile#STATE_DISCONNECTED}
+ * if this proxy object is currently not connected to the Pbap service.
*/
public int getState() {
if (VDBG) log("getState()");
@@ -225,7 +223,7 @@
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
}
- return BluetoothPbap.STATE_ERROR;
+ return BluetoothProfile.STATE_DISCONNECTED;
}
/**
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index 00a15f3..01b3f6e 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -40,7 +40,7 @@
private static final boolean VDBG = false;
public static final String ACTION_CONNECTION_STATE_CHANGED =
- "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
+ "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED";
private volatile IBluetoothPbapClient mService;
private final Context mContext;
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index ea6b769..0d36bdd 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -21,9 +21,9 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
-import java.lang.Comparable;
/**
* Identifier for a specific application component
@@ -33,7 +33,7 @@
* pieces of information, encapsulated here, are required to identify
* a component: the package (a String) it exists in, and the class (a String)
* name inside of that package.
- *
+ *
*/
public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> {
private final String mPackage;
@@ -91,7 +91,7 @@
/**
* Create a new component identifier.
- *
+ *
* @param pkg The name of the package that the component exists in. Can
* not be null.
* @param cls The name of the class inside of <var>pkg</var> that
@@ -106,7 +106,7 @@
/**
* Create a new component identifier from a Context and class name.
- *
+ *
* @param pkg A Context for the package implementing the component,
* from which the actual package name will be retrieved.
* @param cls The name of the class inside of <var>pkg</var> that
@@ -120,7 +120,7 @@
/**
* Create a new component identifier from a Context and Class object.
- *
+ *
* @param pkg A Context for the package implementing the component, from
* which the actual package name will be retrieved.
* @param cls The Class object of the desired component, from which the
@@ -141,14 +141,14 @@
public @NonNull String getPackageName() {
return mPackage;
}
-
+
/**
* Return the class name of this component.
*/
public @NonNull String getClassName() {
return mClass;
}
-
+
/**
* Return the class name, either fully qualified or in a shortened form
* (with a leading '.') if it is a suffix of the package.
@@ -163,7 +163,7 @@
}
return mClass;
}
-
+
private static void appendShortClassName(StringBuilder sb, String packageName,
String className) {
if (className.startsWith(packageName)) {
@@ -195,26 +195,26 @@
* class names contained in the ComponentName. You can later recover
* the ComponentName from this string through
* {@link #unflattenFromString(String)}.
- *
+ *
* @return Returns a new String holding the package and class names. This
* is represented as the package name, concatenated with a '/' and then the
* class name.
- *
+ *
* @see #unflattenFromString(String)
*/
public @NonNull String flattenToString() {
return mPackage + "/" + mClass;
}
-
+
/**
* The same as {@link #flattenToString()}, but abbreviates the class
* name if it is a suffix of the package. The result can still be used
* with {@link #unflattenFromString(String)}.
- *
+ *
* @return Returns a new String holding the package and class names. This
* is represented as the package name, concatenated with a '/' and then the
* class name.
- *
+ *
* @see #unflattenFromString(String)
*/
public @NonNull String flattenToShortString() {
@@ -250,11 +250,11 @@
* followed by a '.' then the final class name will be the concatenation
* of the package name with the string following the '/'. Thus
* "com.foo/.Blah" becomes package="com.foo" class="com.foo.Blah".
- *
+ *
* @param str The String that was returned by flattenToString().
* @return Returns a new ComponentName containing the package and class
* names that were encoded in <var>str</var>
- *
+ *
* @see #flattenToString()
*/
public static @Nullable ComponentName unflattenFromString(@NonNull String str) {
@@ -269,7 +269,7 @@
}
return new ComponentName(pkg, cls);
}
-
+
/**
* Return string representation of this class without the class's name
* as a prefix.
@@ -283,6 +283,12 @@
return "ComponentInfo{" + mPackage + "/" + mClass + "}";
}
+ /** Put this here so that individual services don't have to reimplement this. @hide */
+ public void toProto(ProtoOutputStream proto) {
+ proto.write(ComponentNameProto.PACKAGE_NAME, mPackage);
+ proto.write(ComponentNameProto.CLASS_NAME, mClass);
+ }
+
@Override
public boolean equals(Object obj) {
try {
@@ -311,7 +317,7 @@
}
return this.mClass.compareTo(that.mClass);
}
-
+
public int describeContents() {
return 0;
}
@@ -324,10 +330,10 @@
/**
* Write a ComponentName to a Parcel, handling null pointers. Must be
* read with {@link #readFromParcel(Parcel)}.
- *
+ *
* @param c The ComponentName to be written.
* @param out The Parcel in which the ComponentName will be placed.
- *
+ *
* @see #readFromParcel(Parcel)
*/
public static void writeToParcel(ComponentName c, Parcel out) {
@@ -337,23 +343,23 @@
out.writeString(null);
}
}
-
+
/**
* Read a ComponentName from a Parcel that was previously written
* with {@link #writeToParcel(ComponentName, Parcel)}, returning either
* a null or new object as appropriate.
- *
+ *
* @param in The Parcel from which to read the ComponentName
* @return Returns a new ComponentName matching the previously written
* object, or null if a null had been written.
- *
+ *
* @see #writeToParcel(ComponentName, Parcel)
*/
public static ComponentName readFromParcel(Parcel in) {
String pkg = in.readString();
return pkg != null ? new ComponentName(pkg, in) : null;
}
-
+
public static final Parcelable.Creator<ComponentName> CREATOR
= new Parcelable.Creator<ComponentName>() {
public ComponentName createFromParcel(Parcel in) {
@@ -371,7 +377,7 @@
* must not use this with data written by
* {@link #writeToParcel(ComponentName, Parcel)} since it is not possible
* to handle a null ComponentObject here.
- *
+ *
* @param in The Parcel containing the previously written ComponentName,
* positioned at the location in the buffer where it was written.
*/
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 9ccc552..02e70f5 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -47,6 +47,8 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.slice.Slice;
+import android.slice.SliceProvider;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -178,6 +180,8 @@
public static final Intent ACTION_SYNC_CONN_STATUS_CHANGED =
new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");
+ /** @hide */
+ public static final String SCHEME_SLICE = "slice";
public static final String SCHEME_CONTENT = "content";
public static final String SCHEME_ANDROID_RESOURCE = "android.resource";
public static final String SCHEME_FILE = "file";
@@ -1718,6 +1722,36 @@
}
/**
+ * Turns a slice Uri into slice content.
+ *
+ * @param uri The URI to a slice provider
+ * @return The Slice provided by the app or null if none is given.
+ * @see Slice
+ * @hide
+ */
+ public final @Nullable Slice bindSlice(@NonNull Uri uri) {
+ Preconditions.checkNotNull(uri, "uri");
+ IContentProvider provider = acquireProvider(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+ final Bundle res = provider.call(mPackageName, SliceProvider.METHOD_SLICE, null,
+ extras);
+ Bundle.setDefusable(res, true);
+ return res.getParcelable(SliceProvider.EXTRA_SLICE);
+ } catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
+ return null;
+ } finally {
+ releaseProvider(provider);
+ }
+ }
+
+ /**
* Returns the content provider for the given content URI.
*
* @param uri The URI to a content provider
@@ -1725,7 +1759,7 @@
* @hide
*/
public final IContentProvider acquireProvider(Uri uri) {
- if (!SCHEME_CONTENT.equals(uri.getScheme())) {
+ if (!SCHEME_CONTENT.equals(uri.getScheme()) && !SCHEME_SLICE.equals(uri.getScheme())) {
return null;
}
final String auth = uri.getAuthority();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 03e4dfe..20fbf04 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -64,6 +64,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.WindowManager;
+import android.view.autofill.AutofillManager.AutofillClient;
import android.view.textclassifier.TextClassificationManager;
import java.io.File;
@@ -3034,6 +3035,9 @@
* <dt> {@link #CONNECTIVITY_SERVICE} ("connection")
* <dd> A {@link android.net.ConnectivityManager ConnectivityManager} for
* handling management of network connections.
+ * <dt> {@link #IPSEC_SERVICE} ("ipsec")
+ * <dd> A {@link android.net.IpSecManager IpSecManager} for managing IPSec on
+ * sockets and networks.
* <dt> {@link #WIFI_SERVICE} ("wifi")
* <dd> A {@link android.net.wifi.WifiManager WifiManager} for management of Wi-Fi
* connectivity. On releases before NYC, it should only be obtained from an application
@@ -3378,7 +3382,6 @@
* {@link android.net.IpSecManager} for encrypting Sockets or Networks with
* IPSec.
*
- * @hide
* @see #getSystemService
*/
public static final String IPSEC_SERVICE = "ipsec";
@@ -3465,6 +3468,19 @@
/**
* Use with {@link #getSystemService} to retrieve a {@link
+ * android.net.wifi.rtt.WifiRttManager} for ranging devices with wifi
+ *
+ * Note: this is a replacement for WIFI_RTT_SERVICE above. It will
+ * be renamed once final implementation in place.
+ *
+ * @see #getSystemService
+ * @see android.net.wifi.rtt.WifiRttManager
+ * @hide
+ */
+ public static final String WIFI_RTT2_SERVICE = "rttmanager2";
+
+ /**
+ * Use with {@link #getSystemService} to retrieve a {@link
* android.net.lowpan.LowpanManager} for handling management of
* LoWPAN access.
*
@@ -4772,6 +4788,19 @@
}
/**
+ * @hide
+ */
+ public AutofillClient getAutofillClient() {
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public void setAutofillClient(AutofillClient client) {
+ }
+
+ /**
* Throws an exception if the Context is using system resources,
* which are non-runtime-overlay-themable and may show inconsistent UI.
* @hide
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index a9fd58b..85acdc6 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -37,6 +37,7 @@
import android.os.UserHandle;
import android.view.Display;
import android.view.DisplayAdjustments;
+import android.view.autofill.AutofillManager.AutofillClient;
import java.io.File;
import java.io.FileInputStream;
@@ -967,7 +968,24 @@
/**
* @hide
*/
+ @Override
public int getNextAutofillId() {
return mBase.getNextAutofillId();
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public AutofillClient getAutofillClient() {
+ return mBase.getAutofillClient();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void setAutofillClient(AutofillClient client) {
+ mBase.setAutofillClient(client);
+ }
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c238ffb..c9ad951 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -9444,7 +9444,7 @@
for (int i=0; i<N; i++) {
char c = data.charAt(i);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
- || c == '.' || c == '-') {
+ || (c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+') {
continue;
}
if (c == ':' && i > 0) {
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index cd8201f..41667c4 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1217,7 +1217,7 @@
* @hide
*/
public static String screenOrientationToString(int orientation) {
- switch(orientation) {
+ switch (orientation) {
case SCREEN_ORIENTATION_UNSET:
return "SCREEN_ORIENTATION_UNSET";
case SCREEN_ORIENTATION_UNSPECIFIED:
@@ -1257,6 +1257,22 @@
}
}
+ /**
+ * @hide
+ */
+ public static String colorModeToString(@ColorMode int colorMode) {
+ switch (colorMode) {
+ case COLOR_MODE_DEFAULT:
+ return "COLOR_MODE_DEFAULT";
+ case COLOR_MODE_WIDE_COLOR_GAMUT:
+ return "COLOR_MODE_WIDE_COLOR_GAMUT";
+ case COLOR_MODE_HDR:
+ return "COLOR_MODE_HDR";
+ default:
+ return Integer.toString(colorMode);
+ }
+ }
+
public static final Parcelable.Creator<ActivityInfo> CREATOR
= new Parcelable.Creator<ActivityInfo>() {
public ActivityInfo createFromParcel(Parcel source) {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 0e70645..1352bc2 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -554,14 +554,6 @@
*/
void reconcileSecondaryDexFiles(String packageName);
- /**
- * Update status of external media on the package manager to scan and
- * install packages installed on the external media. Like say the
- * StorageManagerService uses this to call into the package manager to update
- * status of sdcard.
- */
- void updateExternalMediaStatus(boolean mounted, boolean reportStatus);
-
PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage);
int getMoveStatus(int moveId);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ef8f84b..31ca198 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2330,6 +2330,16 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports Wi-Fi RTT (IEEE 802.11mc).
+ *
+ * @hide RTT_API
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_WIFI_RTT = "android.hardware.wifi.rtt";
+
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports LoWPAN networking.
* @hide
*/
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 4c981cd..be7f921 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -16,6 +16,9 @@
package android.content.pm;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager.ApplicationInfoFlags;
@@ -25,6 +28,8 @@
import android.os.Bundle;
import android.util.SparseArray;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -33,6 +38,20 @@
* @hide Only for use within the system server.
*/
public abstract class PackageManagerInternal {
+ public static final int PACKAGE_SYSTEM = 0;
+ public static final int PACKAGE_SETUP_WIZARD = 1;
+ public static final int PACKAGE_INSTALLER = 2;
+ public static final int PACKAGE_VERIFIER = 3;
+ public static final int PACKAGE_BROWSER = 4;
+ @IntDef(value = {
+ PACKAGE_SYSTEM,
+ PACKAGE_SETUP_WIZARD,
+ PACKAGE_INSTALLER,
+ PACKAGE_VERIFIER,
+ PACKAGE_BROWSER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface KnownPackage {}
/**
* Provider for package names.
@@ -172,6 +191,13 @@
@ResolveInfoFlags int flags, int filterCallingUid, int userId);
/**
+ * Retrieve all services that can be performed for the given intent.
+ * @see PackageManager#queryIntentServices(Intent, int)
+ */
+ public abstract List<ResolveInfo> queryIntentServices(
+ Intent intent, int flags, int callingUid, int userId);
+
+ /**
* Interface to {@link com.android.server.pm.PackageManagerService#getHomeActivitiesAsUser}.
*/
public abstract ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
@@ -343,14 +369,19 @@
* Resolves an activity intent, allowing instant apps to be resolved.
*/
public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType,
- int flags, int userId);
+ int flags, int userId, boolean resolveForStart);
/**
* Resolves a service intent, allowing instant apps to be resolved.
*/
- public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
+ public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
int flags, int userId, int callingUid);
+ /**
+ * Resolves a content provider intent.
+ */
+ public abstract ProviderInfo resolveContentProvider(String name, int flags, int userId);
+
/**
* Track the creator of a new isolated uid.
* @param isolatedUid The newly created isolated uid.
@@ -383,4 +414,59 @@
* Updates a package last used time.
*/
public abstract void notifyPackageUse(String packageName, int reason);
+
+ /**
+ * Returns a package object for the given package name.
+ */
+ public abstract @Nullable PackageParser.Package getPackage(@NonNull String packageName);
+
+ /**
+ * Returns a package object for the disabled system package name.
+ */
+ public abstract @Nullable PackageParser.Package getDisabledPackage(@NonNull String packageName);
+
+ /**
+ * Returns whether or not the component is the resolver activity.
+ */
+ public abstract boolean isResolveActivityComponent(@NonNull ComponentInfo component);
+
+ /**
+ * Returns the package name for a known package.
+ */
+ public abstract @Nullable String getKnownPackageName(
+ @KnownPackage int knownPackage, int userId);
+
+ /**
+ * Returns whether the package is an instant app.
+ */
+ public abstract boolean isInstantApp(String packageName, int userId);
+
+ /**
+ * Returns whether the package is an instant app.
+ */
+ public abstract @Nullable String getInstantAppPackageName(int uid);
+
+ /**
+ * Returns whether or not access to the application should be filtered.
+ * <p>
+ * Access may be limited based upon whether the calling or target applications
+ * are instant applications.
+ *
+ * @see #canAccessInstantApps(int)
+ */
+ public abstract boolean filterAppAccess(
+ @Nullable PackageParser.Package pkg, int callingUid, int userId);
+
+ /*
+ * NOTE: The following methods are temporary until permissions are extracted from
+ * the package manager into a component specifically for handling permissions.
+ */
+ /** Returns the flags for the given permission. */
+ public abstract @Nullable int getPermissionFlagsTEMP(@NonNull String permName,
+ @NonNull String packageName, int userId);
+ /** Updates the flags for the given permission. */
+ public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
+ @NonNull String packageName, int flagMask, int flagValues, int userId);
+ /** temporary until mPermissionTrees is moved to PermissionManager */
+ public abstract Object enforcePermissionTreeTEMP(@NonNull String permName, int callingUid);
}
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 17b4f87..b45c26c 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -318,16 +318,19 @@
return null;
}
+ @Override
public String toString() {
return "PermissionInfo{"
+ Integer.toHexString(System.identityHashCode(this))
+ " " + name + "}";
}
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int parcelableFlags) {
super.writeToParcel(dest, parcelableFlags);
dest.writeInt(protectionLevel);
@@ -338,11 +341,25 @@
TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
}
+ /** @hide */
+ public int calculateFootprint() {
+ int size = name.length();
+ if (nonLocalizedLabel != null) {
+ size += nonLocalizedLabel.length();
+ }
+ if (nonLocalizedDescription != null) {
+ size += nonLocalizedDescription.length();
+ }
+ return size;
+ }
+
public static final Creator<PermissionInfo> CREATOR =
new Creator<PermissionInfo>() {
+ @Override
public PermissionInfo createFromParcel(Parcel source) {
return new PermissionInfo(source);
}
+ @Override
public PermissionInfo[] newArray(int size) {
return new PermissionInfo[size];
}
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index d3a3560..6b9c753 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -1763,21 +1763,43 @@
return 0;
}
+
/**
* Return a string representation, intended for logging. Some fields will be retracted.
*/
@Override
public String toString() {
- return toStringInner(/* secure =*/ true, /* includeInternalData =*/ false);
+ return toStringInner(/* secure =*/ true, /* includeInternalData =*/ false,
+ /*indent=*/ null);
}
/** @hide */
public String toInsecureString() {
- return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true);
+ return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true,
+ /*indent=*/ null);
}
- private String toStringInner(boolean secure, boolean includeInternalData) {
+ /** @hide */
+ public String toDumpString(String indent) {
+ return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true, indent);
+ }
+
+ private void addIndentOrComma(StringBuilder sb, String indent) {
+ if (indent != null) {
+ sb.append("\n ");
+ sb.append(indent);
+ } else {
+ sb.append(", ");
+ }
+ }
+
+ private String toStringInner(boolean secure, boolean includeInternalData, String indent) {
final StringBuilder sb = new StringBuilder();
+
+ if (indent != null) {
+ sb.append(indent);
+ }
+
sb.append("ShortcutInfo {");
sb.append("id=");
@@ -1787,47 +1809,51 @@
sb.append(Integer.toHexString(mFlags));
sb.append(" [");
if (!isEnabled()) {
- sb.append("X");
+ sb.append("Dis");
}
if (isImmutable()) {
sb.append("Im");
}
if (isManifestShortcut()) {
- sb.append("M");
+ sb.append("Man");
}
if (isDynamic()) {
- sb.append("D");
+ sb.append("Dyn");
}
if (isPinned()) {
- sb.append("P");
+ sb.append("Pin");
}
if (hasIconFile()) {
- sb.append("If");
+ sb.append("Ic-f");
}
if (isIconPendingSave()) {
- sb.append("^");
+ sb.append("Pens");
}
if (hasIconResource()) {
- sb.append("Ir");
+ sb.append("Ic-r");
}
if (hasKeyFieldsOnly()) {
- sb.append("K");
+ sb.append("Key");
}
if (hasStringResourcesResolved()) {
- sb.append("Sr");
+ sb.append("Str");
}
if (isReturnedByServer()) {
- sb.append("V");
+ sb.append("Rets");
}
sb.append("]");
- sb.append(", packageName=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("packageName=");
sb.append(mPackageName);
sb.append(", activity=");
sb.append(mActivity);
- sb.append(", shortLabel=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("shortLabel=");
sb.append(secure ? "***" : mTitle);
sb.append(", resId=");
sb.append(mTitleResId);
@@ -1835,7 +1861,9 @@
sb.append(mTitleResName);
sb.append("]");
- sb.append(", longLabel=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("longLabel=");
sb.append(secure ? "***" : mText);
sb.append(", resId=");
sb.append(mTextResId);
@@ -1843,7 +1871,9 @@
sb.append(mTextResName);
sb.append("]");
- sb.append(", disabledMessage=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("disabledMessage=");
sb.append(secure ? "***" : mDisabledMessage);
sb.append(", resId=");
sb.append(mDisabledMessageResId);
@@ -1851,19 +1881,27 @@
sb.append(mDisabledMessageResName);
sb.append("]");
- sb.append(", categories=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("categories=");
sb.append(mCategories);
- sb.append(", icon=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("icon=");
sb.append(mIcon);
- sb.append(", rank=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("rank=");
sb.append(mRank);
sb.append(", timestamp=");
sb.append(mLastChangedTimestamp);
- sb.append(", intents=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("intents=");
if (mIntents == null) {
sb.append("null");
} else {
@@ -1885,12 +1923,15 @@
}
}
- sb.append(", extras=");
+ addIndentOrComma(sb, indent);
+
+ sb.append("extras=");
sb.append(mExtras);
if (includeInternalData) {
+ addIndentOrComma(sb, indent);
- sb.append(", iconRes=");
+ sb.append("iconRes=");
sb.append(mIconResId);
sb.append("[");
sb.append(mIconResName);
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index aa35a66..931b5c9 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -16,10 +16,11 @@
package android.hardware;
-import android.app.ActivityThread;
+import static android.system.OsConstants.*;
+
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.app.job.JobInfo;
+import android.app.ActivityThread;
import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.Point;
@@ -34,11 +35,11 @@
import android.os.ServiceManager;
import android.renderscript.Allocation;
import android.renderscript.Element;
-import android.renderscript.RenderScript;
import android.renderscript.RSIllegalArgumentException;
+import android.renderscript.RenderScript;
import android.renderscript.Type;
-import android.util.Log;
import android.text.TextUtils;
+import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
@@ -48,8 +49,6 @@
import java.util.LinkedHashMap;
import java.util.List;
-import static android.system.OsConstants.*;
-
/**
* The Camera class is used to set image capture settings, start/stop preview,
* snap pictures, and retrieve frames for encoding for video. This class is a
@@ -243,12 +242,19 @@
/**
* Returns the number of physical cameras available on this device.
+ *
+ * @return total number of accessible camera devices, or 0 if there are no
+ * cameras or an error was encountered enumerating them.
*/
public native static int getNumberOfCameras();
/**
* Returns the information about a particular camera.
* If {@link #getNumberOfCameras()} returns N, the valid id is 0 to N-1.
+ *
+ * @throws RuntimeException if an invalid ID is provided, or if there is an
+ * error retrieving the information (generally due to a hardware or other
+ * low-level failure).
*/
public static void getCameraInfo(int cameraId, CameraInfo cameraInfo) {
_getCameraInfo(cameraId, cameraInfo);
@@ -362,7 +368,10 @@
/**
* Creates a new Camera object to access the first back-facing camera on the
* device. If the device does not have a back-facing camera, this returns
- * null.
+ * null. Otherwise acts like the {@link #open(int)} call.
+ *
+ * @return a new Camera object for the first back-facing camera, or null if there is no
+ * backfacing camera
* @see #open(int)
*/
public static Camera open() {
@@ -609,6 +618,8 @@
*
* @throws IOException if a connection cannot be re-established (for
* example, if the camera is still in use by another process).
+ * @throws RuntimeException if release() has been called on this Camera
+ * instance.
*/
public native final void reconnect() throws IOException;
@@ -637,6 +648,8 @@
* or null to remove the preview surface
* @throws IOException if the method fails (for example, if the surface
* is unavailable or unsuitable).
+ * @throws RuntimeException if release() has been called on this Camera
+ * instance.
*/
public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {
if (holder != null) {
@@ -684,6 +697,8 @@
* texture
* @throws IOException if the method fails (for example, if the surface
* texture is unavailable or unsuitable).
+ * @throws RuntimeException if release() has been called on this Camera
+ * instance.
*/
public native final void setPreviewTexture(SurfaceTexture surfaceTexture) throws IOException;
@@ -733,12 +748,20 @@
* {@link #setPreviewCallbackWithBuffer(Camera.PreviewCallback)} were
* called, {@link Camera.PreviewCallback#onPreviewFrame(byte[], Camera)}
* will be called when preview data becomes available.
+ *
+ * @throws RuntimeException if starting preview fails; usually this would be
+ * because of a hardware or other low-level error, or because release()
+ * has been called on this Camera instance.
*/
public native final void startPreview();
/**
* Stops capturing and drawing preview frames to the surface, and
* resets the camera for a future call to {@link #startPreview()}.
+ *
+ * @throws RuntimeException if stopping preview fails; usually this would be
+ * because of a hardware or other low-level error, or because release()
+ * has been called on this Camera instance.
*/
public final void stopPreview() {
_stopPreview();
@@ -777,6 +800,8 @@
*
* @param cb a callback object that receives a copy of each preview frame,
* or null to stop receiving callbacks.
+ * @throws RuntimeException if release() has been called on this Camera
+ * instance.
* @see android.media.MediaActionSound
*/
public final void setPreviewCallback(PreviewCallback cb) {
@@ -803,6 +828,8 @@
*
* @param cb a callback object that receives a copy of the next preview frame,
* or null to stop receiving callbacks.
+ * @throws RuntimeException if release() has been called on this Camera
+ * instance.
* @see android.media.MediaActionSound
*/
public final void setOneShotPreviewCallback(PreviewCallback cb) {
@@ -840,6 +867,8 @@
*
* @param cb a callback object that receives a copy of the preview frame,
* or null to stop receiving callbacks and clear the buffer queue.
+ * @throws RuntimeException if release() has been called on this Camera
+ * instance.
* @see #addCallbackBuffer(byte[])
* @see android.media.MediaActionSound
*/
@@ -1259,6 +1288,9 @@
* success sound to the user.</p>
*
* @param cb the callback to run
+ * @throws RuntimeException if starting autofocus fails; usually this would
+ * be because of a hardware or other low-level error, or because
+ * release() has been called on this Camera instance.
* @see #cancelAutoFocus()
* @see android.hardware.Camera.Parameters#setAutoExposureLock(boolean)
* @see android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean)
@@ -1279,6 +1311,9 @@
* this function will return the focus position to the default.
* If the camera does not support auto-focus, this is a no-op.
*
+ * @throws RuntimeException if canceling autofocus fails; usually this would
+ * be because of a hardware or other low-level error, or because
+ * release() has been called on this Camera instance.
* @see #autoFocus(Camera.AutoFocusCallback)
*/
public final void cancelAutoFocus()
@@ -1333,6 +1368,9 @@
* Sets camera auto-focus move callback.
*
* @param cb the callback to run
+ * @throws RuntimeException if enabling the focus move callback fails;
+ * usually this would be because of a hardware or other low-level error,
+ * or because release() has been called on this Camera instance.
*/
public void setAutoFocusMoveCallback(AutoFocusMoveCallback cb) {
mAutoFocusMoveCallback = cb;
@@ -1384,7 +1422,7 @@
};
/**
- * Equivalent to takePicture(shutter, raw, null, jpeg).
+ * Equivalent to <pre>takePicture(Shutter, raw, null, jpeg)</pre>.
*
* @see #takePicture(ShutterCallback, PictureCallback, PictureCallback, PictureCallback)
*/
@@ -1422,6 +1460,9 @@
* @param raw the callback for raw (uncompressed) image data, or null
* @param postview callback with postview image data, may be null
* @param jpeg the callback for JPEG image data, or null
+ * @throws RuntimeException if starting picture capture fails; usually this
+ * would be because of a hardware or other low-level error, or because
+ * release() has been called on this Camera instance.
*/
public final void takePicture(ShutterCallback shutter, PictureCallback raw,
PictureCallback postview, PictureCallback jpeg) {
@@ -1534,6 +1575,9 @@
*
* @param degrees the angle that the picture will be rotated clockwise.
* Valid values are 0, 90, 180, and 270.
+ * @throws RuntimeException if setting orientation fails; usually this would
+ * be because of a hardware or other low-level error, or because
+ * release() has been called on this Camera instance.
* @see #setPreviewDisplay(SurfaceHolder)
*/
public native final void setDisplayOrientation(int degrees);
@@ -1559,6 +1603,9 @@
* changed. {@code false} if the shutter sound state could not be
* changed. {@code true} is also returned if shutter sound playback
* is already set to the requested state.
+ * @throws RuntimeException if the call fails; usually this would be because
+ * of a hardware or other low-level error, or because release() has been
+ * called on this Camera instance.
* @see #takePicture
* @see CameraInfo#canDisableShutterSound
* @see ShutterCallback
@@ -1903,6 +1950,9 @@
* If modifications are made to the returned Parameters, they must be passed
* to {@link #setParameters(Camera.Parameters)} to take effect.
*
+ * @throws RuntimeException if reading parameters fails; usually this would
+ * be because of a hardware or other low-level error, or because
+ * release() has been called on this Camera instance.
* @see #setParameters(Camera.Parameters)
*/
public Parameters getParameters() {
diff --git a/core/java/android/hardware/LegacySensorManager.java b/core/java/android/hardware/LegacySensorManager.java
index f5cf3f7..098121d 100644
--- a/core/java/android/hardware/LegacySensorManager.java
+++ b/core/java/android/hardware/LegacySensorManager.java
@@ -204,7 +204,7 @@
}
private static final class LegacyListener implements SensorEventListener {
- private float mValues[] = new float[6];
+ private float[] mValues = new float[6];
private SensorListener mTarget;
private int mSensors;
private final LmsFilter mYawfilter = new LmsFilter();
@@ -256,7 +256,7 @@
}
public void onSensorChanged(SensorEvent event) {
- final float v[] = mValues;
+ final float[] v = mValues;
v[0] = event.values[0];
v[1] = event.values[1];
v[2] = event.values[2];
@@ -264,10 +264,10 @@
int legacyType = getLegacySensorType(type);
mapSensorDataToWindow(legacyType, v, LegacySensorManager.getRotation());
if (type == Sensor.TYPE_ORIENTATION) {
- if ((mSensors & SensorManager.SENSOR_ORIENTATION_RAW)!=0) {
+ if ((mSensors & SensorManager.SENSOR_ORIENTATION_RAW) != 0) {
mTarget.onSensorChanged(SensorManager.SENSOR_ORIENTATION_RAW, v);
}
- if ((mSensors & SensorManager.SENSOR_ORIENTATION)!=0) {
+ if ((mSensors & SensorManager.SENSOR_ORIENTATION) != 0) {
v[0] = mYawfilter.filter(event.timestamp, v[0]);
mTarget.onSensorChanged(SensorManager.SENSOR_ORIENTATION, v);
}
@@ -317,7 +317,7 @@
switch (sensor) {
case SensorManager.SENSOR_ACCELEROMETER:
case SensorManager.SENSOR_MAGNETIC_FIELD:
- values[0] =-y;
+ values[0] = -y;
values[1] = x;
values[2] = z;
break;
@@ -337,15 +337,15 @@
switch (sensor) {
case SensorManager.SENSOR_ACCELEROMETER:
case SensorManager.SENSOR_MAGNETIC_FIELD:
- values[0] =-x;
- values[1] =-y;
+ values[0] = -x;
+ values[1] = -y;
values[2] = z;
break;
case SensorManager.SENSOR_ORIENTATION:
case SensorManager.SENSOR_ORIENTATION_RAW:
values[0] = (x >= 180) ? (x - 180) : (x + 180);
- values[1] =-y;
- values[2] =-z;
+ values[1] = -y;
+ values[2] = -z;
break;
}
}
@@ -369,10 +369,11 @@
private static final class LmsFilter {
private static final int SENSORS_RATE_MS = 20;
private static final int COUNT = 12;
- private static final float PREDICTION_RATIO = 1.0f/3.0f;
- private static final float PREDICTION_TIME = (SENSORS_RATE_MS*COUNT/1000.0f)*PREDICTION_RATIO;
- private float mV[] = new float[COUNT*2];
- private long mT[] = new long[COUNT*2];
+ private static final float PREDICTION_RATIO = 1.0f / 3.0f;
+ private static final float PREDICTION_TIME =
+ (SENSORS_RATE_MS * COUNT / 1000.0f) * PREDICTION_RATIO;
+ private float[] mV = new float[COUNT * 2];
+ private long[] mT = new long[COUNT * 2];
private int mIndex;
public LmsFilter() {
@@ -383,9 +384,9 @@
float v = in;
final float ns = 1.0f / 1000000000.0f;
float v1 = mV[mIndex];
- if ((v-v1) > 180) {
+ if ((v - v1) > 180) {
v -= 360;
- } else if ((v1-v) > 180) {
+ } else if ((v1 - v) > 180) {
v += 360;
}
/* Manage the circular buffer, we write the data twice spaced
@@ -393,40 +394,43 @@
* when it's full
*/
mIndex++;
- if (mIndex >= COUNT*2)
+ if (mIndex >= COUNT * 2) {
mIndex = COUNT;
+ }
mV[mIndex] = v;
mT[mIndex] = time;
- mV[mIndex-COUNT] = v;
- mT[mIndex-COUNT] = time;
+ mV[mIndex - COUNT] = v;
+ mT[mIndex - COUNT] = time;
float A, B, C, D, E;
float a, b;
int i;
A = B = C = D = E = 0;
- for (i=0 ; i<COUNT-1 ; i++) {
+ for (i = 0; i < COUNT - 1; i++) {
final int j = mIndex - 1 - i;
final float Z = mV[j];
- final float T = (mT[j]/2 + mT[j+1]/2 - time)*ns;
- float dT = (mT[j] - mT[j+1])*ns;
+ final float T = (mT[j] / 2 + mT[j + 1] / 2 - time) * ns;
+ float dT = (mT[j] - mT[j + 1]) * ns;
dT *= dT;
- A += Z*dT;
- B += T*(T*dT);
- C += (T*dT);
- D += Z*(T*dT);
+ A += Z * dT;
+ B += T * (T * dT);
+ C += (T * dT);
+ D += Z * (T * dT);
E += dT;
}
- b = (A*B + C*D) / (E*B + C*C);
- a = (E*b - A) / C;
- float f = b + PREDICTION_TIME*a;
+ b = (A * B + C * D) / (E * B + C * C);
+ a = (E * b - A) / C;
+ float f = b + PREDICTION_TIME * a;
// Normalize
f *= (1.0f / 360.0f);
- if (((f>=0)?f:-f) >= 0.5f)
- f = f - (float)Math.ceil(f + 0.5f) + 1.0f;
- if (f < 0)
+ if (((f >= 0) ? f : -f) >= 0.5f) {
+ f = f - (float) Math.ceil(f + 0.5f) + 1.0f;
+ }
+ if (f < 0) {
f += 1.0f;
+ }
f *= 360.0f;
return f;
}
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index f02e484..7fb0c89 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -794,12 +794,12 @@
1, // SENSOR_TYPE_PICK_UP_GESTURE
1, // SENSOR_TYPE_WRIST_TILT_GESTURE
1, // SENSOR_TYPE_DEVICE_ORIENTATION
- 16,// SENSOR_TYPE_POSE_6DOF
+ 16, // SENSOR_TYPE_POSE_6DOF
1, // SENSOR_TYPE_STATIONARY_DETECT
1, // SENSOR_TYPE_MOTION_DETECT
1, // SENSOR_TYPE_HEART_BEAT
2, // SENSOR_TYPE_DYNAMIC_SENSOR_META
- 16,// skip over additional sensor info type
+ 16, // skip over additional sensor info type
1, // SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT
6, // SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED
};
@@ -857,8 +857,8 @@
static int getMaxLengthValuesArray(Sensor sensor, int sdkLevel) {
// RotationVector length has changed to 3 to 5 for API level 18
// Set it to 3 for backward compatibility.
- if (sensor.mType == Sensor.TYPE_ROTATION_VECTOR &&
- sdkLevel <= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ if (sensor.mType == Sensor.TYPE_ROTATION_VECTOR
+ && sdkLevel <= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return 3;
}
int offset = sensor.mType;
@@ -1033,9 +1033,9 @@
* Returns true if the sensor is a wake-up sensor.
* <p>
* <b>Application Processor Power modes</b> <p>
- * Application Processor(AP), is the processor on which applications run. When no wake lock is held
- * and the user is not interacting with the device, this processor can enter a “Suspend” mode,
- * reducing the power consumption by 10 times or more.
+ * Application Processor(AP), is the processor on which applications run. When no wake lock is
+ * held and the user is not interacting with the device, this processor can enter a “Suspend”
+ * mode, reducing the power consumption by 10 times or more.
* </p>
* <p>
* <b>Non-wake-up sensors</b> <p>
@@ -1232,6 +1232,6 @@
*/
private void setUuid(long msb, long lsb) {
// TODO(b/29547335): Rename this method to setId.
- mId = (int)msb;
+ mId = (int) msb;
}
}
diff --git a/core/java/android/hardware/SensorAdditionalInfo.java b/core/java/android/hardware/SensorAdditionalInfo.java
index 0c6a415..7c876cf 100644
--- a/core/java/android/hardware/SensorAdditionalInfo.java
+++ b/core/java/android/hardware/SensorAdditionalInfo.java
@@ -200,7 +200,7 @@
public static final int TYPE_DEBUG_INFO = 0x40000000;
SensorAdditionalInfo(
- Sensor aSensor, int aType, int aSerial, int [] aIntValues, float [] aFloatValues) {
+ Sensor aSensor, int aType, int aSerial, int[] aIntValues, float[] aFloatValues) {
sensor = aSensor;
type = aType;
serial = aSerial;
@@ -222,10 +222,10 @@
null, new float[] { strength, declination, inclination});
}
/** @hide */
- public static SensorAdditionalInfo createCustomInfo(Sensor aSensor, int type, float [] data) {
+ public static SensorAdditionalInfo createCustomInfo(Sensor aSensor, int type, float[] data) {
if (type < TYPE_CUSTOM_INFO || type >= TYPE_DEBUG_INFO || aSensor == null) {
- throw new IllegalArgumentException("invalid parameter(s): type: " + type +
- "; sensor: " + aSensor);
+ throw new IllegalArgumentException(
+ "invalid parameter(s): type: " + type + "; sensor: " + aSensor);
}
return new SensorAdditionalInfo(aSensor, type, 0, null, data);
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index c0bca97..bbd04a3 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -207,8 +207,8 @@
* timestamp = event.timestamp;
* float[] deltaRotationMatrix = new float[9];
* SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
- * // User code should concatenate the delta rotation we computed with the current rotation
- * // in order to get the updated rotation.
+ * // User code should concatenate the delta rotation we computed with the current
+ * // rotation in order to get the updated rotation.
* // rotationCurrent = rotationCurrent * deltaRotationMatrix;
* }
* </pre>
@@ -244,21 +244,22 @@
* <h4>{@link android.hardware.Sensor#TYPE_GRAVITY Sensor.TYPE_GRAVITY}:</h4>
* <p>A three dimensional vector indicating the direction and magnitude of gravity. Units
* are m/s^2. The coordinate system is the same as is used by the acceleration sensor.</p>
- * <p><b>Note:</b> When the device is at rest, the output of the gravity sensor should be identical
- * to that of the accelerometer.</p>
+ * <p><b>Note:</b> When the device is at rest, the output of the gravity sensor should be
+ * identical to that of the accelerometer.</p>
*
- * <h4>{@link android.hardware.Sensor#TYPE_LINEAR_ACCELERATION Sensor.TYPE_LINEAR_ACCELERATION}:</h4>
- * A three dimensional vector indicating acceleration along each device axis, not including
- * gravity. All values have units of m/s^2. The coordinate system is the same as is used by the
- * acceleration sensor.
+ * <h4>
+ * {@link android.hardware.Sensor#TYPE_LINEAR_ACCELERATION Sensor.TYPE_LINEAR_ACCELERATION}:
+ * </h4> A three dimensional vector indicating acceleration along each device axis, not
+ * including gravity. All values have units of m/s^2. The coordinate system is the same as is
+ * used by the acceleration sensor.
* <p>The output of the accelerometer, gravity and linear-acceleration sensors must obey the
* following relation:</p>
- * <p><ul>acceleration = gravity + linear-acceleration</ul></p>
+ * <p><ul>acceleration = gravity + linear-acceleration</ul></p>
*
* <h4>{@link android.hardware.Sensor#TYPE_ROTATION_VECTOR Sensor.TYPE_ROTATION_VECTOR}:</h4>
- * <p>The rotation vector represents the orientation of the device as a combination of an <i>angle</i>
- * and an <i>axis</i>, in which the device has rotated through an angle θ around an axis
- * <x, y, z>.</p>
+ * <p>The rotation vector represents the orientation of the device as a combination of an
+ * <i>angle</i> and an <i>axis</i>, in which the device has rotated through an angle θ
+ * around an axis <x, y, z>.</p>
* <p>The three elements of the rotation vector are
* <x*sin(θ/2), y*sin(θ/2), z*sin(θ/2)>, such that the magnitude of the rotation
* vector is equal to sin(θ/2), and the direction of the rotation vector is equal to the
diff --git a/core/java/android/hardware/SensorListener.java b/core/java/android/hardware/SensorListener.java
index c71e968..e2033b6 100644
--- a/core/java/android/hardware/SensorListener.java
+++ b/core/java/android/hardware/SensorListener.java
@@ -19,8 +19,8 @@
/**
* Used for receiving notifications from the SensorManager when
* sensor values have changed.
- *
- * @deprecated Use
+ *
+ * @deprecated Use
* {@link android.hardware.SensorEventListener SensorEventListener} instead.
*/
@Deprecated
@@ -36,7 +36,7 @@
* <p><u>Definition of the coordinate system used below.</u><p>
* <p>The X axis refers to the screen's horizontal axis
* (the small edge in portrait mode, the long edge in landscape mode) and
- * points to the right.
+ * points to the right.
* <p>The Y axis refers to the screen's vertical axis and points towards
* the top of the screen (the origin is in the lower-left corner).
* <p>The Z axis points toward the sky when the device is lying on its back
@@ -44,18 +44,18 @@
* <p> <b>IMPORTANT NOTE:</b> The axis <b><u>are swapped</u></b> when the
* device's screen orientation changes. To access the unswapped values,
* use indices 3, 4 and 5 in values[].
- *
+ *
* <p>{@link android.hardware.SensorManager#SENSOR_ORIENTATION SENSOR_ORIENTATION},
* {@link android.hardware.SensorManager#SENSOR_ORIENTATION_RAW SENSOR_ORIENTATION_RAW}:<p>
* All values are angles in degrees.
- *
+ *
* <p>values[0]: Azimuth, rotation around the Z axis (0<=azimuth<360).
* 0 = North, 90 = East, 180 = South, 270 = West
- *
+ *
* <p>values[1]: Pitch, rotation around X axis (-180<=pitch<=180), with positive
* values when the z-axis moves toward the y-axis.
*
- * <p>values[2]: Roll, rotation around Y axis (-90<=roll<=90), with positive values
+ * <p>values[2]: Roll, rotation around Y axis (-90<=roll<=90), with positive values
* when the z-axis moves toward the x-axis.
*
* <p>Note that this definition of yaw, pitch and roll is different from the
@@ -64,17 +64,17 @@
*
* <p>{@link android.hardware.SensorManager#SENSOR_ACCELEROMETER SENSOR_ACCELEROMETER}:<p>
* All values are in SI units (m/s^2) and measure contact forces.
- *
- * <p>values[0]: force applied by the device on the x-axis
- * <p>values[1]: force applied by the device on the y-axis
+ *
+ * <p>values[0]: force applied by the device on the x-axis
+ * <p>values[1]: force applied by the device on the y-axis
* <p>values[2]: force applied by the device on the z-axis
- *
+ *
* <p><u>Examples</u>:
* <li>When the device is pushed on its left side toward the right, the
* x acceleration value is negative (the device applies a reaction force
* to the push toward the left)</li>
- *
- * <li>When the device lies flat on a table, the acceleration value is
+ *
+ * <li>When the device lies flat on a table, the acceleration value is
* {@link android.hardware.SensorManager#STANDARD_GRAVITY -STANDARD_GRAVITY},
* which correspond to the force the device applies on the table in reaction
* to gravity.</li>
@@ -83,7 +83,7 @@
* All values are in micro-Tesla (uT) and measure the ambient magnetic
* field in the X, Y and -Z axis.
* <p><b><u>Note:</u></b> the magnetic field's Z axis is inverted.
- *
+ *
* @param sensor The ID of the sensor being monitored
* @param values The new values for the sensor.
*/
@@ -97,5 +97,5 @@
* @param sensor The ID of the sensor being monitored
* @param accuracy The new accuracy of this sensor.
*/
- public void onAccuracyChanged(int sensor, int accuracy);
+ public void onAccuracyChanged(int sensor, int accuracy);
}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index e1cd451..35aaf78 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -83,7 +83,7 @@
/** @hide */
protected static final String TAG = "SensorManager";
- private static final float[] mTempMatrix = new float[16];
+ private static final float[] sTempMatrix = new float[16];
// Cached lists of sensors by type. Guarded by mSensorListByType.
private final SparseArray<List<Sensor>> mSensorListByType =
@@ -188,7 +188,7 @@
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
- public static final int SENSOR_MAX = ((SENSOR_ALL + 1)>>1);
+ public static final int SENSOR_MAX = ((SENSOR_ALL + 1) >> 1);
/**
@@ -425,8 +425,9 @@
} else {
list = new ArrayList<Sensor>();
for (Sensor i : fullList) {
- if (i.getType() == type)
+ if (i.getType() == type) {
list.add(i);
+ }
}
}
list = Collections.unmodifiableList(list);
@@ -461,8 +462,9 @@
} else {
List<Sensor> list = new ArrayList();
for (Sensor i : fullList) {
- if (i.getType() == type)
+ if (i.getType() == type) {
list.add(i);
+ }
}
return Collections.unmodifiableList(list);
}
@@ -490,10 +492,11 @@
// For the following sensor types, return a wake-up sensor. These types are by default
// defined as wake-up sensors. For the rest of the SDK defined sensor types return a
// non_wake-up version.
- if (type == Sensor.TYPE_PROXIMITY || type == Sensor.TYPE_SIGNIFICANT_MOTION ||
- type == Sensor.TYPE_TILT_DETECTOR || type == Sensor.TYPE_WAKE_GESTURE ||
- type == Sensor.TYPE_GLANCE_GESTURE || type == Sensor.TYPE_PICK_UP_GESTURE ||
- type == Sensor.TYPE_WRIST_TILT_GESTURE || type == Sensor.TYPE_DYNAMIC_SENSOR_META) {
+ if (type == Sensor.TYPE_PROXIMITY || type == Sensor.TYPE_SIGNIFICANT_MOTION
+ || type == Sensor.TYPE_TILT_DETECTOR || type == Sensor.TYPE_WAKE_GESTURE
+ || type == Sensor.TYPE_GLANCE_GESTURE || type == Sensor.TYPE_PICK_UP_GESTURE
+ || type == Sensor.TYPE_WRIST_TILT_GESTURE
+ || type == Sensor.TYPE_DYNAMIC_SENSOR_META) {
wakeUpSensor = true;
}
@@ -509,12 +512,12 @@
* <p>
* For example,
* <ul>
- * <li>getDefaultSensor({@link Sensor#TYPE_ACCELEROMETER}, true) returns a wake-up accelerometer
- * sensor if it exists. </li>
- * <li>getDefaultSensor({@link Sensor#TYPE_PROXIMITY}, false) returns a non wake-up proximity
- * sensor if it exists. </li>
- * <li>getDefaultSensor({@link Sensor#TYPE_PROXIMITY}, true) returns a wake-up proximity sensor
- * which is the same as the Sensor returned by {@link #getDefaultSensor(int)}. </li>
+ * <li>getDefaultSensor({@link Sensor#TYPE_ACCELEROMETER}, true) returns a wake-up
+ * accelerometer sensor if it exists. </li>
+ * <li>getDefaultSensor({@link Sensor#TYPE_PROXIMITY}, false) returns a non wake-up
+ * proximity sensor if it exists. </li>
+ * <li>getDefaultSensor({@link Sensor#TYPE_PROXIMITY}, true) returns a wake-up proximity
+ * sensor which is the same as the Sensor returned by {@link #getDefaultSensor(int)}. </li>
* </ul>
* </p>
* <p class="note">
@@ -532,8 +535,9 @@
public Sensor getDefaultSensor(int type, boolean wakeUp) {
List<Sensor> l = getSensorList(type);
for (Sensor sensor : l) {
- if (sensor.isWakeUpSensor() == wakeUp)
+ if (sensor.isWakeUpSensor() == wakeUp) {
return sensor;
+ }
}
return null;
}
@@ -842,8 +846,8 @@
* @return <code>true</code> if the sensor is supported and successfully enabled.
* @see #registerListener(SensorEventListener, Sensor, int, int)
*/
- public boolean registerListener(SensorEventListener listener, Sensor sensor, int samplingPeriodUs,
- int maxReportLatencyUs, Handler handler) {
+ public boolean registerListener(SensorEventListener listener, Sensor sensor,
+ int samplingPeriodUs, int maxReportLatencyUs, Handler handler) {
int delayUs = getDelay(samplingPeriodUs);
return registerListenerImpl(listener, sensor, delayUs, handler, maxReportLatencyUs, 0);
}
@@ -953,7 +957,7 @@
* Used for receiving notifications from the SensorManager when dynamic sensors are connected or
* disconnected.
*/
- public static abstract class DynamicSensorCallback {
+ public abstract static class DynamicSensorCallback {
/**
* Called when there is a dynamic sensor being connected to the system.
*
@@ -1180,7 +1184,7 @@
float Ay = gravity[1];
float Az = gravity[2];
- final float normsqA = (Ax*Ax + Ay*Ay + Az*Az);
+ final float normsqA = (Ax * Ax + Ay * Ay + Az * Az);
final float g = 9.81f;
final float freeFallGravitySquared = 0.01f * g * g;
if (normsqA < freeFallGravitySquared) {
@@ -1191,10 +1195,10 @@
final float Ex = geomagnetic[0];
final float Ey = geomagnetic[1];
final float Ez = geomagnetic[2];
- float Hx = Ey*Az - Ez*Ay;
- float Hy = Ez*Ax - Ex*Az;
- float Hz = Ex*Ay - Ey*Ax;
- final float normH = (float)Math.sqrt(Hx*Hx + Hy*Hy + Hz*Hz);
+ float Hx = Ey * Az - Ez * Ay;
+ float Hy = Ez * Ax - Ex * Az;
+ float Hz = Ex * Ay - Ey * Ax;
+ final float normH = (float) Math.sqrt(Hx * Hx + Hy * Hy + Hz * Hz);
if (normH < 0.1f) {
// device is close to free fall (or in space?), or close to
@@ -1205,13 +1209,13 @@
Hx *= invH;
Hy *= invH;
Hz *= invH;
- final float invA = 1.0f / (float)Math.sqrt(Ax*Ax + Ay*Ay + Az*Az);
+ final float invA = 1.0f / (float) Math.sqrt(Ax * Ax + Ay * Ay + Az * Az);
Ax *= invA;
Ay *= invA;
Az *= invA;
- final float Mx = Ay*Hz - Az*Hy;
- final float My = Az*Hx - Ax*Hz;
- final float Mz = Ax*Hy - Ay*Hx;
+ final float Mx = Ay * Hz - Az * Hy;
+ final float My = Az * Hx - Ax * Hz;
+ final float Mz = Ax * Hy - Ay * Hx;
if (R != null) {
if (R.length == 9) {
R[0] = Hx; R[1] = Hy; R[2] = Hz;
@@ -1228,17 +1232,17 @@
// compute the inclination matrix by projecting the geomagnetic
// vector onto the Z (gravity) and X (horizontal component
// of geomagnetic vector) axes.
- final float invE = 1.0f / (float)Math.sqrt(Ex*Ex + Ey*Ey + Ez*Ez);
- final float c = (Ex*Mx + Ey*My + Ez*Mz) * invE;
- final float s = (Ex*Ax + Ey*Ay + Ez*Az) * invE;
+ final float invE = 1.0f / (float) Math.sqrt(Ex * Ex + Ey * Ey + Ez * Ez);
+ final float c = (Ex * Mx + Ey * My + Ez * Mz) * invE;
+ final float s = (Ex * Ax + Ey * Ay + Ez * Az) * invE;
if (I.length == 9) {
I[0] = 1; I[1] = 0; I[2] = 0;
I[3] = 0; I[4] = c; I[5] = s;
- I[6] = 0; I[7] =-s; I[8] = c;
+ I[6] = 0; I[7] = -s; I[8] = c;
} else if (I.length == 16) {
I[0] = 1; I[1] = 0; I[2] = 0;
I[4] = 0; I[5] = c; I[6] = s;
- I[8] = 0; I[9] =-s; I[10]= c;
+ I[8] = 0; I[9] = -s; I[10] = c;
I[3] = I[7] = I[11] = I[12] = I[13] = I[14] = 0;
I[15] = 1;
}
@@ -1262,9 +1266,9 @@
*/
public static float getInclination(float[] I) {
if (I.length == 9) {
- return (float)Math.atan2(I[5], I[4]);
+ return (float) Math.atan2(I[5], I[4]);
} else {
- return (float)Math.atan2(I[6], I[5]);
+ return (float) Math.atan2(I[6], I[5]);
}
}
@@ -1343,17 +1347,16 @@
* @see #getRotationMatrix(float[], float[], float[], float[])
*/
- public static boolean remapCoordinateSystem(float[] inR, int X, int Y,
- float[] outR)
- {
+ public static boolean remapCoordinateSystem(float[] inR, int X, int Y, float[] outR) {
if (inR == outR) {
- final float[] temp = mTempMatrix;
- synchronized(temp) {
+ final float[] temp = sTempMatrix;
+ synchronized (temp) {
// we don't expect to have a lot of contention
if (remapCoordinateSystemImpl(inR, X, Y, temp)) {
final int size = outR.length;
- for (int i=0 ; i<size ; i++)
+ for (int i = 0; i < size; i++) {
outR[i] = temp[i];
+ }
return true;
}
}
@@ -1361,9 +1364,7 @@
return remapCoordinateSystemImpl(inR, X, Y, outR);
}
- private static boolean remapCoordinateSystemImpl(float[] inR, int X, int Y,
- float[] outR)
- {
+ private static boolean remapCoordinateSystemImpl(float[] inR, int X, int Y, float[] outR) {
/*
* X and Y define a rotation matrix 'r':
*
@@ -1376,14 +1377,18 @@
*/
final int length = outR.length;
- if (inR.length != length)
+ if (inR.length != length) {
return false; // invalid parameter
- if ((X & 0x7C)!=0 || (Y & 0x7C)!=0)
+ }
+ if ((X & 0x7C) != 0 || (Y & 0x7C) != 0) {
return false; // invalid parameter
- if (((X & 0x3)==0) || ((Y & 0x3)==0))
+ }
+ if (((X & 0x3) == 0) || ((Y & 0x3) == 0)) {
return false; // no axis specified
- if ((X & 0x3) == (Y & 0x3))
+ }
+ if ((X & 0x3) == (Y & 0x3)) {
return false; // same axis specified
+ }
// Z is "the other" axis, its sign is either +/- sign(X)*sign(Y)
// this can be calculated by exclusive-or'ing X and Y; except for
@@ -1391,28 +1396,29 @@
int Z = X ^ Y;
// extract the axis (remove the sign), offset in the range 0 to 2.
- final int x = (X & 0x3)-1;
- final int y = (Y & 0x3)-1;
- final int z = (Z & 0x3)-1;
+ final int x = (X & 0x3) - 1;
+ final int y = (Y & 0x3) - 1;
+ final int z = (Z & 0x3) - 1;
// compute the sign of Z (whether it needs to be inverted)
- final int axis_y = (z+1)%3;
- final int axis_z = (z+2)%3;
- if (((x^axis_y)|(y^axis_z)) != 0)
+ final int axis_y = (z + 1) % 3;
+ final int axis_z = (z + 2) % 3;
+ if (((x ^ axis_y) | (y ^ axis_z)) != 0) {
Z ^= 0x80;
+ }
- final boolean sx = (X>=0x80);
- final boolean sy = (Y>=0x80);
- final boolean sz = (Z>=0x80);
+ final boolean sx = (X >= 0x80);
+ final boolean sy = (Y >= 0x80);
+ final boolean sz = (Z >= 0x80);
// Perform R * r, in avoiding actual muls and adds.
- final int rowLength = ((length==16)?4:3);
- for (int j=0 ; j<3 ; j++) {
- final int offset = j*rowLength;
- for (int i=0 ; i<3 ; i++) {
- if (x==i) outR[offset+i] = sx ? -inR[offset+0] : inR[offset+0];
- if (y==i) outR[offset+i] = sy ? -inR[offset+1] : inR[offset+1];
- if (z==i) outR[offset+i] = sz ? -inR[offset+2] : inR[offset+2];
+ final int rowLength = ((length == 16) ? 4 : 3);
+ for (int j = 0; j < 3; j++) {
+ final int offset = j * rowLength;
+ for (int i = 0; i < 3; i++) {
+ if (x == i) outR[offset + i] = sx ? -inR[offset + 0] : inR[offset + 0];
+ if (y == i) outR[offset + i] = sy ? -inR[offset + 1] : inR[offset + 1];
+ if (z == i) outR[offset + i] = sz ? -inR[offset + 2] : inR[offset + 2];
}
}
if (length == 16) {
@@ -1466,7 +1472,7 @@
* @see #getRotationMatrix(float[], float[], float[], float[])
* @see GeomagneticField
*/
- public static float[] getOrientation(float[] R, float values[]) {
+ public static float[] getOrientation(float[] R, float[] values) {
/*
* 4x4 (length=16) case:
* / R[ 0] R[ 1] R[ 2] 0 \
@@ -1481,13 +1487,13 @@
*
*/
if (R.length == 9) {
- values[0] = (float)Math.atan2(R[1], R[4]);
- values[1] = (float)Math.asin(-R[7]);
- values[2] = (float)Math.atan2(-R[6], R[8]);
+ values[0] = (float) Math.atan2(R[1], R[4]);
+ values[1] = (float) Math.asin(-R[7]);
+ values[2] = (float) Math.atan2(-R[6], R[8]);
} else {
- values[0] = (float)Math.atan2(R[1], R[5]);
- values[1] = (float)Math.asin(-R[9]);
- values[2] = (float)Math.atan2(-R[8], R[10]);
+ values[0] = (float) Math.atan2(R[1], R[5]);
+ values[1] = (float) Math.asin(-R[9]);
+ values[2] = (float) Math.atan2(-R[8], R[10]);
}
return values;
@@ -1524,7 +1530,7 @@
*/
public static float getAltitude(float p0, float p) {
final float coef = 1.0f / 5.255f;
- return 44330.0f * (1.0f - (float)Math.pow(p/p0, coef));
+ return 44330.0f * (1.0f - (float) Math.pow(p / p0, coef));
}
/** Helper function to compute the angle change between two rotation matrices.
@@ -1557,12 +1563,13 @@
* (in radians) is stored
*/
- public static void getAngleChange( float[] angleChange, float[] R, float[] prevR) {
- float rd1=0,rd4=0, rd6=0,rd7=0, rd8=0;
- float ri0=0,ri1=0,ri2=0,ri3=0,ri4=0,ri5=0,ri6=0,ri7=0,ri8=0;
- float pri0=0, pri1=0, pri2=0, pri3=0, pri4=0, pri5=0, pri6=0, pri7=0, pri8=0;
+ public static void getAngleChange(float[] angleChange, float[] R, float[] prevR) {
+ float rd1 = 0, rd4 = 0, rd6 = 0, rd7 = 0, rd8 = 0;
+ float ri0 = 0, ri1 = 0, ri2 = 0, ri3 = 0, ri4 = 0, ri5 = 0, ri6 = 0, ri7 = 0, ri8 = 0;
+ float pri0 = 0, pri1 = 0, pri2 = 0, pri3 = 0, pri4 = 0;
+ float pri5 = 0, pri6 = 0, pri7 = 0, pri8 = 0;
- if(R.length == 9) {
+ if (R.length == 9) {
ri0 = R[0];
ri1 = R[1];
ri2 = R[2];
@@ -1572,7 +1579,7 @@
ri6 = R[6];
ri7 = R[7];
ri8 = R[8];
- } else if(R.length == 16) {
+ } else if (R.length == 16) {
ri0 = R[0];
ri1 = R[1];
ri2 = R[2];
@@ -1584,7 +1591,7 @@
ri8 = R[10];
}
- if(prevR.length == 9) {
+ if (prevR.length == 9) {
pri0 = prevR[0];
pri1 = prevR[1];
pri2 = prevR[2];
@@ -1594,7 +1601,7 @@
pri6 = prevR[6];
pri7 = prevR[7];
pri8 = prevR[8];
- } else if(prevR.length == 16) {
+ } else if (prevR.length == 16) {
pri0 = prevR[0];
pri1 = prevR[1];
pri2 = prevR[2];
@@ -1615,9 +1622,9 @@
rd7 = pri2 * ri1 + pri5 * ri4 + pri8 * ri7; //rd[2][1]
rd8 = pri2 * ri2 + pri5 * ri5 + pri8 * ri8; //rd[2][2]
- angleChange[0] = (float)Math.atan2(rd1, rd4);
- angleChange[1] = (float)Math.asin(-rd7);
- angleChange[2] = (float)Math.atan2(-rd6, rd8);
+ angleChange[0] = (float) Math.atan2(rd1, rd4);
+ angleChange[1] = (float) Math.asin(-rd7);
+ angleChange[2] = (float) Math.atan2(-rd6, rd8);
}
@@ -1650,8 +1657,8 @@
if (rotationVector.length >= 4) {
q0 = rotationVector[3];
} else {
- q0 = 1 - q1*q1 - q2*q2 - q3*q3;
- q0 = (q0 > 0) ? (float)Math.sqrt(q0) : 0;
+ q0 = 1 - q1 * q1 - q2 * q2 - q3 * q3;
+ q0 = (q0 > 0) ? (float) Math.sqrt(q0) : 0;
}
float sq_q1 = 2 * q1 * q1;
@@ -1664,7 +1671,7 @@
float q2_q3 = 2 * q2 * q3;
float q1_q0 = 2 * q1 * q0;
- if(R.length == 9) {
+ if (R.length == 9) {
R[0] = 1 - sq_q2 - sq_q3;
R[1] = q1_q2 - q3_q0;
R[2] = q1_q3 + q2_q0;
@@ -1707,8 +1714,8 @@
if (rv.length >= 4) {
Q[0] = rv[3];
} else {
- Q[0] = 1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2];
- Q[0] = (Q[0] > 0) ? (float)Math.sqrt(Q[0]) : 0;
+ Q[0] = 1 - rv[0] * rv[0] - rv[1] * rv[1] - rv[2] * rv[2];
+ Q[0] = (Q[0] > 0) ? (float) Math.sqrt(Q[0]) : 0;
}
Q[1] = rv[0];
Q[2] = rv[1];
@@ -1800,7 +1807,7 @@
*/
@SystemApi
public boolean initDataInjection(boolean enable) {
- return initDataInjectionImpl(enable);
+ return initDataInjectionImpl(enable);
}
/**
@@ -1846,9 +1853,9 @@
}
int expectedNumValues = Sensor.getMaxLengthValuesArray(sensor, Build.VERSION_CODES.M);
if (values.length != expectedNumValues) {
- throw new IllegalArgumentException ("Wrong number of values for sensor " +
- sensor.getName() + " actual=" + values.length + " expected=" +
- expectedNumValues);
+ throw new IllegalArgumentException("Wrong number of values for sensor "
+ + sensor.getName() + " actual=" + values.length + " expected="
+ + expectedNumValues);
}
if (accuracy < SENSOR_STATUS_NO_CONTACT || accuracy > SENSOR_STATUS_ACCURACY_HIGH) {
throw new IllegalArgumentException("Invalid sensor accuracy");
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 607788d..1174cb6 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -28,10 +28,11 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
-import dalvik.system.CloseGuard;
import com.android.internal.annotations.GuardedBy;
+import dalvik.system.CloseGuard;
+
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.ref.WeakReference;
@@ -40,7 +41,6 @@
import java.util.List;
import java.util.Map;
-
/**
* Sensor manager implementation that communicates with the built-in
* system sensors.
@@ -101,7 +101,7 @@
/** {@hide} */
public SystemSensorManager(Context context, Looper mainLooper) {
- synchronized(sLock) {
+ synchronized (sLock) {
if (!sNativeClassInited) {
sNativeClassInited = true;
nativeClassInit();
@@ -114,7 +114,7 @@
mNativeInstance = nativeCreate(context.getOpPackageName());
// initialize the sensor list
- for (int index = 0;;++index) {
+ for (int index = 0;; ++index) {
Sensor sensor = new Sensor();
if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
mFullSensorsList.add(sensor);
@@ -157,9 +157,9 @@
return false;
}
if (mSensorListeners.size() >= MAX_LISTENER_COUNT) {
- throw new IllegalStateException("register failed, " +
- "the sensor listeners size has exceeded the maximum limit " +
- MAX_LISTENER_COUNT);
+ throw new IllegalStateException("register failed, "
+ + "the sensor listeners size has exceeded the maximum limit "
+ + MAX_LISTENER_COUNT);
}
// Invariants to preserve:
@@ -170,9 +170,10 @@
SensorEventQueue queue = mSensorListeners.get(listener);
if (queue == null) {
Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
- final String fullClassName = listener.getClass().getEnclosingClass() != null ?
- listener.getClass().getEnclosingClass().getName() :
- listener.getClass().getName();
+ final String fullClassName =
+ listener.getClass().getEnclosingClass() != null
+ ? listener.getClass().getEnclosingClass().getName()
+ : listener.getClass().getName();
queue = new SensorEventQueue(listener, looper, this, fullClassName);
if (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs)) {
queue.dispose();
@@ -221,17 +222,18 @@
if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) return false;
if (mTriggerListeners.size() >= MAX_LISTENER_COUNT) {
- throw new IllegalStateException("request failed, " +
- "the trigger listeners size has exceeded the maximum limit " +
- MAX_LISTENER_COUNT);
+ throw new IllegalStateException("request failed, "
+ + "the trigger listeners size has exceeded the maximum limit "
+ + MAX_LISTENER_COUNT);
}
synchronized (mTriggerListeners) {
TriggerEventQueue queue = mTriggerListeners.get(listener);
if (queue == null) {
- final String fullClassName = listener.getClass().getEnclosingClass() != null ?
- listener.getClass().getEnclosingClass().getName() :
- listener.getClass().getName();
+ final String fullClassName =
+ listener.getClass().getEnclosingClass() != null
+ ? listener.getClass().getEnclosingClass().getName()
+ : listener.getClass().getName();
queue = new TriggerEventQueue(listener, mMainLooper, this, fullClassName);
if (!queue.addSensor(sensor, 0, 0)) {
queue.dispose();
@@ -336,27 +338,27 @@
mHandleToSensor.remove(sensor.getHandle());
if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
- synchronized(mTriggerListeners) {
+ synchronized (mTriggerListeners) {
HashMap<TriggerEventListener, TriggerEventQueue> triggerListeners =
- new HashMap<TriggerEventListener, TriggerEventQueue>(mTriggerListeners);
+ new HashMap<TriggerEventListener, TriggerEventQueue>(mTriggerListeners);
- for (TriggerEventListener l: triggerListeners.keySet()) {
- if (DEBUG_DYNAMIC_SENSOR){
- Log.i(TAG, "removed trigger listener" + l.toString() +
- " due to sensor disconnection");
+ for (TriggerEventListener l : triggerListeners.keySet()) {
+ if (DEBUG_DYNAMIC_SENSOR) {
+ Log.i(TAG, "removed trigger listener" + l.toString()
+ + " due to sensor disconnection");
}
cancelTriggerSensorImpl(l, sensor, true);
}
}
} else {
- synchronized(mSensorListeners) {
+ synchronized (mSensorListeners) {
HashMap<SensorEventListener, SensorEventQueue> sensorListeners =
- new HashMap<SensorEventListener, SensorEventQueue>(mSensorListeners);
+ new HashMap<SensorEventListener, SensorEventQueue>(mSensorListeners);
for (SensorEventListener l: sensorListeners.keySet()) {
- if (DEBUG_DYNAMIC_SENSOR){
- Log.i(TAG, "removed event listener" + l.toString() +
- " due to sensor disconnection");
+ if (DEBUG_DYNAMIC_SENSOR) {
+ Log.i(TAG, "removed event listener" + l.toString()
+ + " due to sensor disconnection");
}
unregisterListenerImpl(l, sensor);
}
@@ -365,7 +367,7 @@
}
private void updateDynamicSensorList() {
- synchronized(mFullDynamicSensorsList) {
+ synchronized (mFullDynamicSensorsList) {
if (mDynamicSensorListDirty) {
List<Sensor> list = new ArrayList<>();
nativeGetDynamicSensors(mNativeInstance, list);
@@ -488,15 +490,15 @@
int i = 0, j = 0;
while (true) {
- if (j < oldList.size() && ( i >= newList.size() ||
- newList.get(i).getHandle() > oldList.get(j).getHandle()) ) {
+ if (j < oldList.size() && (i >= newList.size()
+ || newList.get(i).getHandle() > oldList.get(j).getHandle())) {
changed = true;
if (removed != null) {
removed.add(oldList.get(j));
}
++j;
- } else if (i < newList.size() && ( j >= oldList.size() ||
- newList.get(i).getHandle() < oldList.get(j).getHandle())) {
+ } else if (i < newList.size() && (j >= oldList.size()
+ || newList.get(i).getHandle() < oldList.get(j).getHandle())) {
changed = true;
if (added != null) {
added.add(newList.get(i));
@@ -505,8 +507,8 @@
updated.add(newList.get(i));
}
++i;
- } else if (i < newList.size() && j < oldList.size() &&
- newList.get(i).getHandle() == oldList.get(j).getHandle()) {
+ } else if (i < newList.size() && j < oldList.size()
+ && newList.get(i).getHandle() == oldList.get(j).getHandle()) {
if (updated != null) {
updated.add(oldList.get(j));
}
@@ -623,7 +625,7 @@
* associated with any listener and there is one InjectEventQueue associated with a
* SensorManager instance.
*/
- private static abstract class BaseEventQueue {
+ private abstract static class BaseEventQueue {
private static native long nativeInitBaseEventQueue(long nativeManager,
WeakReference<BaseEventQueue> eventQWeak, MessageQueue msgQ,
String packageName, int mode, String opPackageName);
@@ -633,9 +635,9 @@
private static native void nativeDestroySensorEventQueue(long eventQ);
private static native int nativeFlushSensor(long eventQ);
private static native int nativeInjectSensorData(long eventQ, int handle,
- float[] values,int accuracy, long timestamp);
+ float[] values, int accuracy, long timestamp);
- private long nSensorEventQueue;
+ private long mNativeSensorEventQueue;
private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
private final CloseGuard mCloseGuard = CloseGuard.get();
@@ -646,7 +648,7 @@
BaseEventQueue(Looper looper, SystemSensorManager manager, int mode, String packageName) {
if (packageName == null) packageName = "";
- nSensorEventQueue = nativeInitBaseEventQueue(manager.mNativeInstance,
+ mNativeSensorEventQueue = nativeInitBaseEventQueue(manager.mNativeInstance,
new WeakReference<>(this), looper.getQueue(),
packageName, mode, manager.mContext.getOpPackageName());
mCloseGuard.open("dispose");
@@ -668,17 +670,17 @@
addSensorEvent(sensor);
if (enableSensor(sensor, delayUs, maxBatchReportLatencyUs) != 0) {
// Try continuous mode if batching fails.
- if (maxBatchReportLatencyUs == 0 ||
- maxBatchReportLatencyUs > 0 && enableSensor(sensor, delayUs, 0) != 0) {
- removeSensor(sensor, false);
- return false;
+ if (maxBatchReportLatencyUs == 0
+ || maxBatchReportLatencyUs > 0 && enableSensor(sensor, delayUs, 0) != 0) {
+ removeSensor(sensor, false);
+ return false;
}
}
return true;
}
public boolean removeAllSensors() {
- for (int i=0 ; i<mActiveSensors.size(); i++) {
+ for (int i = 0; i < mActiveSensors.size(); i++) {
if (mActiveSensors.valueAt(i) == true) {
int handle = mActiveSensors.keyAt(i);
Sensor sensor = mManager.mHandleToSensor.get(handle);
@@ -706,8 +708,8 @@
}
public int flush() {
- if (nSensorEventQueue == 0) throw new NullPointerException();
- return nativeFlushSensor(nSensorEventQueue);
+ if (mNativeSensorEventQueue == 0) throw new NullPointerException();
+ return nativeFlushSensor(mNativeSensorEventQueue);
}
public boolean hasSensors() {
@@ -731,29 +733,30 @@
}
mCloseGuard.close();
}
- if (nSensorEventQueue != 0) {
- nativeDestroySensorEventQueue(nSensorEventQueue);
- nSensorEventQueue = 0;
+ if (mNativeSensorEventQueue != 0) {
+ nativeDestroySensorEventQueue(mNativeSensorEventQueue);
+ mNativeSensorEventQueue = 0;
}
}
private int enableSensor(
Sensor sensor, int rateUs, int maxBatchReportLatencyUs) {
- if (nSensorEventQueue == 0) throw new NullPointerException();
+ if (mNativeSensorEventQueue == 0) throw new NullPointerException();
if (sensor == null) throw new NullPointerException();
- return nativeEnableSensor(nSensorEventQueue, sensor.getHandle(), rateUs,
+ return nativeEnableSensor(mNativeSensorEventQueue, sensor.getHandle(), rateUs,
maxBatchReportLatencyUs);
}
protected int injectSensorDataBase(int handle, float[] values, int accuracy,
long timestamp) {
- return nativeInjectSensorData(nSensorEventQueue, handle, values, accuracy, timestamp);
+ return nativeInjectSensorData(
+ mNativeSensorEventQueue, handle, values, accuracy, timestamp);
}
private int disableSensor(Sensor sensor) {
- if (nSensorEventQueue == 0) throw new NullPointerException();
+ if (mNativeSensorEventQueue == 0) throw new NullPointerException();
if (sensor == null) throw new NullPointerException();
- return nativeDisableSensor(nSensorEventQueue, sensor.getHandle());
+ return nativeDisableSensor(mNativeSensorEventQueue, sensor.getHandle());
}
protected abstract void dispatchSensorEvent(int handle, float[] values, int accuracy,
long timestamp);
@@ -840,7 +843,7 @@
// sensor disconnected
return;
}
- ((SensorEventListener2)mListener).onFlushCompleted(sensor);
+ ((SensorEventListener2) mListener).onFlushCompleted(sensor);
}
return;
}
@@ -858,7 +861,7 @@
}
SensorAdditionalInfo info =
new SensorAdditionalInfo(sensor, type, serial, intValues, floatValues);
- ((SensorEventCallback)mListener).onSensorAdditionalInfo(info);
+ ((SensorEventCallback) mListener).onSensorAdditionalInfo(info);
}
}
}
@@ -930,8 +933,8 @@
super(looper, manager, OPERATING_MODE_DATA_INJECTION, packageName);
}
- int injectSensorData(int handle, float[] values,int accuracy, long timestamp) {
- return injectSensorDataBase(handle, values, accuracy, timestamp);
+ int injectSensorData(int handle, float[] values, int accuracy, long timestamp) {
+ return injectSensorDataBase(handle, values, accuracy, timestamp);
}
@SuppressWarnings("unused")
@@ -959,6 +962,7 @@
int handle = -1;
if (parameter.sensor != null) handle = parameter.sensor.getHandle();
return nativeSetOperationParameter(
- mNativeInstance, handle, parameter.type, parameter.floatValues, parameter.intValues) == 0;
+ mNativeInstance, handle,
+ parameter.type, parameter.floatValues, parameter.intValues) == 0;
}
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 6fbacaf..ae4f05a 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -278,6 +278,15 @@
*/
public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 1 << 7;
+ /**
+ * Virtual display flag: Indicates that the contents will be destroyed once
+ * the display is removed.
+ *
+ * @see #createVirtualDisplay
+ * @hide
+ */
+ public static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8;
+
/** @hide */
public DisplayManager(Context context) {
mContext = context;
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 744ee8e..d7ecc81 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2078,16 +2078,30 @@
* {@code ro.tether.denied} system property, Settings.TETHER_SUPPORTED or
* due to device configuration.
*
+ * <p>If this app does not have permission to use this API, it will always
+ * return false rather than throw an exception.</p>
+ *
+ * <p>If the device has a hotspot provisioning app, the caller is required to hold the
+ * {@link android.Manifest.permission.TETHER_PRIVILEGED} permission.</p>
+ *
+ * <p>Otherwise, this method requires the caller to hold the ability to modify system
+ * settings as determined by {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @return a boolean - {@code true} indicating Tethering is supported.
*
* {@hide}
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+ @RequiresPermission(anyOf = {android.Manifest.permission.TETHER_PRIVILEGED,
+ android.Manifest.permission.WRITE_SETTINGS})
public boolean isTetheringSupported() {
+ String pkgName = mContext.getOpPackageName();
try {
- String pkgName = mContext.getOpPackageName();
return mService.isTetheringSupported(pkgName);
+ } catch (SecurityException e) {
+ // This API is not available to this caller, but for backward-compatibility
+ // this will just return false instead of throwing.
+ return false;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 5ae3400..79310e2 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -19,15 +19,16 @@
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+
import com.android.internal.util.HexDump;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
/**
* IpSecAlgorithm specifies a single algorithm that can be applied to an IpSec Transform. Refer to
* RFC 4301.
- *
- * @hide
*/
public final class IpSecAlgorithm implements Parcelable {
@@ -75,13 +76,7 @@
public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
/** @hide */
- @StringDef({
- CRYPT_AES_CBC,
- AUTH_HMAC_MD5,
- AUTH_HMAC_SHA1,
- AUTH_HMAC_SHA256,
- AUTH_HMAC_SHA512
- })
+ @StringDef({CRYPT_AES_CBC, AUTH_HMAC_MD5, AUTH_HMAC_SHA1, AUTH_HMAC_SHA256, AUTH_HMAC_SHA512})
@Retention(RetentionPolicy.SOURCE)
public @interface AlgorithmName {}
@@ -197,4 +192,12 @@
.append("}")
.toString();
}
+
+ /** package */
+ static boolean equals(IpSecAlgorithm lhs, IpSecAlgorithm rhs) {
+ if (lhs == null || rhs == null) return (lhs == rhs);
+ return (lhs.mName.equals(rhs.mName)
+ && Arrays.equals(lhs.mKey, rhs.mKey)
+ && lhs.mTruncLenBits == rhs.mTruncLenBits);
+ }
};
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index 5a5c740..632b7fc 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -17,105 +17,170 @@
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.Log;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
+
+import com.android.internal.annotations.VisibleForTesting;
/** @hide */
public final class IpSecConfig implements Parcelable {
private static final String TAG = "IpSecConfig";
- //MODE_TRANSPORT or MODE_TUNNEL
- int mode;
+ // MODE_TRANSPORT or MODE_TUNNEL
+ private int mMode = IpSecTransform.MODE_TRANSPORT;
- // For tunnel mode
- InetAddress localAddress;
+ // Needs to be valid only for tunnel mode
+ // Preventing this from being null simplifies Java->Native binder
+ private String mLocalAddress = "";
- InetAddress remoteAddress;
+ // Preventing this from being null simplifies Java->Native binder
+ private String mRemoteAddress = "";
- // Limit selection by network interface
- Network network;
+ // The underlying Network that represents the "gateway" Network
+ // for outbound packets. It may also be used to select packets.
+ private Network mNetwork;
public static class Flow {
// Minimum requirements for identifying a transform
// SPI identifying the IPsec flow in packet processing
// and a remote IP address
- int spiResourceId;
+ private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID;
// Encryption Algorithm
- IpSecAlgorithm encryption;
+ private IpSecAlgorithm mEncryption;
// Authentication Algorithm
- IpSecAlgorithm authentication;
+ private IpSecAlgorithm mAuthentication;
@Override
public String toString() {
return new StringBuilder()
- .append("{spiResourceId=")
- .append(spiResourceId)
- .append(", encryption=")
- .append(encryption)
- .append(", authentication=")
- .append(authentication)
+ .append("{mSpiResourceId=")
+ .append(mSpiResourceId)
+ .append(", mEncryption=")
+ .append(mEncryption)
+ .append(", mAuthentication=")
+ .append(mAuthentication)
.append("}")
.toString();
}
+
+ static boolean equals(IpSecConfig.Flow lhs, IpSecConfig.Flow rhs) {
+ if (lhs == null || rhs == null) return (lhs == rhs);
+ return (lhs.mSpiResourceId == rhs.mSpiResourceId
+ && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
+ && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication));
+ }
}
- final Flow[] flow = new Flow[] {new Flow(), new Flow()};
+ private final Flow[] mFlow = new Flow[] {new Flow(), new Flow()};
// For tunnel mode IPv4 UDP Encapsulation
// IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
- int encapType;
- int encapLocalPortResourceId;
- int encapRemotePort;
+ private int mEncapType = IpSecTransform.ENCAP_NONE;
+ private int mEncapSocketResourceId = IpSecManager.INVALID_RESOURCE_ID;
+ private int mEncapRemotePort;
// An interval, in seconds between the NattKeepalive packets
- int nattKeepaliveInterval;
+ private int mNattKeepaliveInterval;
+
+ /** Set the mode for this IPsec transform */
+ public void setMode(int mode) {
+ mMode = mode;
+ }
+
+ /** Set the local IP address for Tunnel mode */
+ public void setLocalAddress(String localAddress) {
+ if (localAddress == null) {
+ throw new IllegalArgumentException("localAddress may not be null!");
+ }
+ mLocalAddress = localAddress;
+ }
+
+ /** Set the remote IP address for this IPsec transform */
+ public void setRemoteAddress(String remoteAddress) {
+ if (remoteAddress == null) {
+ throw new IllegalArgumentException("remoteAddress may not be null!");
+ }
+ mRemoteAddress = remoteAddress;
+ }
+
+ /** Set the SPI for a given direction by resource ID */
+ public void setSpiResourceId(int direction, int resourceId) {
+ mFlow[direction].mSpiResourceId = resourceId;
+ }
+
+ /** Set the encryption algorithm for a given direction */
+ public void setEncryption(int direction, IpSecAlgorithm encryption) {
+ mFlow[direction].mEncryption = encryption;
+ }
+
+ /** Set the authentication algorithm for a given direction */
+ public void setAuthentication(int direction, IpSecAlgorithm authentication) {
+ mFlow[direction].mAuthentication = authentication;
+ }
+
+ public void setNetwork(Network network) {
+ mNetwork = network;
+ }
+
+ public void setEncapType(int encapType) {
+ mEncapType = encapType;
+ }
+
+ public void setEncapSocketResourceId(int resourceId) {
+ mEncapSocketResourceId = resourceId;
+ }
+
+ public void setEncapRemotePort(int port) {
+ mEncapRemotePort = port;
+ }
+
+ public void setNattKeepaliveInterval(int interval) {
+ mNattKeepaliveInterval = interval;
+ }
// Transport or Tunnel
public int getMode() {
- return mode;
+ return mMode;
}
- public InetAddress getLocalAddress() {
- return localAddress;
+ public String getLocalAddress() {
+ return mLocalAddress;
}
public int getSpiResourceId(int direction) {
- return flow[direction].spiResourceId;
+ return mFlow[direction].mSpiResourceId;
}
- public InetAddress getRemoteAddress() {
- return remoteAddress;
+ public String getRemoteAddress() {
+ return mRemoteAddress;
}
public IpSecAlgorithm getEncryption(int direction) {
- return flow[direction].encryption;
+ return mFlow[direction].mEncryption;
}
public IpSecAlgorithm getAuthentication(int direction) {
- return flow[direction].authentication;
+ return mFlow[direction].mAuthentication;
}
public Network getNetwork() {
- return network;
+ return mNetwork;
}
public int getEncapType() {
- return encapType;
+ return mEncapType;
}
- public int getEncapLocalResourceId() {
- return encapLocalPortResourceId;
+ public int getEncapSocketResourceId() {
+ return mEncapSocketResourceId;
}
public int getEncapRemotePort() {
- return encapRemotePort;
+ return mEncapRemotePort;
}
public int getNattKeepaliveInterval() {
- return nattKeepaliveInterval;
+ return mNattKeepaliveInterval;
}
// Parcelable Methods
@@ -127,82 +192,70 @@
@Override
public void writeToParcel(Parcel out, int flags) {
- // TODO: Use a byte array or other better method for storing IPs that can also include scope
- out.writeString((localAddress != null) ? localAddress.getHostAddress() : null);
- // TODO: Use a byte array or other better method for storing IPs that can also include scope
- out.writeString((remoteAddress != null) ? remoteAddress.getHostAddress() : null);
- out.writeParcelable(network, flags);
- out.writeInt(flow[IpSecTransform.DIRECTION_IN].spiResourceId);
- out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].encryption, flags);
- out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].authentication, flags);
- out.writeInt(flow[IpSecTransform.DIRECTION_OUT].spiResourceId);
- out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].encryption, flags);
- out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].authentication, flags);
- out.writeInt(encapType);
- out.writeInt(encapLocalPortResourceId);
- out.writeInt(encapRemotePort);
+ out.writeInt(mMode);
+ out.writeString(mLocalAddress);
+ out.writeString(mRemoteAddress);
+ out.writeParcelable(mNetwork, flags);
+ out.writeInt(mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId);
+ out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mEncryption, flags);
+ out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthentication, flags);
+ out.writeInt(mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId);
+ out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mEncryption, flags);
+ out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication, flags);
+ out.writeInt(mEncapType);
+ out.writeInt(mEncapSocketResourceId);
+ out.writeInt(mEncapRemotePort);
+ out.writeInt(mNattKeepaliveInterval);
}
- // Package Private: Used by the IpSecTransform.Builder;
- // there should be no public constructor for this object
- IpSecConfig() {}
-
- private static InetAddress readInetAddressFromParcel(Parcel in) {
- String addrString = in.readString();
- if (addrString == null) {
- return null;
- }
- try {
- return InetAddress.getByName(addrString);
- } catch (UnknownHostException e) {
- Log.wtf(TAG, "Invalid IpAddress " + addrString);
- return null;
- }
- }
+ @VisibleForTesting
+ public IpSecConfig() {}
private IpSecConfig(Parcel in) {
- localAddress = readInetAddressFromParcel(in);
- remoteAddress = readInetAddressFromParcel(in);
- network = (Network) in.readParcelable(Network.class.getClassLoader());
- flow[IpSecTransform.DIRECTION_IN].spiResourceId = in.readInt();
- flow[IpSecTransform.DIRECTION_IN].encryption =
+ mMode = in.readInt();
+ mLocalAddress = in.readString();
+ mRemoteAddress = in.readString();
+ mNetwork = (Network) in.readParcelable(Network.class.getClassLoader());
+ mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId = in.readInt();
+ mFlow[IpSecTransform.DIRECTION_IN].mEncryption =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- flow[IpSecTransform.DIRECTION_IN].authentication =
+ mFlow[IpSecTransform.DIRECTION_IN].mAuthentication =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- flow[IpSecTransform.DIRECTION_OUT].spiResourceId = in.readInt();
- flow[IpSecTransform.DIRECTION_OUT].encryption =
+ mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId = in.readInt();
+ mFlow[IpSecTransform.DIRECTION_OUT].mEncryption =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- flow[IpSecTransform.DIRECTION_OUT].authentication =
+ mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- encapType = in.readInt();
- encapLocalPortResourceId = in.readInt();
- encapRemotePort = in.readInt();
+ mEncapType = in.readInt();
+ mEncapSocketResourceId = in.readInt();
+ mEncapRemotePort = in.readInt();
+ mNattKeepaliveInterval = in.readInt();
}
@Override
public String toString() {
StringBuilder strBuilder = new StringBuilder();
strBuilder
- .append("{mode=")
- .append(mode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT")
- .append(", localAddress=")
- .append(localAddress)
- .append(", remoteAddress=")
- .append(remoteAddress)
- .append(", network=")
- .append(network)
- .append(", encapType=")
- .append(encapType)
- .append(", encapLocalPortResourceId=")
- .append(encapLocalPortResourceId)
- .append(", encapRemotePort=")
- .append(encapRemotePort)
- .append(", nattKeepaliveInterval=")
- .append(nattKeepaliveInterval)
- .append(", flow[OUT]=")
- .append(flow[IpSecTransform.DIRECTION_OUT])
- .append(", flow[IN]=")
- .append(flow[IpSecTransform.DIRECTION_IN])
+ .append("{mMode=")
+ .append(mMode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT")
+ .append(", mLocalAddress=")
+ .append(mLocalAddress)
+ .append(", mRemoteAddress=")
+ .append(mRemoteAddress)
+ .append(", mNetwork=")
+ .append(mNetwork)
+ .append(", mEncapType=")
+ .append(mEncapType)
+ .append(", mEncapSocketResourceId=")
+ .append(mEncapSocketResourceId)
+ .append(", mEncapRemotePort=")
+ .append(mEncapRemotePort)
+ .append(", mNattKeepaliveInterval=")
+ .append(mNattKeepaliveInterval)
+ .append(", mFlow[OUT]=")
+ .append(mFlow[IpSecTransform.DIRECTION_OUT])
+ .append(", mFlow[IN]=")
+ .append(mFlow[IpSecTransform.DIRECTION_IN])
.append("}");
return strBuilder.toString();
@@ -218,4 +271,23 @@
return new IpSecConfig[size];
}
};
+
+ @VisibleForTesting
+ /** Equals method used for testing */
+ public static boolean equals(IpSecConfig lhs, IpSecConfig rhs) {
+ if (lhs == null || rhs == null) return (lhs == rhs);
+ return (lhs.mMode == rhs.mMode
+ && lhs.mLocalAddress.equals(rhs.mLocalAddress)
+ && lhs.mRemoteAddress.equals(rhs.mRemoteAddress)
+ && ((lhs.mNetwork != null && lhs.mNetwork.equals(rhs.mNetwork))
+ || (lhs.mNetwork == rhs.mNetwork))
+ && lhs.mEncapType == rhs.mEncapType
+ && lhs.mEncapSocketResourceId == rhs.mEncapSocketResourceId
+ && lhs.mEncapRemotePort == rhs.mEncapRemotePort
+ && lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval
+ && IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_OUT],
+ rhs.mFlow[IpSecTransform.DIRECTION_OUT])
+ && IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_IN],
+ rhs.mFlow[IpSecTransform.DIRECTION_IN]));
+ }
}
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 2f791e1..d7b3256 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -25,7 +25,11 @@
import android.os.RemoteException;
import android.util.AndroidException;
import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
import dalvik.system.CloseGuard;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.DatagramSocket;
@@ -36,7 +40,9 @@
* This class contains methods for managing IPsec sessions, which will perform kernel-space
* encryption and decryption of socket or Network traffic.
*
- * @hide
+ * <p>An IpSecManager may be obtained by calling {@link
+ * android.content.Context#getSystemService(String) Context#getSystemService(String)} with {@link
+ * android.content.Context#IPSEC_SERVICE Context#IPSEC_SERVICE}
*/
@SystemService(Context.IPSEC_SERVICE)
public final class IpSecManager {
@@ -184,7 +190,8 @@
}
/** @hide */
- int getResourceId() {
+ @VisibleForTesting
+ public int getResourceId() {
return mResourceId;
}
}
@@ -485,7 +492,8 @@
}
/** @hide */
- int getResourceId() {
+ @VisibleForTesting
+ public int getResourceId() {
return mResourceId;
}
};
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index cfbac58b..e15a2c6 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -26,9 +26,12 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+
import dalvik.system.CloseGuard;
+
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -43,8 +46,6 @@
*
* <p>An IpSecTransform may either represent a tunnel mode transform that operates on a wide array
* of traffic or may represent a transport mode transform operating on a Socket or Sockets.
- *
- * @hide
*/
public final class IpSecTransform implements AutoCloseable {
private static final String TAG = "IpSecTransform";
@@ -67,10 +68,10 @@
public @interface TransformDirection {}
/** @hide */
- public static final int MODE_TUNNEL = 0;
+ public static final int MODE_TRANSPORT = 0;
/** @hide */
- public static final int MODE_TRANSPORT = 1;
+ public static final int MODE_TUNNEL = 1;
/** @hide */
public static final int ENCAP_NONE = 0;
@@ -112,7 +113,11 @@
return IIpSecService.Stub.asInterface(b);
}
- private void checkResultStatusAndThrow(int status)
+ /**
+ * Checks the result status and throws an appropriate exception if
+ * the status is not Status.OK.
+ */
+ private void checkResultStatus(int status)
throws IOException, IpSecManager.ResourceUnavailableException,
IpSecManager.SpiUnavailableException {
switch (status) {
@@ -140,7 +145,7 @@
IpSecTransformResponse result =
svc.createTransportModeTransform(mConfig, new Binder());
int status = result.status;
- checkResultStatusAndThrow(status);
+ checkResultStatus(status);
mResourceId = result.resourceId;
/* Keepalive will silently fail if not needed by the config; but, if needed and
@@ -242,61 +247,20 @@
/* Package */
void startKeepalive(Context c) {
- // FIXME: NO_KEEPALIVE needs to be a constant
- if (mConfig.getNattKeepaliveInterval() == 0) {
- return;
- }
-
- ConnectivityManager cm =
- (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
-
- if (mKeepalive != null) {
- Log.wtf(TAG, "Keepalive already started for this IpSecTransform.");
- return;
- }
-
- synchronized (mKeepaliveSyncLock) {
- mKeepalive =
- cm.startNattKeepalive(
- mConfig.getNetwork(),
- mConfig.getNattKeepaliveInterval(),
- mKeepaliveCallback,
- mConfig.getLocalAddress(),
- 0x1234, /* FIXME: get the real port number again,
- which we need to retrieve from the provided
- EncapsulationSocket, and which isn't currently
- stashed in IpSecConfig */
- mConfig.getRemoteAddress());
- try {
- // FIXME: this is still a horrible way to fudge the synchronous callback
- mKeepaliveSyncLock.wait(2000);
- } catch (InterruptedException e) {
- }
- }
- if (mKeepaliveStatus != ConnectivityManager.PacketKeepalive.SUCCESS) {
- throw new UnsupportedOperationException("Packet Keepalive cannot be started");
+ if (mConfig.getNattKeepaliveInterval() != 0) {
+ Log.wtf(TAG, "Keepalive not yet supported.");
}
}
- /* Package */
- int getResourceId() {
+ /** @hide */
+ @VisibleForTesting
+ public int getResourceId() {
return mResourceId;
}
/* Package */
void stopKeepalive() {
- if (mKeepalive == null) {
- return;
- }
- mKeepalive.stop();
- synchronized (mKeepaliveSyncLock) {
- if (mKeepaliveStatus == ConnectivityManager.PacketKeepalive.SUCCESS) {
- try {
- mKeepaliveSyncLock.wait(2000);
- } catch (InterruptedException e) {
- }
- }
- }
+ return;
}
/**
@@ -322,7 +286,7 @@
*/
public IpSecTransform.Builder setEncryption(
@TransformDirection int direction, IpSecAlgorithm algo) {
- mConfig.flow[direction].encryption = algo;
+ mConfig.setEncryption(direction, algo);
return this;
}
@@ -337,7 +301,7 @@
*/
public IpSecTransform.Builder setAuthentication(
@TransformDirection int direction, IpSecAlgorithm algo) {
- mConfig.flow[direction].authentication = algo;
+ mConfig.setAuthentication(direction, algo);
return this;
}
@@ -360,9 +324,7 @@
*/
public IpSecTransform.Builder setSpi(
@TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
- // TODO: convert to using the resource Id of the SPI. Then build() can validate
- // the owner in the IpSecService
- mConfig.flow[direction].spiResourceId = spi.getResourceId();
+ mConfig.setSpiResourceId(direction, spi.getResourceId());
return this;
}
@@ -377,7 +339,7 @@
*/
@SystemApi
public IpSecTransform.Builder setUnderlyingNetwork(Network net) {
- mConfig.network = net;
+ mConfig.setNetwork(net);
return this;
}
@@ -394,10 +356,9 @@
*/
public IpSecTransform.Builder setIpv4Encapsulation(
IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
- // TODO: check encap type is valid.
- mConfig.encapType = ENCAP_ESPINUDP;
- mConfig.encapLocalPortResourceId = localSocket.getResourceId();
- mConfig.encapRemotePort = remotePort;
+ mConfig.setEncapType(ENCAP_ESPINUDP);
+ mConfig.setEncapSocketResourceId(localSocket.getResourceId());
+ mConfig.setEncapRemotePort(remotePort);
return this;
}
@@ -415,7 +376,7 @@
*/
@SystemApi
public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) {
- mConfig.nattKeepaliveInterval = intervalSeconds;
+ mConfig.setNattKeepaliveInterval(intervalSeconds);
return this;
}
@@ -448,10 +409,8 @@
public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress)
throws IpSecManager.ResourceUnavailableException,
IpSecManager.SpiUnavailableException, IOException {
- //FIXME: argument validation here
- //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
- mConfig.mode = MODE_TRANSPORT;
- mConfig.remoteAddress = remoteAddress;
+ mConfig.setMode(MODE_TRANSPORT);
+ mConfig.setRemoteAddress(remoteAddress.getHostAddress());
return new IpSecTransform(mContext, mConfig).activate();
}
@@ -472,9 +431,9 @@
InetAddress localAddress, InetAddress remoteAddress) {
//FIXME: argument validation here
//throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
- mConfig.localAddress = localAddress;
- mConfig.remoteAddress = remoteAddress;
- mConfig.mode = MODE_TUNNEL;
+ mConfig.setLocalAddress(localAddress.getHostAddress());
+ mConfig.setRemoteAddress(remoteAddress.getHostAddress());
+ mConfig.setMode(MODE_TUNNEL);
return new IpSecTransform(mContext, mConfig);
}
@@ -488,14 +447,5 @@
mContext = context;
mConfig = new IpSecConfig();
}
-
- /**
- * Return an {@link IpSecConfig} object for testing purposes.
- * @hide
- */
- @VisibleForTesting
- public IpSecConfig getIpSecConfig() {
- return mConfig;
- }
}
}
diff --git a/core/java/android/net/LocalServerSocket.java b/core/java/android/net/LocalServerSocket.java
index 3fcde330..d1f49d2 100644
--- a/core/java/android/net/LocalServerSocket.java
+++ b/core/java/android/net/LocalServerSocket.java
@@ -16,14 +16,15 @@
package android.net;
-import java.io.IOException;
+import java.io.Closeable;
import java.io.FileDescriptor;
+import java.io.IOException;
/**
* Non-standard class for creating an inbound UNIX-domain socket
* in the Linux abstract namespace.
*/
-public class LocalServerSocket {
+public class LocalServerSocket implements Closeable {
private final LocalSocketImpl impl;
private final LocalSocketAddress localAddress;
@@ -106,7 +107,7 @@
*
* @throws IOException
*/
- public void close() throws IOException
+ @Override public void close() throws IOException
{
impl.close();
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 66b6b47..bf0a264 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -78,17 +78,17 @@
* A constant indicating a sensor timer.
*/
public static final int SENSOR = 3;
-
+
/**
* A constant indicating a a wifi running timer
*/
public static final int WIFI_RUNNING = 4;
-
+
/**
* A constant indicating a full wifi lock timer
*/
public static final int FULL_WIFI_LOCK = 5;
-
+
/**
* A constant indicating a wifi scan
*/
@@ -217,8 +217,10 @@
* - Package wakeup alarms are now on screen-off timebase
* New in version 26:
* - Resource power manager (rpm) states [but screenOffRpm is disabled from working properly]
+ * New in version 27:
+ * - Always On Display (screen doze mode) time and power
*/
- static final String CHECKIN_VERSION = "26";
+ static final String CHECKIN_VERSION = "27";
/**
* Old version, we hit 9 and ran out of room, need to remove.
@@ -1381,12 +1383,12 @@
public static final int STATE_PHONE_SCANNING_FLAG = 1<<21;
public static final int STATE_SCREEN_ON_FLAG = 1<<20; // consider moving to states2
public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19; // consider moving to states2
- // empty slot
+ public static final int STATE_SCREEN_DOZE_FLAG = 1 << 18;
// empty slot
public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<16;
public static final int MOST_INTERESTING_STATES =
- STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG;
+ STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG | STATE_SCREEN_DOZE_FLAG;
public static final int SETTLE_TO_ZERO_STATES = 0xffff0000 & ~MOST_INTERESTING_STATES;
@@ -1414,8 +1416,8 @@
public static final int STATE2_BLUETOOTH_SCAN_FLAG = 1 << 20;
public static final int MOST_INTERESTING_STATES2 =
- STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
- | STATE2_CHARGING_FLAG | STATE2_PHONE_IN_CALL_FLAG | STATE2_BLUETOOTH_ON_FLAG;
+ STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
+ | STATE2_CHARGING_FLAG | STATE2_PHONE_IN_CALL_FLAG | STATE2_BLUETOOTH_ON_FLAG;
public static final int SETTLE_TO_ZERO_STATES2 = 0xffff0000 & ~MOST_INTERESTING_STATES2;
@@ -1863,6 +1865,21 @@
*/
public abstract int getScreenOnCount(int which);
+ /**
+ * Returns the time in microseconds that the screen has been dozing while the device was
+ * running on battery.
+ *
+ * {@hide}
+ */
+ public abstract long getScreenDozeTime(long elapsedRealtimeUs, int which);
+
+ /**
+ * Returns the number of times the screen was turned dozing.
+ *
+ * {@hide}
+ */
+ public abstract int getScreenDozeCount(int which);
+
public abstract long getInteractiveTime(long elapsedRealtimeUs, int which);
public static final int SCREEN_BRIGHTNESS_DARK = 0;
@@ -2116,8 +2133,7 @@
"group", "compl", "dorm", "uninit"
};
- public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS
- = new BitDescription[] {
+ public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS = new BitDescription[] {
new BitDescription(HistoryItem.STATE_CPU_RUNNING_FLAG, "running", "r"),
new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock", "w"),
new BitDescription(HistoryItem.STATE_SENSOR_ON_FLAG, "sensor", "s"),
@@ -2131,6 +2147,7 @@
new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio", "a"),
new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen", "S"),
new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged", "BP"),
+ new BitDescription(HistoryItem.STATE_SCREEN_DOZE_FLAG, "screen_doze", "Sd"),
new BitDescription(HistoryItem.STATE_DATA_CONNECTION_MASK,
HistoryItem.STATE_DATA_CONNECTION_SHIFT, "data_conn", "Pcn",
DATA_CONNECTION_NAMES, DATA_CONNECTION_NAMES),
@@ -2467,6 +2484,18 @@
public abstract int getDischargeAmountScreenOffSinceCharge();
/**
+ * Get the amount the battery has discharged while the screen was doze,
+ * since the last time power was unplugged.
+ */
+ public abstract int getDischargeAmountScreenDoze();
+
+ /**
+ * Get the amount the battery has discharged while the screen was doze,
+ * since the last time the device was charged.
+ */
+ public abstract int getDischargeAmountScreenDozeSinceCharge();
+
+ /**
* Returns the total, last, or current battery uptime in microseconds.
*
* @param curTime the elapsed realtime in microseconds.
@@ -2483,7 +2512,7 @@
public abstract long computeBatteryRealtime(long curTime, int which);
/**
- * Returns the total, last, or current battery screen off uptime in microseconds.
+ * Returns the total, last, or current battery screen off/doze uptime in microseconds.
*
* @param curTime the elapsed realtime in microseconds.
* @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
@@ -2491,7 +2520,7 @@
public abstract long computeBatteryScreenOffUptime(long curTime, int which);
/**
- * Returns the total, last, or current battery screen off realtime in microseconds.
+ * Returns the total, last, or current battery screen off/doze realtime in microseconds.
*
* @param curTime the current elapsed realtime in microseconds.
* @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
@@ -2590,18 +2619,24 @@
};
/**
- * Return the counter keeping track of the amount of battery discharge while the screen was off,
- * measured in micro-Ampere-hours. This will be non-zero only if the device's battery has
- * a coulomb counter.
- */
- public abstract LongCounter getDischargeScreenOffCoulombCounter();
-
- /**
- * Return the counter keeping track of the amount of battery discharge measured in
+ * Return the amount of battery discharge while the screen was off, measured in
* micro-Ampere-hours. This will be non-zero only if the device's battery has
* a coulomb counter.
*/
- public abstract LongCounter getDischargeCoulombCounter();
+ public abstract long getMahDischargeScreenOff(int which);
+
+ /**
+ * Return the amount of battery discharge while the screen was in doze mode, measured in
+ * micro-Ampere-hours. This will be non-zero only if the device's battery has
+ * a coulomb counter.
+ */
+ public abstract long getMahDischargeScreenDoze(int which);
+
+ /**
+ * Return the amount of battery discharge measured in micro-Ampere-hours. This will be
+ * non-zero only if the device's battery has a coulomb counter.
+ */
+ public abstract long getMahDischarge(int which);
/**
* Returns the estimated real battery capacity, which may be less than the capacity
@@ -3112,6 +3147,7 @@
final long totalRealtime = computeRealtime(rawRealtime, which);
final long totalUptime = computeUptime(rawUptime, which);
final long screenOnTime = getScreenOnTime(rawRealtime, which);
+ final long screenDozeTime = getScreenDozeTime(rawRealtime, which);
final long interactiveTime = getInteractiveTime(rawRealtime, which);
final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
final long deviceIdleModeLightTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT,
@@ -3124,9 +3160,9 @@
rawRealtime, which);
final int connChanges = getNumConnectivityChange(which);
final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
- final long dischargeCount = getDischargeCoulombCounter().getCountLocked(which);
- final long dischargeScreenOffCount = getDischargeScreenOffCoulombCounter()
- .getCountLocked(which);
+ final long dischargeCount = getMahDischarge(which);
+ final long dischargeScreenOffCount = getMahDischargeScreenOff(which);
+ final long dischargeScreenDozeCount = getMahDischargeScreenDoze(which);
final StringBuilder sb = new StringBuilder(128);
@@ -3143,7 +3179,8 @@
getStartClockTime(),
whichBatteryScreenOffRealtime / 1000, whichBatteryScreenOffUptime / 1000,
getEstimatedBatteryCapacity(),
- getMinLearnedBatteryCapacity(), getMaxLearnedBatteryCapacity());
+ getMinLearnedBatteryCapacity(), getMaxLearnedBatteryCapacity(),
+ screenDozeTime / 1000);
// Calculate wakelock times across all uids.
@@ -3295,13 +3332,15 @@
getDischargeStartLevel()-getDischargeCurrentLevel(),
getDischargeStartLevel()-getDischargeCurrentLevel(),
getDischargeAmountScreenOn(), getDischargeAmountScreenOff(),
- dischargeCount / 1000, dischargeScreenOffCount / 1000);
+ dischargeCount / 1000, dischargeScreenOffCount / 1000,
+ getDischargeAmountScreenDoze(), dischargeScreenDozeCount / 1000);
} else {
dumpLine(pw, 0 /* uid */, category, BATTERY_DISCHARGE_DATA,
getLowDischargeAmountSinceCharge(), getHighDischargeAmountSinceCharge(),
getDischargeAmountScreenOnSinceCharge(),
getDischargeAmountScreenOffSinceCharge(),
- dischargeCount / 1000, dischargeScreenOffCount / 1000);
+ dischargeCount / 1000, dischargeScreenOffCount / 1000,
+ getDischargeAmountScreenDozeSinceCharge(), dischargeScreenDozeCount / 1000);
}
if (reqUid < 0) {
@@ -3831,6 +3870,7 @@
which);
final long batteryTimeRemaining = computeBatteryTimeRemaining(rawRealtime);
final long chargeTimeRemaining = computeChargeTimeRemaining(rawRealtime);
+ final long screenDozeTime = getScreenDozeTime(rawRealtime, which);
final StringBuilder sb = new StringBuilder(128);
@@ -3868,25 +3908,35 @@
sb.setLength(0);
sb.append(prefix);
- sb.append(" Time on battery: ");
- formatTimeMs(sb, whichBatteryRealtime / 1000); sb.append("(");
- sb.append(formatRatioLocked(whichBatteryRealtime, totalRealtime));
- sb.append(") realtime, ");
- formatTimeMs(sb, whichBatteryUptime / 1000);
- sb.append("("); sb.append(formatRatioLocked(whichBatteryUptime, totalRealtime));
- sb.append(") uptime");
+ sb.append(" Time on battery: ");
+ formatTimeMs(sb, whichBatteryRealtime / 1000); sb.append("(");
+ sb.append(formatRatioLocked(whichBatteryRealtime, totalRealtime));
+ sb.append(") realtime, ");
+ formatTimeMs(sb, whichBatteryUptime / 1000);
+ sb.append("("); sb.append(formatRatioLocked(whichBatteryUptime, whichBatteryRealtime));
+ sb.append(") uptime");
pw.println(sb.toString());
+
sb.setLength(0);
sb.append(prefix);
- sb.append(" Time on battery screen off: ");
- formatTimeMs(sb, whichBatteryScreenOffRealtime / 1000); sb.append("(");
- sb.append(formatRatioLocked(whichBatteryScreenOffRealtime, totalRealtime));
- sb.append(") realtime, ");
- formatTimeMs(sb, whichBatteryScreenOffUptime / 1000);
- sb.append("(");
- sb.append(formatRatioLocked(whichBatteryScreenOffUptime, totalRealtime));
- sb.append(") uptime");
+ sb.append(" Time on battery screen off: ");
+ formatTimeMs(sb, whichBatteryScreenOffRealtime / 1000); sb.append("(");
+ sb.append(formatRatioLocked(whichBatteryScreenOffRealtime, whichBatteryRealtime));
+ sb.append(") realtime, ");
+ formatTimeMs(sb, whichBatteryScreenOffUptime / 1000);
+ sb.append("(");
+ sb.append(formatRatioLocked(whichBatteryScreenOffUptime, whichBatteryRealtime));
+ sb.append(") uptime");
pw.println(sb.toString());
+
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Time on battery screen doze: ");
+ formatTimeMs(sb, screenDozeTime / 1000); sb.append("(");
+ sb.append(formatRatioLocked(screenDozeTime, whichBatteryRealtime));
+ sb.append(")");
+ pw.println(sb.toString());
+
sb.setLength(0);
sb.append(prefix);
sb.append(" Total run time: ");
@@ -3910,8 +3960,7 @@
pw.println(sb.toString());
}
- final LongCounter dischargeCounter = getDischargeCoulombCounter();
- final long dischargeCount = dischargeCounter.getCountLocked(which);
+ final long dischargeCount = getMahDischarge(which);
if (dischargeCount >= 0) {
sb.setLength(0);
sb.append(prefix);
@@ -3921,8 +3970,7 @@
pw.println(sb.toString());
}
- final LongCounter dischargeScreenOffCounter = getDischargeScreenOffCoulombCounter();
- final long dischargeScreenOffCount = dischargeScreenOffCounter.getCountLocked(which);
+ final long dischargeScreenOffCount = getMahDischargeScreenOff(which);
if (dischargeScreenOffCount >= 0) {
sb.setLength(0);
sb.append(prefix);
@@ -3932,7 +3980,18 @@
pw.println(sb.toString());
}
- final long dischargeScreenOnCount = dischargeCount - dischargeScreenOffCount;
+ final long dischargeScreenDozeCount = getMahDischargeScreenDoze(which);
+ if (dischargeScreenDozeCount >= 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Screen doze discharge: ");
+ sb.append(BatteryStatsHelper.makemAh(dischargeScreenDozeCount / 1000.0));
+ sb.append(" mAh");
+ pw.println(sb.toString());
+ }
+
+ final long dischargeScreenOnCount =
+ dischargeCount - dischargeScreenOffCount - dischargeScreenDozeCount;
if (dischargeScreenOnCount >= 0) {
sb.setLength(0);
sb.append(prefix);
@@ -4340,20 +4399,24 @@
pw.println(getDischargeCurrentLevel());
}
pw.print(prefix); pw.print(" Amount discharged while screen on: ");
- pw.println(getDischargeAmountScreenOn());
+ pw.println(getDischargeAmountScreenOn());
pw.print(prefix); pw.print(" Amount discharged while screen off: ");
- pw.println(getDischargeAmountScreenOff());
+ pw.println(getDischargeAmountScreenOff());
+ pw.print(prefix); pw.print(" Amount discharged while screen doze: ");
+ pw.println(getDischargeAmountScreenDoze());
pw.println(" ");
} else {
pw.print(prefix); pw.println(" Device battery use since last full charge");
pw.print(prefix); pw.print(" Amount discharged (lower bound): ");
- pw.println(getLowDischargeAmountSinceCharge());
+ pw.println(getLowDischargeAmountSinceCharge());
pw.print(prefix); pw.print(" Amount discharged (upper bound): ");
- pw.println(getHighDischargeAmountSinceCharge());
+ pw.println(getHighDischargeAmountSinceCharge());
pw.print(prefix); pw.print(" Amount discharged while screen on: ");
- pw.println(getDischargeAmountScreenOnSinceCharge());
+ pw.println(getDischargeAmountScreenOnSinceCharge());
pw.print(prefix); pw.print(" Amount discharged while screen off: ");
- pw.println(getDischargeAmountScreenOffSinceCharge());
+ pw.println(getDischargeAmountScreenOffSinceCharge());
+ pw.print(prefix); pw.print(" Amount discharged while screen doze: ");
+ pw.println(getDischargeAmountScreenDozeSinceCharge());
pw.println();
}
@@ -5426,7 +5489,7 @@
}
}
}
-
+
public void prepareForDumpLocked() {
}
@@ -6248,7 +6311,7 @@
pw.println();
}
}
-
+
@SuppressWarnings("unused")
public void dumpCheckinLocked(Context context, PrintWriter pw,
List<ApplicationInfo> apps, int flags, long histStart) {
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 0df6361..e9e695b 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -23,7 +23,6 @@
import android.util.Slog;
import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
@@ -202,7 +201,7 @@
* then its own pid is returned.
*/
public static final native int getCallingPid();
-
+
/**
* Return the Linux uid assigned to the process that sent you the
* current transaction that is being processed. This uid can be used with
@@ -335,7 +334,7 @@
* it needs to.
*/
public static final native void flushPendingCommands();
-
+
/**
* Add the calling thread to the IPC thread pool. This function does
* not return until the current process is exiting.
@@ -372,7 +371,7 @@
}
}
}
-
+
/**
* Convenience method for associating a specific interface with the Binder.
* After calling, queryLocalInterface() will be implemented for you
@@ -383,7 +382,7 @@
mOwner = owner;
mDescriptor = descriptor;
}
-
+
/**
* Default implementation returns an empty interface name.
*/
@@ -408,7 +407,7 @@
public boolean isBinderAlive() {
return true;
}
-
+
/**
* Use information supplied to attachInterface() to return the
* associated IInterface if it matches the requested
@@ -630,7 +629,7 @@
}
return r;
}
-
+
/**
* Local implementation is a no-op.
*/
@@ -643,7 +642,7 @@
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
return true;
}
-
+
protected void finalize() throws Throwable {
try {
destroyBinder();
@@ -730,7 +729,15 @@
}
}
+/**
+ * Java proxy for a native IBinder object.
+ * Allocated and constructed by the native javaObjectforIBinder function. Never allocated
+ * directly from Java code.
+ */
final class BinderProxy implements IBinder {
+ // See android_util_Binder.cpp for the native half of this.
+ // TODO: Consider using NativeAllocationRegistry instead of finalization.
+
// Assume the process-wide default value when created
volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking;
@@ -789,7 +796,7 @@
reply.recycle();
}
}
-
+
public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -826,7 +833,7 @@
BinderProxy() {
mSelf = new WeakReference(this);
}
-
+
@Override
protected void finalize() throws Throwable {
try {
@@ -835,9 +842,9 @@
super.finalize();
}
}
-
+
private native final void destroy();
-
+
private static final void sendDeathNotice(DeathRecipient recipient) {
if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
try {
@@ -848,8 +855,20 @@
exc);
}
}
-
+
+ // This WeakReference to "this" is used only by native code to "attach" to the
+ // native IBinder object.
+ // Using WeakGlobalRefs instead currently appears unsafe, in that they can yield a
+ // non-null value after the BinderProxy is enqueued for finalization.
+ // Used only once immediately after construction.
+ // TODO: Consider making the extra native-to-java call to compute this on the fly.
final private WeakReference mSelf;
+
+ // Native pointer to the wrapped native IBinder object. Counted as strong reference.
private long mObject;
+
+ // Native pointer to native DeathRecipientList. Counted as strong reference.
+ // Basically owned by the JavaProxy object. Reference counted only because DeathRecipients
+ // hold a weak reference that can be temporarily promoted.
private long mOrgue;
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 0562716..a474b47 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -104,11 +104,6 @@
void setIPv6AddrGenMode(String iface, int mode);
/**
- * Enables or enables IPv6 ND offload.
- */
- void setInterfaceIpv6NdOffload(String iface, boolean enable);
-
- /**
* Add the specified route to the interface.
*/
void addRoute(int netId, in RouteInfo route);
diff --git a/core/java/android/os/IServiceManager.java b/core/java/android/os/IServiceManager.java
index 7b11c28..87c65ec 100644
--- a/core/java/android/os/IServiceManager.java
+++ b/core/java/android/os/IServiceManager.java
@@ -18,12 +18,12 @@
/**
* Basic interface for finding and publishing system services.
- *
+ *
* An implementation of this interface is usually published as the
* global context object, which can be retrieved via
* BinderNative.getContextObject(). An easy way to retrieve this
* is with the static method BnServiceManager.getDefault().
- *
+ *
* @hide
*/
public interface IServiceManager extends IInterface
@@ -33,33 +33,33 @@
* service manager. Blocks for a few seconds waiting for it to be
* published if it does not already exist.
*/
- public IBinder getService(String name) throws RemoteException;
-
+ IBinder getService(String name) throws RemoteException;
+
/**
* Retrieve an existing service called @a name from the
* service manager. Non-blocking.
*/
- public IBinder checkService(String name) throws RemoteException;
+ IBinder checkService(String name) throws RemoteException;
/**
* Place a new @a service called @a name into the service
* manager.
*/
- public void addService(String name, IBinder service, boolean allowIsolated)
+ void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
throws RemoteException;
/**
* Return a list of all currently running services.
*/
- public String[] listServices() throws RemoteException;
+ String[] listServices(int dumpPriority) throws RemoteException;
/**
* Assign a permission controller to the service manager. After set, this
* interface is checked before any services are added.
*/
- public void setPermissionController(IPermissionController controller)
+ void setPermissionController(IPermissionController controller)
throws RemoteException;
-
+
static final String descriptor = "android.os.IServiceManager";
int GET_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
@@ -68,4 +68,13 @@
int LIST_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3;
int CHECK_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4;
int SET_PERMISSION_CONTROLLER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+5;
+
+ /*
+ * Must update values in IServiceManager.h
+ */
+ int DUMP_PRIORITY_CRITICAL = 1 << 0;
+ int DUMP_PRIORITY_HIGH = 1 << 1;
+ int DUMP_PRIORITY_NORMAL = 1 << 2;
+ int DUMP_PRIORITY_ALL = DUMP_PRIORITY_CRITICAL | DUMP_PRIORITY_HIGH
+ | DUMP_PRIORITY_NORMAL;
}
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index a83d313..d8f9567 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -20,11 +20,11 @@
* Binder interface to communicate with the Java-based statistics service helper.
* {@hide}
*/
-oneway interface IStatsCompanionService {
+interface IStatsCompanionService {
/**
* Tell statscompanion that stastd is up and running.
*/
- void statsdReady();
+ oneway void statsdReady();
/**
* Register an alarm for anomaly detection to fire at the given timestamp (ms since epoch).
@@ -32,10 +32,10 @@
* Uses AlarmManager.set API, so if the timestamp is in the past, alarm fires immediately, and
* alarm is inexact.
*/
- void setAnomalyAlarm(long timestampMs);
+ oneway void setAnomalyAlarm(long timestampMs);
/** Cancel any anomaly detection alarm. */
- void cancelAnomalyAlarm();
+ oneway void cancelAnomalyAlarm();
/**
* Register a repeating alarm for polling to fire at the given timestamp and every
@@ -44,8 +44,11 @@
* Uses AlarmManager.setRepeating API, so if the timestamp is in past, alarm fires immediately,
* and alarm is inexact.
*/
- void setPollingAlarms(long timestampMs, long intervalMs);
+ oneway void setPollingAlarms(long timestampMs, long intervalMs);
/** Cancel any repeating polling alarm. */
- void cancelPollingAlarms();
+ oneway void cancelPollingAlarms();
+
+ /** Pull the specified data. Results will be sent to statsd when complete. */
+ String pullData(int pullCode);
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 031ca91..857e8a6 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -1340,6 +1340,13 @@
* @see Parcelable
*/
public final <T extends Parcelable> void writeTypedList(List<T> val) {
+ writeTypedList(val, 0);
+ }
+
+ /**
+ * @hide
+ */
+ public <T extends Parcelable> void writeTypedList(List<T> val, int parcelableFlags) {
if (val == null) {
writeInt(-1);
return;
@@ -1348,13 +1355,7 @@
int i=0;
writeInt(N);
while (i < N) {
- T item = val.get(i);
- if (item != null) {
- writeInt(1);
- item.writeToParcel(this, 0);
- } else {
- writeInt(0);
- }
+ writeTypedObject(val.get(i), parcelableFlags);
i++;
}
}
@@ -1456,13 +1457,7 @@
int N = val.length;
writeInt(N);
for (int i = 0; i < N; i++) {
- T item = val[i];
- if (item != null) {
- writeInt(1);
- item.writeToParcel(this, parcelableFlags);
- } else {
- writeInt(0);
- }
+ writeTypedObject(val[i], parcelableFlags);
}
} else {
writeInt(-1);
@@ -1470,146 +1465,6 @@
}
/**
- * Write a uniform (all items are null or the same class) array list of
- * parcelables.
- *
- * @param list The list to write.
- *
- * @hide
- */
- public final <T extends Parcelable> void writeTypedArrayList(@Nullable ArrayList<T> list,
- int parcelableFlags) {
- if (list != null) {
- int N = list.size();
- writeInt(N);
- boolean wroteCreator = false;
- for (int i = 0; i < N; i++) {
- T item = list.get(i);
- if (item != null) {
- writeInt(1);
- if (!wroteCreator) {
- writeParcelableCreator(item);
- wroteCreator = true;
- }
- item.writeToParcel(this, parcelableFlags);
- } else {
- writeInt(0);
- }
- }
- } else {
- writeInt(-1);
- }
- }
-
- /**
- * Reads a uniform (all items are null or the same class) array list of
- * parcelables.
- *
- * @return The list or null.
- *
- * @hide
- */
- public final @Nullable <T> ArrayList<T> readTypedArrayList(@Nullable ClassLoader loader) {
- int N = readInt();
- if (N <= 0) {
- return null;
- }
- Parcelable.Creator<?> creator = null;
- ArrayList<T> result = new ArrayList<T>(N);
- for (int i = 0; i < N; i++) {
- if (readInt() != 0) {
- if (creator == null) {
- creator = readParcelableCreator(loader);
- if (creator == null) {
- return null;
- }
- }
- final T parcelable;
- if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
- Parcelable.ClassLoaderCreator<?> classLoaderCreator =
- (Parcelable.ClassLoaderCreator<?>) creator;
- parcelable = (T) classLoaderCreator.createFromParcel(this, loader);
- } else {
- parcelable = (T) creator.createFromParcel(this);
- }
- result.add(parcelable);
- } else {
- result.add(null);
- }
- }
- return result;
- }
-
- /**
- * Write a uniform (all items are null or the same class) array set of
- * parcelables.
- *
- * @param set The set to write.
- *
- * @hide
- */
- public final <T extends Parcelable> void writeTypedArraySet(@Nullable ArraySet<T> set,
- int parcelableFlags) {
- if (set != null) {
- int N = set.size();
- writeInt(N);
- boolean wroteCreator = false;
- for (int i = 0; i < N; i++) {
- T item = set.valueAt(i);
- if (item != null) {
- writeInt(1);
- if (!wroteCreator) {
- writeParcelableCreator(item);
- wroteCreator = true;
- }
- item.writeToParcel(this, parcelableFlags);
- } else {
- writeInt(0);
- }
- }
- } else {
- writeInt(-1);
- }
- }
-
- /**
- * Reads a uniform (all items are null or the same class) array set of
- * parcelables.
- *
- * @return The set or null.
- *
- * @hide
- */
- public final @Nullable <T> ArraySet<T> readTypedArraySet(@Nullable ClassLoader loader) {
- int N = readInt();
- if (N <= 0) {
- return null;
- }
- Parcelable.Creator<?> creator = null;
- ArraySet<T> result = new ArraySet<T>(N);
- for (int i = 0; i < N; i++) {
- T parcelable = null;
- if (readInt() != 0) {
- if (creator == null) {
- creator = readParcelableCreator(loader);
- if (creator == null) {
- return null;
- }
- }
- if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
- Parcelable.ClassLoaderCreator<?> classLoaderCreator =
- (Parcelable.ClassLoaderCreator<?>) creator;
- parcelable = (T) classLoaderCreator.createFromParcel(this, loader);
- } else {
- parcelable = (T) creator.createFromParcel(this);
- }
- }
- result.append(parcelable);
- }
- return result;
- }
-
- /**
* Flatten the Parcelable object into the parcel.
*
* @param val The Parcelable object to be written.
@@ -2458,11 +2313,7 @@
}
ArrayList<T> l = new ArrayList<T>(N);
while (N > 0) {
- if (readInt() != 0) {
- l.add(c.createFromParcel(this));
- } else {
- l.add(null);
- }
+ l.add(readTypedObject(c));
N--;
}
return l;
@@ -2485,18 +2336,10 @@
int N = readInt();
int i = 0;
for (; i < M && i < N; i++) {
- if (readInt() != 0) {
- list.set(i, c.createFromParcel(this));
- } else {
- list.set(i, null);
- }
+ list.set(i, readTypedObject(c));
}
for (; i<N; i++) {
- if (readInt() != 0) {
- list.add(c.createFromParcel(this));
- } else {
- list.add(null);
- }
+ list.add(readTypedObject(c));
}
for (; i<M; i++) {
list.remove(N);
@@ -2641,9 +2484,7 @@
}
T[] l = c.newArray(N);
for (int i=0; i<N; i++) {
- if (readInt() != 0) {
- l[i] = c.createFromParcel(this);
- }
+ l[i] = readTypedObject(c);
}
return l;
}
@@ -2652,11 +2493,7 @@
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
- if (readInt() != 0) {
- val[i] = c.createFromParcel(this);
- } else {
- val[i] = null;
- }
+ val[i] = readTypedObject(c);
}
} else {
throw new RuntimeException("bad array lengths");
diff --git a/core/java/android/os/ParcelableException.java b/core/java/android/os/ParcelableException.java
index d84d629..7f71905 100644
--- a/core/java/android/os/ParcelableException.java
+++ b/core/java/android/os/ParcelableException.java
@@ -52,10 +52,12 @@
final String msg = in.readString();
try {
final Class<?> clazz = Class.forName(name, true, Parcelable.class.getClassLoader());
- return (Throwable) clazz.getConstructor(String.class).newInstance(msg);
+ if (Throwable.class.isAssignableFrom(clazz)) {
+ return (Throwable) clazz.getConstructor(String.class).newInstance(msg);
+ }
} catch (ReflectiveOperationException e) {
- throw new RuntimeException(name + ": " + msg);
}
+ return new RuntimeException(name + ": " + msg);
}
/** {@hide} */
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 960c9f5..7f4dee6 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -443,6 +443,20 @@
public static final String SHUTDOWN_USER_REQUESTED = "userrequested";
/**
+ * The value to pass as the 'reason' argument to android_reboot() when battery temperature
+ * is too high.
+ * @hide
+ */
+ public static final String SHUTDOWN_BATTERY_THERMAL_STATE = "thermal,battery";
+
+ /**
+ * The value to pass as the 'reason' argument to android_reboot() when device is running
+ * critically low on battery.
+ * @hide
+ */
+ public static final String SHUTDOWN_LOW_BATTERY = "battery";
+
+ /**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@@ -451,7 +465,9 @@
SHUTDOWN_REASON_SHUTDOWN,
SHUTDOWN_REASON_REBOOT,
SHUTDOWN_REASON_USER_REQUESTED,
- SHUTDOWN_REASON_THERMAL_SHUTDOWN
+ SHUTDOWN_REASON_THERMAL_SHUTDOWN,
+ SHUTDOWN_REASON_LOW_BATTERY,
+ SHUTDOWN_REASON_BATTERY_THERMAL
})
public @interface ShutdownReason {}
@@ -485,6 +501,18 @@
*/
public static final int SHUTDOWN_REASON_THERMAL_SHUTDOWN = 4;
+ /**
+ * constant for shutdown reason being low battery.
+ * @hide
+ */
+ public static final int SHUTDOWN_REASON_LOW_BATTERY = 5;
+
+ /**
+ * constant for shutdown reason being critical battery thermal state.
+ * @hide
+ */
+ public static final int SHUTDOWN_REASON_BATTERY_THERMAL = 6;
+
final Context mContext;
final IPowerManager mService;
final Handler mHandler;
@@ -1384,7 +1412,11 @@
*/
public void release(int flags) {
synchronized (mToken) {
- mInternalCount--;
+ if (mInternalCount > 0) {
+ // internal count must only be decreased if it is > 0 or state of
+ // the WakeLock object is broken.
+ mInternalCount--;
+ }
if ((flags & RELEASE_FLAG_TIMEOUT) == 0) {
mExternalCount--;
}
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index e11494d..f41848f 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -26,7 +26,6 @@
/** @hide */
public final class ServiceManager {
private static final String TAG = "ServiceManager";
-
private static IServiceManager sServiceManager;
private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
@@ -43,7 +42,7 @@
/**
* Returns a reference to a service with the given name.
- *
+ *
* @param name the name of the service to get
* @return a reference to the service, or <code>null</code> if the service doesn't exist
*/
@@ -79,35 +78,46 @@
/**
* Place a new @a service called @a name into the service
* manager.
- *
+ *
* @param name the name of the new service
* @param service the service object
*/
public static void addService(String name, IBinder service) {
- try {
- getIServiceManager().addService(name, service, false);
- } catch (RemoteException e) {
- Log.e(TAG, "error in addService", e);
- }
+ addService(name, service, false, IServiceManager.DUMP_PRIORITY_NORMAL);
}
/**
* Place a new @a service called @a name into the service
* manager.
- *
+ *
* @param name the name of the new service
* @param service the service object
* @param allowIsolated set to true to allow isolated sandboxed processes
* to access this service
*/
public static void addService(String name, IBinder service, boolean allowIsolated) {
+ addService(name, service, allowIsolated, IServiceManager.DUMP_PRIORITY_NORMAL);
+ }
+
+ /**
+ * Place a new @a service called @a name into the service
+ * manager.
+ *
+ * @param name the name of the new service
+ * @param service the service object
+ * @param allowIsolated set to true to allow isolated sandboxed processes
+ * @param dumpPriority supported dump priority levels as a bitmask
+ * to access this service
+ */
+ public static void addService(String name, IBinder service, boolean allowIsolated,
+ int dumpPriority) {
try {
- getIServiceManager().addService(name, service, allowIsolated);
+ getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
-
+
/**
* Retrieve an existing service called @a name from the
* service manager. Non-blocking.
@@ -133,7 +143,7 @@
*/
public static String[] listServices() {
try {
- return getIServiceManager().listServices();
+ return getIServiceManager().listServices(IServiceManager.DUMP_PRIORITY_ALL);
} catch (RemoteException e) {
Log.e(TAG, "error in listServices", e);
return null;
@@ -144,7 +154,7 @@
* This is only intended to be called when the process is first being brought
* up and bound by the activity manager. There is only one thread in the process
* at that time, so no locking is done.
- *
+ *
* @param cache the cache of service references
* @hide
*/
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index be24426..589b8c4 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -40,63 +40,65 @@
if (in != null) {
return in;
}
-
+
return new ServiceManagerProxy(obj);
}
-
+
public ServiceManagerNative()
{
attachInterface(this, descriptor);
}
-
+
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
{
try {
switch (code) {
- case IServiceManager.GET_SERVICE_TRANSACTION: {
- data.enforceInterface(IServiceManager.descriptor);
- String name = data.readString();
- IBinder service = getService(name);
- reply.writeStrongBinder(service);
- return true;
- }
-
- case IServiceManager.CHECK_SERVICE_TRANSACTION: {
- data.enforceInterface(IServiceManager.descriptor);
- String name = data.readString();
- IBinder service = checkService(name);
- reply.writeStrongBinder(service);
- return true;
- }
-
- case IServiceManager.ADD_SERVICE_TRANSACTION: {
- data.enforceInterface(IServiceManager.descriptor);
- String name = data.readString();
- IBinder service = data.readStrongBinder();
- boolean allowIsolated = data.readInt() != 0;
- addService(name, service, allowIsolated);
- return true;
- }
-
- case IServiceManager.LIST_SERVICES_TRANSACTION: {
- data.enforceInterface(IServiceManager.descriptor);
- String[] list = listServices();
- reply.writeStringArray(list);
- return true;
- }
-
- case IServiceManager.SET_PERMISSION_CONTROLLER_TRANSACTION: {
- data.enforceInterface(IServiceManager.descriptor);
- IPermissionController controller
- = IPermissionController.Stub.asInterface(
- data.readStrongBinder());
- setPermissionController(controller);
- return true;
- }
+ case IServiceManager.GET_SERVICE_TRANSACTION: {
+ data.enforceInterface(IServiceManager.descriptor);
+ String name = data.readString();
+ IBinder service = getService(name);
+ reply.writeStrongBinder(service);
+ return true;
+ }
+
+ case IServiceManager.CHECK_SERVICE_TRANSACTION: {
+ data.enforceInterface(IServiceManager.descriptor);
+ String name = data.readString();
+ IBinder service = checkService(name);
+ reply.writeStrongBinder(service);
+ return true;
+ }
+
+ case IServiceManager.ADD_SERVICE_TRANSACTION: {
+ data.enforceInterface(IServiceManager.descriptor);
+ String name = data.readString();
+ IBinder service = data.readStrongBinder();
+ boolean allowIsolated = data.readInt() != 0;
+ int dumpPriority = data.readInt();
+ addService(name, service, allowIsolated, dumpPriority);
+ return true;
+ }
+
+ case IServiceManager.LIST_SERVICES_TRANSACTION: {
+ data.enforceInterface(IServiceManager.descriptor);
+ int dumpPriority = data.readInt();
+ String[] list = listServices(dumpPriority);
+ reply.writeStringArray(list);
+ return true;
+ }
+
+ case IServiceManager.SET_PERMISSION_CONTROLLER_TRANSACTION: {
+ data.enforceInterface(IServiceManager.descriptor);
+ IPermissionController controller =
+ IPermissionController.Stub.asInterface(
+ data.readStrongBinder());
+ setPermissionController(controller);
+ return true;
+ }
}
} catch (RemoteException e) {
}
-
+
return false;
}
@@ -110,11 +112,11 @@
public ServiceManagerProxy(IBinder remote) {
mRemote = remote;
}
-
+
public IBinder asBinder() {
return mRemote;
}
-
+
public IBinder getService(String name) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -139,7 +141,7 @@
return binder;
}
- public void addService(String name, IBinder service, boolean allowIsolated)
+ public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -147,12 +149,13 @@
data.writeString(name);
data.writeStrongBinder(service);
data.writeInt(allowIsolated ? 1 : 0);
+ data.writeInt(dumpPriority);
mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
reply.recycle();
data.recycle();
}
-
- public String[] listServices() throws RemoteException {
+
+ public String[] listServices(int dumpPriority) throws RemoteException {
ArrayList<String> services = new ArrayList<String>();
int n = 0;
while (true) {
@@ -160,6 +163,7 @@
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeInt(n);
+ data.writeInt(dumpPriority);
n++;
try {
boolean res = mRemote.transact(LIST_SERVICES_TRANSACTION, data, reply, 0);
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 2528439..826ec1eb 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -154,12 +154,15 @@
// Byte 1: Thread-policy
/** @hide */
+ @TestApi
public static final int DETECT_DISK_WRITE = 0x01; // for ThreadPolicy
/** @hide */
+ @TestApi
public static final int DETECT_DISK_READ = 0x02; // for ThreadPolicy
/** @hide */
+ @TestApi
public static final int DETECT_NETWORK = 0x04; // for ThreadPolicy
/**
@@ -167,6 +170,7 @@
*
* @hide
*/
+ @TestApi
public static final int DETECT_CUSTOM = 0x08; // for ThreadPolicy
/**
@@ -174,9 +178,11 @@
*
* @hide
*/
+ @TestApi
public static final int DETECT_RESOURCE_MISMATCH = 0x10; // for ThreadPolicy
/** @hide */
+ @TestApi
public static final int DETECT_UNBUFFERED_IO = 0x20; // for ThreadPolicy
private static final int ALL_THREAD_DETECT_BITS =
@@ -194,6 +200,7 @@
*
* @hide
*/
+ @TestApi
public static final int DETECT_VM_CURSOR_LEAKS = 0x01 << 8; // for VmPolicy
/**
@@ -201,6 +208,7 @@
*
* @hide
*/
+ @TestApi
public static final int DETECT_VM_CLOSABLE_LEAKS = 0x02 << 8; // for VmPolicy
/**
@@ -208,25 +216,32 @@
*
* @hide
*/
+ @TestApi
public static final int DETECT_VM_ACTIVITY_LEAKS = 0x04 << 8; // for VmPolicy
/** @hide */
- private static final int DETECT_VM_INSTANCE_LEAKS = 0x08 << 8; // for VmPolicy
+ @TestApi
+ public static final int DETECT_VM_INSTANCE_LEAKS = 0x08 << 8; // for VmPolicy
/** @hide */
+ @TestApi
public static final int DETECT_VM_REGISTRATION_LEAKS = 0x10 << 8; // for VmPolicy
/** @hide */
- private static final int DETECT_VM_FILE_URI_EXPOSURE = 0x20 << 8; // for VmPolicy
+ @TestApi
+ public static final int DETECT_VM_FILE_URI_EXPOSURE = 0x20 << 8; // for VmPolicy
/** @hide */
- private static final int DETECT_VM_CLEARTEXT_NETWORK = 0x40 << 8; // for VmPolicy
+ @TestApi
+ public static final int DETECT_VM_CLEARTEXT_NETWORK = 0x40 << 8; // for VmPolicy
/** @hide */
- private static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 0x80 << 8; // for VmPolicy
+ @TestApi
+ public static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 0x80 << 8; // for VmPolicy
/** @hide */
- private static final int DETECT_VM_UNTAGGED_SOCKET = 0x80 << 24; // for VmPolicy
+ @TestApi
+ public static final int DETECT_VM_UNTAGGED_SOCKET = 0x80 << 24; // for VmPolicy
private static final int ALL_VM_DETECT_BITS =
DETECT_VM_CURSOR_LEAKS
@@ -1858,6 +1873,10 @@
}
/** @hide */
+ public static final String CLEARTEXT_DETECTED_MSG =
+ "Detected cleartext network traffic from UID ";
+
+ /** @hide */
public static void onCleartextNetworkDetected(byte[] firstPacket) {
byte[] rawAddr = null;
if (firstPacket != null) {
@@ -1873,14 +1892,10 @@
}
final int uid = android.os.Process.myUid();
- String msg = "Detected cleartext network traffic from UID " + uid;
+ String msg = CLEARTEXT_DETECTED_MSG + uid;
if (rawAddr != null) {
try {
- msg =
- "Detected cleartext network traffic from UID "
- + uid
- + " to "
- + InetAddress.getByAddress(rawAddr);
+ msg += " to " + InetAddress.getByAddress(rawAddr);
} catch (UnknownHostException ignored) {
}
}
@@ -1891,12 +1906,13 @@
}
/** @hide */
+ public static final String UNTAGGED_SOCKET_VIOLATION_MESSAGE =
+ "Untagged socket detected; use"
+ + " TrafficStats.setThreadSocketTag() to track all network usage";
+
+ /** @hide */
public static void onUntaggedSocket() {
- onVmPolicyViolation(
- null,
- new Throwable(
- "Untagged socket detected; use"
- + " TrafficStats.setThreadSocketTag() to track all network usage"));
+ onVmPolicyViolation(null, new Throwable(UNTAGGED_SOCKET_VIOLATION_MESSAGE));
}
// Map from VM violation fingerprint to uptime millis.
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 17f00c2..9369eeb 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -154,11 +154,21 @@
public abstract boolean isUserUnlocked(int userId);
/**
- * Return whether the given user is running
+ * Returns whether the given user is running
*/
public abstract boolean isUserRunning(int userId);
/**
+ * Returns whether the given user is initialized
+ */
+ public abstract boolean isUserInitialized(int userId);
+
+ /**
+ * Returns whether the given user exists
+ */
+ public abstract boolean exists(int userId);
+
+ /**
* Set user's running state
*/
public abstract void setUserState(int userId, int userState);
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 50855bb..e865ed1 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -45,103 +45,11 @@
*/
void unregisterListener(IStorageEventListener listener) = 1;
/**
- * Returns true if a USB mass storage host is connected
- */
- boolean isUsbMassStorageConnected() = 2;
- /**
- * Enables / disables USB mass storage. The caller should check actual
- * status of enabling/disabling USB mass storage via StorageEventListener.
- */
- void setUsbMassStorageEnabled(boolean enable) = 3;
- /**
- * Returns true if a USB mass storage host is enabled (media is shared)
- */
- boolean isUsbMassStorageEnabled() = 4;
- /**
- * Mount external storage at given mount point. Returns an int consistent
- * with StorageResultCode
- */
- int mountVolume(in String mountPoint) = 5;
- /**
- * Safely unmount external storage at given mount point. The unmount is an
- * asynchronous operation. Applications should register StorageEventListener
- * for storage related status changes.
- * @param mountPoint the mount point
- * @param force whether or not to forcefully unmount it (e.g. even if programs are using this
- * data currently)
- * @param removeEncryption whether or not encryption mapping should be removed from the volume.
- * This value implies {@code force}.
- */
- void unmountVolume(in String mountPoint, boolean force, boolean removeEncryption) = 6;
- /**
- * Format external storage given a mount point. Returns an int consistent
- * with StorageResultCode
- */
- int formatVolume(in String mountPoint) = 7;
- /**
- * Returns an array of pids with open files on the specified path.
- */
- int[] getStorageUsers(in String path) = 8;
- /**
- * Gets the state of a volume via its mountpoint.
- */
- String getVolumeState(in String mountPoint) = 9;
- /*
- * Creates a secure container with the specified parameters. Returns an int
- * consistent with StorageResultCode
- */
- int createSecureContainer(in String id, int sizeMb, in String fstype, in String key,
- int ownerUid, boolean external) = 10;
- /*
- * Finalize a container which has just been created and populated. After
- * finalization, the container is immutable. Returns an int consistent with
- * StorageResultCode
- */
- int finalizeSecureContainer(in String id) = 11;
- /*
- * Destroy a secure container, and free up all resources associated with it.
- * NOTE: Ensure all references are released prior to deleting. Returns an
- * int consistent with StorageResultCode
- */
- int destroySecureContainer(in String id, boolean force) = 12;
- /*
- * Mount a secure container with the specified key and owner UID. Returns an
- * int consistent with StorageResultCode
- */
- int mountSecureContainer(in String id, in String key, int ownerUid, boolean readOnly) = 13;
- /*
- * Unount a secure container. Returns an int consistent with
- * StorageResultCode
- */
- int unmountSecureContainer(in String id, boolean force) = 14;
- /*
- * Returns true if the specified container is mounted
- */
- boolean isSecureContainerMounted(in String id) = 15;
- /*
- * Rename an unmounted secure container. Returns an int consistent with
- * StorageResultCode
- */
- int renameSecureContainer(in String oldId, in String newId) = 16;
- /*
- * Returns the filesystem path of a mounted secure container.
- */
- String getSecureContainerPath(in String id) = 17;
- /**
- * Gets an Array of currently known secure container IDs
- */
- String[] getSecureContainerList() = 18;
- /**
* Shuts down the StorageManagerService and gracefully unmounts all external media.
* Invokes call back once the shutdown is complete.
*/
void shutdown(IStorageShutdownObserver observer) = 19;
/**
- * Call into StorageManagerService by PackageManager to notify that its done
- * processing the media status update request.
- */
- void finishMediaUpdate() = 20;
- /**
* Mounts an Opaque Binary Blob (OBB) with the specified decryption key and
* only allows the calling process's UID access to the contents.
* StorageManagerService will call back to the supplied IObbActionListener to inform
@@ -166,10 +74,6 @@
*/
String getMountedObbPath(in String rawPath) = 24;
/**
- * Returns whether or not the external storage is emulated.
- */
- boolean isExternalStorageEmulated() = 25;
- /**
* Decrypts any encrypted volumes.
*/
int decryptStorage(in String password) = 26;
@@ -186,14 +90,6 @@
*/
StorageVolume[] getVolumeList(int uid, in String packageName, int flags) = 29;
/**
- * Gets the path on the filesystem for the ASEC container itself.
- *
- * @param cid ASEC container ID
- * @return path to filesystem or {@code null} if it's not found
- * @throws RemoteException
- */
- String getSecureContainerFilesystemPath(in String cid) = 30;
- /**
* Determines the encryption state of the volume.
* @return a numerical value. See {@code ENCRYPTION_STATE_*} for possible
* values.
@@ -208,11 +104,6 @@
* may only be called by the system process.
*/
int verifyEncryptionPassword(in String password) = 32;
- /*
- * Fix permissions in a container which has just been created and populated.
- * Returns an int consistent with StorageResultCode
- */
- int fixPermissionsSecureContainer(in String id, int gid, in String filename) = 33;
/**
* Ensure that all directories along given path exist, creating parent
* directories as needed. Validates that given path is absolute and that it
@@ -247,7 +138,6 @@
* @return contents of field
*/
String getField(in String field) = 39;
- int resizeSecureContainer(in String id, int sizeMb, in String key) = 40;
/**
* Report the time of the last maintenance operation such as fstrim.
* @return Timestamp of the last maintenance operation, in the
@@ -260,7 +150,6 @@
* @throws RemoteException
*/
void runMaintenance() = 42;
- void waitForAsecScan() = 43;
DiskInfo[] getDisks() = 44;
VolumeInfo[] getVolumes(int flags) = 45;
VolumeRecord[] getVolumeRecords(int flags) = 46;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a5c55ba..eca2fcd 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10200,7 +10200,7 @@
"allow_user_switching_when_system_user_locked";
/**
- * Boot count since the device starts running APK level 24.
+ * Boot count since the device starts running API level 24.
* <p>
* Type: int
*/
@@ -10894,6 +10894,26 @@
*/
public static final String ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE =
"enable_deletion_helper_no_threshold_toggle";
+
+ /**
+ * The list of snooze options for notifications
+ * This is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "default=60,options_array=15:30:60:120"
+ *
+ * The following keys are supported:
+ *
+ * <pre>
+ * default (int)
+ * options_array (string)
+ * </pre>
+ *
+ * All delays in integer minutes. Array order is respected.
+ * Options will be used in order up to the maximum allowed by the UI.
+ * @hide
+ */
+ public static final String NOTIFICATION_SNOOZE_OPTIONS =
+ "notification_snooze_options";
}
/**
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 3e08dcf..2e59f6c 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -187,7 +187,7 @@
* protect a dataset that contains sensitive information by requiring dataset authentication
* (see {@link Dataset.Builder#setAuthentication(android.content.IntentSender)}), and to include
* info about the "primary" field of the partition in the custom presentation for "secondary"
- * fields — that would prevent a malicious app from getting the "primary" fields without the
+ * fields—that would prevent a malicious app from getting the "primary" fields without the
* user realizing they're being released (for example, a malicious app could have fields for a
* credit card number, verification code, and expiration date crafted in a way that just the latter
* is visible; by explicitly indicating the expiration date is related to a given credit card
@@ -305,7 +305,7 @@
* <li>Use the {@link android.app.assist.AssistStructure.ViewNode#getWebDomain()} to get the
* source of the document.
* <li>Get the canonical domain using the
- * <a href="https://publicsuffix.org/>Public Suffix List</a> (see example below).
+ * <a href="https://publicsuffix.org/">Public Suffix List</a> (see example below).
* <li>Use <a href="https://developers.google.com/digital-asset-links/">Digital Asset Links</a>
* to obtain the package name and certificate fingerprint of the package corresponding to
* the canonical domain.
@@ -503,13 +503,19 @@
@NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
/**
- * Called when user requests service to save the fields of a screen.
+ * Called when the user requests the service to save the contents of a screen.
*
* <p>Service must call one of the {@link SaveCallback} methods (like
* {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
- * to notify the result of the request.
+ * to notify the Android System of the result of the request.
*
- * <p><b>Note:</b> To retrieve the actual value of the field, the service should call
+ * <p>If the service could not handle the request right away—for example, because it must
+ * launch an activity asking the user to authenticate first or because the network is
+ * down—the service could keep the {@link SaveRequest request} and reuse it later,
+ * but the service must call {@link SaveCallback#onSuccess()} right away.
+ *
+ * <p><b>Note:</b> To retrieve the actual value of fields input by the user, the service
+ * should call
* {@link android.app.assist.AssistStructure.ViewNode#getAutofillValue()}; if it calls
* {@link android.app.assist.AssistStructure.ViewNode#getText()} or other methods, there is no
* guarantee such method will return the most recent value of the field.
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index f147400..5c7388f 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -146,4 +146,9 @@
public String getSettingsActivity() {
return mSettingsActivity;
}
+
+ @Override
+ public String toString() {
+ return mServiceInfo == null ? "null" : mServiceInfo.toString();
+ }
}
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index cb341b1..ef9598a 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -29,32 +29,77 @@
import com.android.internal.util.Preconditions;
+import java.io.Serializable;
import java.util.ArrayList;
+import java.util.regex.Pattern;
/**
- * A dataset object represents a group of key/value pairs used to autofill parts of a screen.
+ * A dataset object represents a group of fields (key / value pairs) used to autofill parts of a
+ * screen.
*
- * <p>In its simplest form, a dataset contains one or more key / value pairs (comprised of
- * {@link AutofillId} and {@link AutofillValue} respectively); and one or more
- * {@link RemoteViews presentation} for these pairs (a pair could have its own
- * {@link RemoteViews presentation}, or use the default {@link RemoteViews presentation} associated
- * with the whole dataset). When an autofill service returns datasets in a {@link FillResponse}
+ * <a name="BasicUsage"></a>
+ * <h3>Basic usage</h3>
+ *
+ * <p>In its simplest form, a dataset contains one or more fields (comprised of
+ * an {@link AutofillId id}, a {@link AutofillValue value}, and an optional filter
+ * {@link Pattern regex}); and one or more {@link RemoteViews presentations} for these fields
+ * (each field could have its own {@link RemoteViews presentation}, or use the default
+ * {@link RemoteViews presentation} associated with the whole dataset).
+ *
+ * <p>When an autofill service returns datasets in a {@link FillResponse}
* and the screen input is focused in a view that is present in at least one of these datasets,
- * the Android System displays a UI affordance containing the {@link RemoteViews presentation} of
+ * the Android System displays a UI containing the {@link RemoteViews presentation} of
* all datasets pairs that have that view's {@link AutofillId}. Then, when the user selects a
- * dataset from the affordance, all views in that dataset are autofilled.
+ * dataset from the UI, all views in that dataset are autofilled.
*
- * <p>In a more sophisticated form, the dataset value can be protected until the user authenticates
- * the dataset - see {@link Dataset.Builder#setAuthentication(IntentSender)}.
+ * <a name="Authentication"></a>
+ * <h3>Dataset authentication</h3>
*
- * @see android.service.autofill.AutofillService for more information and examples about the
- * role of datasets in the autofill workflow.
+ * <p>In a more sophisticated form, the dataset values can be protected until the user authenticates
+ * the dataset—in that case, when a dataset is selected by the user, the Android System
+ * launches an intent set by the service to "unlock" the dataset.
+ *
+ * <p>For example, when a data set contains credit card information (such as number,
+ * expiration date, and verification code), you could provide a dataset presentation saying
+ * "Tap to authenticate". Then when the user taps that option, you would launch an activity asking
+ * the user to enter the credit card code, and if the user enters a valid code, you could then
+ * "unlock" the dataset.
+ *
+ * <p>You can also use authenticated datasets to offer an interactive UI for the user. For example,
+ * if the activity being autofilled is an account creation screen, you could use an authenticated
+ * dataset to automatically generate a random password for the user.
+ *
+ * <p>See {@link Dataset.Builder#setAuthentication(IntentSender)} for more details about the dataset
+ * authentication mechanism.
+ *
+ * <a name="Filtering"></a>
+ * <h3>Filtering</h3>
+ * <p>The autofill UI automatically changes which values are shown based on value of the view
+ * anchoring it, following the rules below:
+ * <ol>
+ * <li>If the view's {@link android.view.View#getAutofillValue() autofill value} is not
+ * {@link AutofillValue#isText() text} or is empty, all datasets are shown.
+ * <li>Datasets that have a filter regex (set through
+ * {@link Dataset.Builder#setValue(AutofillId, AutofillValue, Pattern)} or
+ * {@link Dataset.Builder#setValue(AutofillId, AutofillValue, Pattern, RemoteViews)}) and whose
+ * regex matches the view's text value converted to lower case are shown.
+ * <li>Datasets that do not require authentication, have a field value that is
+ * {@link AutofillValue#isText() text} and whose {@link AutofillValue#getTextValue() value} starts
+ * with the lower case value of the view's text are shown.
+ * <li>All other datasets are hidden.
+ * </ol>
+ *
+ * <a name="MoreInfo"></a>
+ * <h3>More information</h3>
+ * <p>See {@link android.service.autofill.AutofillService} for more information and examples about
+ * the role of datasets in the autofill workflow.
*/
public final class Dataset implements Parcelable {
private final ArrayList<AutofillId> mFieldIds;
private final ArrayList<AutofillValue> mFieldValues;
private final ArrayList<RemoteViews> mFieldPresentations;
+ private final ArrayList<Pattern> mFieldFilters;
private final RemoteViews mPresentation;
private final IntentSender mAuthentication;
@Nullable String mId;
@@ -63,6 +108,7 @@
mFieldIds = builder.mFieldIds;
mFieldValues = builder.mFieldValues;
mFieldPresentations = builder.mFieldPresentations;
+ mFieldFilters = builder.mFieldFilters;
mPresentation = builder.mPresentation;
mAuthentication = builder.mAuthentication;
mId = builder.mId;
@@ -85,6 +131,12 @@
}
/** @hide */
+ @Nullable
+ public Pattern getFilter(int index) {
+ return mFieldFilters.get(index);
+ }
+
+ /** @hide */
public @Nullable IntentSender getAuthentication() {
return mAuthentication;
}
@@ -103,6 +155,8 @@
.append(", fieldValues=").append(mFieldValues)
.append(", fieldPresentations=")
.append(mFieldPresentations == null ? 0 : mFieldPresentations.size())
+ .append(", fieldFilters=")
+ .append(mFieldFilters == null ? 0 : mFieldFilters.size())
.append(", hasPresentation=").append(mPresentation != null)
.append(", hasAuthentication=").append(mAuthentication != null)
.append(']').toString();
@@ -127,6 +181,7 @@
private ArrayList<AutofillId> mFieldIds;
private ArrayList<AutofillValue> mFieldValues;
private ArrayList<RemoteViews> mFieldPresentations;
+ private ArrayList<Pattern> mFieldFilters;
private RemoteViews mPresentation;
private IntentSender mAuthentication;
private boolean mDestroyed;
@@ -182,12 +237,12 @@
* credit card information without the CVV for the data set in the {@link FillResponse
* response} then the returned data set should contain the CVV entry.
*
- * <p><b>NOTE:</b> Do not make the provided pending intent
+ * <p><b>Note:</b> Do not make the provided pending intent
* immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
* platform needs to fill in the authentication arguments.
*
* @param authentication Intent to an activity with your authentication flow.
- * @return This builder.
+ * @return this builder.
*
* @see android.app.PendingIntent
*/
@@ -214,11 +269,10 @@
*
* @param id id for this dataset or {@code null} to unset.
*
- * @return This builder.
+ * @return this builder.
*/
public @NonNull Builder setId(@Nullable String id) {
throwIfDestroyed();
-
mId = id;
return this;
}
@@ -230,17 +284,16 @@
* android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
* @param value value to be autofilled. Pass {@code null} if you do not have the value
* but the target view is a logical part of the dataset. For example, if
- * the dataset needs an authentication and you have no access to the value.
- * @return This builder.
+ * the dataset needs authentication and you have no access to the value.
+ * @return this builder.
* @throws IllegalStateException if the builder was constructed without a
* {@link RemoteViews presentation}.
*/
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value) {
throwIfDestroyed();
- if (mPresentation == null) {
- throw new IllegalStateException("Dataset presentation not set on constructor");
- }
- setValueAndPresentation(id, value, null);
+ Preconditions.checkState(mPresentation != null,
+ "Dataset presentation not set on constructor");
+ setLifeTheUniverseAndEverything(id, value, null, null);
return this;
}
@@ -250,23 +303,81 @@
*
* @param id id returned by {@link
* android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
- * @param value value to be auto filled. Pass {@code null} if you do not have the value
+ * @param value the value to be autofilled. Pass {@code null} if you do not have the value
* but the target view is a logical part of the dataset. For example, if
- * the dataset needs an authentication and you have no access to the value.
- * Filtering matches any user typed string to {@code null} values.
- * @param presentation The presentation used to visualize this field.
- * @return This builder.
+ * the dataset needs authentication and you have no access to the value.
+ * @param presentation the presentation used to visualize this field.
+ * @return this builder.
+ *
*/
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@NonNull RemoteViews presentation) {
throwIfDestroyed();
Preconditions.checkNotNull(presentation, "presentation cannot be null");
- setValueAndPresentation(id, value, presentation);
+ setLifeTheUniverseAndEverything(id, value, presentation, null);
return this;
}
- private void setValueAndPresentation(AutofillId id, AutofillValue value,
- RemoteViews presentation) {
+ /**
+ * Sets the value of a field using an <a href="#Filtering">explicit filter</a>.
+ *
+ * <p>This method is typically used when the dataset is not authenticated and the field
+ * value is not {@link AutofillValue#isText() text} but the service still wants to allow
+ * the user to filter it out.
+ *
+ * @param id id returned by {@link
+ * android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+ * @param value the value to be autofilled. Pass {@code null} if you do not have the value
+ * but the target view is a logical part of the dataset. For example, if
+ * the dataset needs authentication and you have no access to the value.
+ * @param filter regex used to determine if the dataset should be shown in the autofill UI.
+ *
+ * @return this builder.
+ * @throws IllegalStateException if the builder was constructed without a
+ * {@link RemoteViews presentation}.
+ */
+ public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
+ @NonNull Pattern filter) {
+ throwIfDestroyed();
+ Preconditions.checkNotNull(filter, "filter cannot be null");
+ Preconditions.checkState(mPresentation != null,
+ "Dataset presentation not set on constructor");
+ setLifeTheUniverseAndEverything(id, value, null, filter);
+ return this;
+ }
+
+ /**
+ * Sets the value of a field, using a custom {@link RemoteViews presentation} to
+ * visualize it and a <a href="#Filtering">explicit filter</a>.
+ *
+ * <p>Typically used to allow filtering on
+ * {@link Dataset.Builder#setAuthentication(IntentSender) authenticated datasets}. For
+ * example, if the dataset represents a credit card number and the service does not want to
+ * show the "Tap to authenticate" message until the user tapped 4 digits, in which case
+ * the filter would be {@code Pattern.compile("\\d.{4,}")}.
+ *
+ * @param id id returned by {@link
+ * android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+ * @param value the value to be autofilled. Pass {@code null} if you do not have the value
+ * but the target view is a logical part of the dataset. For example, if
+ * the dataset needs authentication and you have no access to the value.
+ * @param presentation the presentation used to visualize this field.
+ * @param filter regex used to determine if the dataset should be shown in the autofill UI.
+ *
+ * @return this builder.
+ */
+ public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
+ @NonNull Pattern filter, @NonNull RemoteViews presentation) {
+ throwIfDestroyed();
+ Preconditions.checkNotNull(filter, "filter cannot be null");
+ Preconditions.checkNotNull(presentation, "presentation cannot be null");
+ setLifeTheUniverseAndEverything(id, value, presentation, filter);
+ return this;
+ }
+
+ private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
+ @Nullable AutofillValue value, @Nullable RemoteViews presentation,
+ @Nullable Pattern filter) {
Preconditions.checkNotNull(id, "id cannot be null");
if (mFieldIds != null) {
final int existingIdx = mFieldIds.indexOf(id);
@@ -279,10 +390,12 @@
mFieldIds = new ArrayList<>();
mFieldValues = new ArrayList<>();
mFieldPresentations = new ArrayList<>();
+ mFieldFilters = new ArrayList<>();
}
mFieldIds.add(id);
mFieldValues.add(value);
mFieldPresentations.add(presentation);
+ mFieldFilters.add(filter);
}
/**
@@ -290,8 +403,9 @@
*
* <p>You should not interact with this builder once this method is called.
*
- * <p>It is required that you specify at least one field before calling this method. It's
- * also mandatory to provide a presentation view to visualize the data set in the UI.
+ * @throws IllegalStateException if no field was set (through
+ * {@link #setValue(AutofillId, AutofillValue)} or
+ * {@link #setValue(AutofillId, AutofillValue, RemoteViews)}).
*
* @return The built dataset.
*/
@@ -299,7 +413,7 @@
throwIfDestroyed();
mDestroyed = true;
if (mFieldIds == null) {
- throw new IllegalArgumentException("at least one value must be set");
+ throw new IllegalStateException("at least one value must be set");
}
return new Dataset(this);
}
@@ -323,9 +437,10 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeParcelable(mPresentation, flags);
- parcel.writeTypedArrayList(mFieldIds, flags);
- parcel.writeTypedArrayList(mFieldValues, flags);
+ parcel.writeTypedList(mFieldIds, flags);
+ parcel.writeTypedList(mFieldValues, flags);
parcel.writeParcelableList(mFieldPresentations, flags);
+ parcel.writeSerializable(mFieldFilters);
parcel.writeParcelable(mAuthentication, flags);
parcel.writeString(mId);
}
@@ -340,10 +455,14 @@
final Builder builder = (presentation == null)
? new Builder()
: new Builder(presentation);
- final ArrayList<AutofillId> ids = parcel.readTypedArrayList(null);
- final ArrayList<AutofillValue> values = parcel.readTypedArrayList(null);
+ final ArrayList<AutofillId> ids = parcel.createTypedArrayList(AutofillId.CREATOR);
+ final ArrayList<AutofillValue> values =
+ parcel.createTypedArrayList(AutofillValue.CREATOR);
final ArrayList<RemoteViews> presentations = new ArrayList<>();
parcel.readParcelableList(presentations, null);
+ @SuppressWarnings("unchecked")
+ final ArrayList<Serializable> filters =
+ (ArrayList<Serializable>) parcel.readSerializable();
final int idCount = (ids != null) ? ids.size() : 0;
final int valueCount = (values != null) ? values.size() : 0;
for (int i = 0; i < idCount; i++) {
@@ -351,7 +470,8 @@
final AutofillValue value = (valueCount > i) ? values.get(i) : null;
final RemoteViews fieldPresentation = presentations.isEmpty() ? null
: presentations.get(i);
- builder.setValueAndPresentation(id, value, fieldPresentation);
+ final Pattern filter = (Pattern) filters.get(i);
+ builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation, filter);
}
builder.setAuthentication(parcel.readParcelable(null));
builder.setId(parcel.readString());
diff --git a/core/java/android/service/autofill/ImageTransformation.java b/core/java/android/service/autofill/ImageTransformation.java
index 2151f74..4afda24 100644
--- a/core/java/android/service/autofill/ImageTransformation.java
+++ b/core/java/android/service/autofill/ImageTransformation.java
@@ -20,11 +20,12 @@
import android.annotation.DrawableRes;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import android.util.Log;
-import android.util.Pair;
import android.view.autofill.AutofillId;
import android.widget.ImageView;
import android.widget.RemoteViews;
@@ -43,9 +44,9 @@
*
* <pre class="prettyprint">
* new ImageTransformation.Builder(ccNumberId, Pattern.compile("^4815.*$"),
- * R.drawable.ic_credit_card_logo1)
- * .addOption(Pattern.compile("^1623.*$"), R.drawable.ic_credit_card_logo2)
- * .addOption(Pattern.compile("^42.*$"), R.drawable.ic_credit_card_logo3)
+ * R.drawable.ic_credit_card_logo1, "Brand 1")
+ * .addOption(Pattern.compile("^1623.*$"), R.drawable.ic_credit_card_logo2, "Brand 2")
+ * .addOption(Pattern.compile("^42.*$"), R.drawable.ic_credit_card_logo3, "Brand 3")
* .build();
* </pre>
*
@@ -59,7 +60,7 @@
private static final String TAG = "ImageTransformation";
private final AutofillId mId;
- private final ArrayList<Pair<Pattern, Integer>> mOptions;
+ private final ArrayList<Option> mOptions;
private ImageTransformation(Builder builder) {
mId = builder.mId;
@@ -82,17 +83,21 @@
}
for (int i = 0; i < size; i++) {
- final Pair<Pattern, Integer> option = mOptions.get(i);
+ final Option option = mOptions.get(i);
try {
- if (option.first.matcher(value).matches()) {
+ if (option.pattern.matcher(value).matches()) {
Log.d(TAG, "Found match at " + i + ": " + option);
- parentTemplate.setImageViewResource(childViewId, option.second);
+ parentTemplate.setImageViewResource(childViewId, option.resId);
+ if (option.contentDescription != null) {
+ parentTemplate.setContentDescription(childViewId,
+ option.contentDescription);
+ }
return;
}
} catch (Exception e) {
// Do not log full exception to avoid PII leaking
- Log.w(TAG, "Error matching regex #" + i + "(" + option.first.pattern() + ") on id "
- + option.second + ": " + e.getClass());
+ Log.w(TAG, "Error matching regex #" + i + "(" + option.pattern + ") on id "
+ + option.resId + ": " + e.getClass());
throw e;
}
@@ -105,25 +110,44 @@
*/
public static class Builder {
private final AutofillId mId;
- private final ArrayList<Pair<Pattern, Integer>> mOptions = new ArrayList<>();
+ private final ArrayList<Option> mOptions = new ArrayList<>();
private boolean mDestroyed;
/**
- * Create a new builder for a autofill id and add a first option.
+ * Creates a new builder for a autofill id and add a first option.
*
* @param id id of the screen field that will be used to evaluate whether the image should
* be used.
* @param regex regular expression defining what should be matched to use this image.
* @param resId resource id of the image (in the autofill service's package). The
* {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
+ *
+ * @deprecated use
+ * {@link #ImageTransformation.Builder(AutofillId, Pattern, int, CharSequence)} instead.
*/
+ @Deprecated
public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId) {
mId = Preconditions.checkNotNull(id);
-
addOption(regex, resId);
}
/**
+ * Creates a new builder for a autofill id and add a first option.
+ *
+ * @param id id of the screen field that will be used to evaluate whether the image should
+ * be used.
+ * @param regex regular expression defining what should be matched to use this image.
+ * @param resId resource id of the image (in the autofill service's package). The
+ * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
+ * @param contentDescription content description to be applied in the child view.
+ */
+ public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId,
+ @NonNull CharSequence contentDescription) {
+ mId = Preconditions.checkNotNull(id);
+ addOption(regex, resId, contentDescription);
+ }
+
+ /**
* Adds an option to replace the child view with a different image when the regex matches.
*
* @param regex regular expression defining what should be matched to use this image.
@@ -131,17 +155,43 @@
* {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
*
* @return this build
+ *
+ * @deprecated use {@link #addOption(Pattern, int, CharSequence)} instead.
*/
+ @Deprecated
public Builder addOption(@NonNull Pattern regex, @DrawableRes int resId) {
+ addOptionInternal(regex, resId, null);
+ return this;
+ }
+
+ /**
+ * Adds an option to replace the child view with a different image and content description
+ * when the regex matches.
+ *
+ * @param regex regular expression defining what should be matched to use this image.
+ * @param resId resource id of the image (in the autofill service's package). The
+ * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
+ * @param contentDescription content description to be applied in the child view.
+ *
+ * @return this build
+ */
+ public Builder addOption(@NonNull Pattern regex, @DrawableRes int resId,
+ @NonNull CharSequence contentDescription) {
+ addOptionInternal(regex, resId, Preconditions.checkNotNull(contentDescription));
+ return this;
+ }
+
+ private void addOptionInternal(@NonNull Pattern regex, @DrawableRes int resId,
+ @Nullable CharSequence contentDescription) {
throwIfDestroyed();
Preconditions.checkNotNull(regex);
Preconditions.checkArgument(resId != 0);
- mOptions.add(new Pair<>(regex, resId));
- return this;
+ mOptions.add(new Option(regex, resId, contentDescription));
}
+
/**
* Creates a new {@link ImageTransformation} instance.
*/
@@ -178,15 +228,18 @@
parcel.writeParcelable(mId, flags);
final int size = mOptions.size();
- final Pattern[] regexs = new Pattern[size];
+ final Pattern[] patterns = new Pattern[size];
final int[] resIds = new int[size];
+ final CharSequence[] contentDescriptions = new String[size];
for (int i = 0; i < size; i++) {
- Pair<Pattern, Integer> regex = mOptions.get(i);
- regexs[i] = regex.first;
- resIds[i] = regex.second;
+ final Option option = mOptions.get(i);
+ patterns[i] = option.pattern;
+ resIds[i] = option.resId;
+ contentDescriptions[i] = option.contentDescription;
}
- parcel.writeSerializable(regexs);
+ parcel.writeSerializable(patterns);
parcel.writeIntArray(resIds);
+ parcel.writeCharSequenceArray(contentDescriptions);
}
public static final Parcelable.Creator<ImageTransformation> CREATOR =
@@ -197,15 +250,22 @@
final Pattern[] regexs = (Pattern[]) parcel.readSerializable();
final int[] resIds = parcel.createIntArray();
+ final CharSequence[] contentDescriptions = parcel.readCharSequenceArray();
// Always go through the builder to ensure the data ingested by the system obeys the
// contract of the builder to avoid attacks using specially crafted parcels.
- final ImageTransformation.Builder builder = new ImageTransformation.Builder(id,
- regexs[0], resIds[0]);
+ final CharSequence contentDescription = contentDescriptions[0];
+ final ImageTransformation.Builder builder = (contentDescription != null)
+ ? new ImageTransformation.Builder(id, regexs[0], resIds[0], contentDescription)
+ : new ImageTransformation.Builder(id, regexs[0], resIds[0]);
final int size = regexs.length;
for (int i = 1; i < size; i++) {
- builder.addOption(regexs[i], resIds[i]);
+ if (contentDescriptions[i] != null) {
+ builder.addOption(regexs[i], resIds[i], contentDescriptions[i]);
+ } else {
+ builder.addOption(regexs[i], resIds[i]);
+ }
}
return builder.build();
@@ -216,4 +276,16 @@
return new ImageTransformation[size];
}
};
+
+ private static final class Option {
+ public final Pattern pattern;
+ public final int resId;
+ public final CharSequence contentDescription;
+
+ Option(Pattern pattern, int resId, CharSequence contentDescription) {
+ this.pattern = pattern;
+ this.resId = resId;
+ this.contentDescription = TextUtils.trimNoCopySpans(contentDescription);
+ }
+ }
}
diff --git a/core/java/android/service/autofill/InternalSanitizer.java b/core/java/android/service/autofill/InternalSanitizer.java
new file mode 100644
index 0000000..95d2f66
--- /dev/null
+++ b/core/java/android/service/autofill/InternalSanitizer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 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.service.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcelable;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Superclass of all sanitizers the system understands. As this is not public all public subclasses
+ * have to implement {@link Sanitizer} again.
+ *
+ * @hide
+ */
+@TestApi
+public abstract class InternalSanitizer implements Sanitizer, Parcelable {
+
+ /**
+ * Sanitizes an {@link AutofillValue}.
+ *
+ * @hide
+ */
+ public abstract AutofillValue sanitize(@NonNull AutofillValue value);
+}
diff --git a/core/java/android/service/autofill/Sanitizer.java b/core/java/android/service/autofill/Sanitizer.java
new file mode 100644
index 0000000..38757ac
--- /dev/null
+++ b/core/java/android/service/autofill/Sanitizer.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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.service.autofill;
+
+/**
+ * Helper class used to sanitize user input before using it in a save request.
+ *
+ * <p>Typically used to avoid displaying the save UI for values that are autofilled but reformatted
+ * by the app—for example, if the autofill service sends a credit card number
+ * value as "004815162342108" and the app automatically changes it to "0048 1516 2342 108".
+ */
+public interface Sanitizer {
+}
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 3a70138..7207f1d 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -34,9 +34,13 @@
/**
* Notifies the Android System that an
- * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} was successfully fulfilled
+ * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} was successfully handled
* by the service.
*
+ * <p>If the service could not handle the request right away—for example, because it must
+ * launch an activity asking the user to authenticate first or because the network is
+ * down—it should still call {@link #onSuccess()}.
+ *
* @throws RuntimeException if an error occurred while calling the Android System.
*/
public void onSuccess() {
@@ -51,9 +55,16 @@
/**
* Notifies the Android System that an
- * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} could not be fulfilled
+ * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} could not be handled
* by the service.
*
+ * <p>This method should only be called when the service could not handle the request right away
+ * and could not recover or retry it. If the service could retry or recover, it could keep
+ * the {@link SaveRequest} and call {@link #onSuccess()} instead.
+ *
+ * <p><b>Note:</b> The Android System displays an UI with the supplied error message; if
+ * you prefer to show your own message, call {@link #onSuccess()} instead.
+ *
* @param message error message to be displayed to the user.
*
* @throws RuntimeException if an error occurred while calling the Android System.
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index e0a0730..1b9240c 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -25,6 +25,8 @@
import android.content.IntentSender;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.DebugUtils;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
@@ -232,6 +234,8 @@
private final int mFlags;
private final CustomDescription mCustomDescription;
private final InternalValidator mValidator;
+ private final InternalSanitizer[] mSanitizerKeys;
+ private final AutofillId[][] mSanitizerValues;
private SaveInfo(Builder builder) {
mType = builder.mType;
@@ -243,6 +247,18 @@
mFlags = builder.mFlags;
mCustomDescription = builder.mCustomDescription;
mValidator = builder.mValidator;
+ if (builder.mSanitizers == null) {
+ mSanitizerKeys = null;
+ mSanitizerValues = null;
+ } else {
+ final int size = builder.mSanitizers.size();
+ mSanitizerKeys = new InternalSanitizer[size];
+ mSanitizerValues = new AutofillId[size][];
+ for (int i = 0; i < size; i++) {
+ mSanitizerKeys[i] = builder.mSanitizers.keyAt(i);
+ mSanitizerValues[i] = builder.mSanitizers.valueAt(i);
+ }
+ }
}
/** @hide */
@@ -292,6 +308,18 @@
return mValidator;
}
+ /** @hide */
+ @Nullable
+ public InternalSanitizer[] getSanitizerKeys() {
+ return mSanitizerKeys;
+ }
+
+ /** @hide */
+ @Nullable
+ public AutofillId[][] getSanitizerValues() {
+ return mSanitizerValues;
+ }
+
/**
* A builder for {@link SaveInfo} objects.
*/
@@ -307,6 +335,9 @@
private int mFlags;
private CustomDescription mCustomDescription;
private InternalValidator mValidator;
+ private ArrayMap<InternalSanitizer, AutofillId[]> mSanitizers;
+ // Set used to validate against duplicate ids.
+ private ArraySet<AutofillId> mSanitizerIds;
/**
* Creates a new builder.
@@ -530,6 +561,61 @@
}
/**
+ * Adds a sanitizer for one or more field.
+ *
+ * <p>When a sanitizer is set for a field, the {@link AutofillValue} is sent to the
+ * sanitizer before a save request is <a href="#TriggeringSaveRequest">triggered</a>.
+ *
+ * <p>Typically used to avoid displaying the save UI for values that are autofilled but
+ * reformattedby the app. For example, to remove spaces between every 4 digits of a
+ * credit card number:
+ *
+ * <pre class="prettyprint">
+ * builder.addSanitizer(
+ * new TextValueSanitizer(Pattern.compile("^(\\d{4}\s?\\d{4}\s?\\d{4}\s?\\d{4})$"),
+ * "$1$2$3$4"), ccNumberId);
+ * </pre>
+ *
+ * <p>The same sanitizer can be reused to sanitize multiple fields. For example, to trim
+ * both the username and password fields:
+ *
+ * <pre class="prettyprint">
+ * builder.addSanitizer(
+ * new TextValueSanitizer(Pattern.compile("^\\s*(.*)\\s*$"), "$1"),
+ * usernameId, passwordId);
+ * </pre>
+ *
+ * @param sanitizer an implementation provided by the Android System.
+ * @param ids id of fields whose value will be sanitized.
+ * @return this builder.
+ *
+ * @throws IllegalArgumentException if a sanitizer for any of the {@code ids} has already
+ * been added or if {@code ids} is empty.
+ */
+ public @NonNull Builder addSanitizer(@NonNull Sanitizer sanitizer,
+ @NonNull AutofillId... ids) {
+ throwIfDestroyed();
+ Preconditions.checkArgument(!ArrayUtils.isEmpty(ids), "ids cannot be empty or null");
+ Preconditions.checkArgument((sanitizer instanceof InternalSanitizer),
+ "not provided by Android System: " + sanitizer);
+
+ if (mSanitizers == null) {
+ mSanitizers = new ArrayMap<>();
+ mSanitizerIds = new ArraySet<>(ids.length);
+ }
+
+ // Check for duplicates first.
+ for (AutofillId id : ids) {
+ Preconditions.checkArgument(!mSanitizerIds.contains(id), "already added %s", id);
+ mSanitizerIds.add(id);
+ }
+
+ mSanitizers.put((InternalSanitizer) sanitizer, ids);
+
+ return this;
+ }
+
+ /**
* Builds a new {@link SaveInfo} instance.
*
* @throws IllegalStateException if no
@@ -569,6 +655,10 @@
.append(", mFlags=").append(mFlags)
.append(", mCustomDescription=").append(mCustomDescription)
.append(", validation=").append(mValidator)
+ .append(", sanitizerKeys=")
+ .append(mSanitizerKeys == null ? "N/A:" : mSanitizerKeys.length)
+ .append(", sanitizerValues=")
+ .append(mSanitizerValues == null ? "N/A:" : mSanitizerValues.length)
.append("]").toString();
}
@@ -591,6 +681,12 @@
parcel.writeCharSequence(mDescription);
parcel.writeParcelable(mCustomDescription, flags);
parcel.writeParcelable(mValidator, flags);
+ parcel.writeParcelableArray(mSanitizerKeys, flags);
+ if (mSanitizerKeys != null) {
+ for (int i = 0; i < mSanitizerValues.length; i++) {
+ parcel.writeParcelableArray(mSanitizerValues[i], flags);
+ }
+ }
parcel.writeInt(mFlags);
}
@@ -621,6 +717,16 @@
if (validator != null) {
builder.setValidator(validator);
}
+ final InternalSanitizer[] sanitizers =
+ parcel.readParcelableArray(null, InternalSanitizer.class);
+ if (sanitizers != null) {
+ final int size = sanitizers.length;
+ for (int i = 0; i < size; i++) {
+ final AutofillId[] autofillIds =
+ parcel.readParcelableArray(null, AutofillId.class);
+ builder.addSanitizer(sanitizers[i], autofillIds);
+ }
+ }
builder.setFlags(parcel.readInt());
return builder.build();
}
diff --git a/core/java/android/service/autofill/SaveRequest.java b/core/java/android/service/autofill/SaveRequest.java
index 1a6c5b0..65fdb5c 100644
--- a/core/java/android/service/autofill/SaveRequest.java
+++ b/core/java/android/service/autofill/SaveRequest.java
@@ -48,7 +48,8 @@
}
private SaveRequest(@NonNull Parcel parcel) {
- this(parcel.readTypedArrayList(null), parcel.readBundle(), parcel.createStringArrayList());
+ this(parcel.createTypedArrayList(FillContext.CREATOR),
+ parcel.readBundle(), parcel.createStringArrayList());
}
/**
@@ -84,7 +85,7 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeTypedArrayList(mFillContexts, flags);
+ parcel.writeTypedList(mFillContexts, flags);
parcel.writeBundle(mClientState);
parcel.writeStringList(mDatasetIds);
}
diff --git a/core/java/android/service/autofill/TextValueSanitizer.java b/core/java/android/service/autofill/TextValueSanitizer.java
new file mode 100644
index 0000000..12e85b1
--- /dev/null
+++ b/core/java/android/service/autofill/TextValueSanitizer.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 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.service.autofill;
+
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Slog;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Sanitizes a text {@link AutofillValue} using a regular expression (regex) substitution.
+ *
+ * <p>For example, to remove spaces from groups of 4-digits in a credit card:
+ *
+ * <pre class="prettyprint">
+ * new TextValueSanitizer(Pattern.compile("^(\\d{4}\s?\\d{4}\s?\\d{4}\s?\\d{4})$"), "$1$2$3$4")
+ * </pre>
+ */
+public final class TextValueSanitizer extends InternalSanitizer implements
+ Sanitizer, Parcelable {
+ private static final String TAG = "TextValueSanitizer";
+
+ private final Pattern mRegex;
+ private final String mSubst;
+
+ /**
+ * Default constructor.
+ *
+ * @param regex regular expression with groups (delimited by {@code (} and {@code (}) that
+ * are used to substitute parts of the {@link AutofillValue#getTextValue() text value}.
+ * @param subst the string that substitutes the matched regex, using {@code $} for
+ * group substitution ({@code $1} for 1st group match, {@code $2} for 2nd, etc).
+ */
+ public TextValueSanitizer(@NonNull Pattern regex, @NonNull String subst) {
+ mRegex = Preconditions.checkNotNull(regex);
+ mSubst = Preconditions.checkNotNull(subst);
+ }
+
+ /** @hide */
+ @Override
+ @TestApi
+ public AutofillValue sanitize(@NonNull AutofillValue value) {
+ if (value == null) {
+ Slog.w(TAG, "sanitize() called with null value");
+ return null;
+ }
+ if (!value.isText()) return value;
+
+ final CharSequence text = value.getTextValue();
+
+ try {
+ final Matcher matcher = mRegex.matcher(text);
+ if (!matcher.matches()) return value;
+
+ final CharSequence sanitized = matcher.replaceAll(mSubst);
+ return AutofillValue.forText(sanitized);
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception evaluating " + mRegex + "/" + mSubst + ": " + e);
+ return value;
+ }
+ }
+
+ /////////////////////////////////////
+ // Object "contract" methods. //
+ /////////////////////////////////////
+ @Override
+ public String toString() {
+ if (!sDebug) return super.toString();
+
+ return "TextValueSanitizer: [regex=" + mRegex + ", subst=" + mSubst + "]";
+ }
+
+ /////////////////////////////////////
+ // Parcelable "contract" methods. //
+ /////////////////////////////////////
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeSerializable(mRegex);
+ parcel.writeString(mSubst);
+ }
+
+ public static final Parcelable.Creator<TextValueSanitizer> CREATOR =
+ new Parcelable.Creator<TextValueSanitizer>() {
+ @Override
+ public TextValueSanitizer createFromParcel(Parcel parcel) {
+ return new TextValueSanitizer((Pattern) parcel.readSerializable(), parcel.readString());
+ }
+
+ @Override
+ public TextValueSanitizer[] newArray(int size) {
+ return new TextValueSanitizer[size];
+ }
+ };
+}
diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java
index 813acc2..2707f14 100644
--- a/core/java/android/service/carrier/CarrierService.java
+++ b/core/java/android/service/carrier/CarrierService.java
@@ -17,10 +17,13 @@
import android.annotation.CallSuper;
import android.app.Service;
import android.content.Intent;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.util.Log;
import com.android.internal.telephony.ITelephonyRegistry;
@@ -48,6 +51,8 @@
*/
public abstract class CarrierService extends Service {
+ private static final String LOG_TAG = "CarrierService";
+
public static final String CARRIER_SERVICE_INTERFACE = "android.service.carrier.CarrierService";
private static ITelephonyRegistry sRegistry;
@@ -133,11 +138,26 @@
/**
* A wrapper around ICarrierService that forwards calls to implementations of
* {@link CarrierService}.
+ * @hide
*/
- private class ICarrierServiceWrapper extends ICarrierService.Stub {
+ public class ICarrierServiceWrapper extends ICarrierService.Stub {
+ /** @hide */
+ public static final int RESULT_OK = 0;
+ /** @hide */
+ public static final int RESULT_ERROR = 1;
+ /** @hide */
+ public static final String KEY_CONFIG_BUNDLE = "config_bundle";
+
@Override
- public PersistableBundle getCarrierConfig(CarrierIdentifier id) {
- return CarrierService.this.onLoadConfig(id);
+ public void getCarrierConfig(CarrierIdentifier id, ResultReceiver result) {
+ try {
+ Bundle data = new Bundle();
+ data.putParcelable(KEY_CONFIG_BUNDLE, CarrierService.this.onLoadConfig(id));
+ result.send(RESULT_OK, data);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Error in onLoadConfig: " + e.getMessage(), e);
+ result.send(RESULT_ERROR, null);
+ }
}
}
}
diff --git a/core/java/android/service/carrier/ICarrierService.aidl b/core/java/android/service/carrier/ICarrierService.aidl
index 4c87585..ac6f9614 100644
--- a/core/java/android/service/carrier/ICarrierService.aidl
+++ b/core/java/android/service/carrier/ICarrierService.aidl
@@ -17,6 +17,7 @@
package android.service.carrier;
import android.os.PersistableBundle;
+import android.os.ResultReceiver;
import android.service.carrier.CarrierIdentifier;
/**
@@ -28,5 +29,5 @@
interface ICarrierService {
/** @see android.service.carrier.CarrierService#onLoadConfig */
- PersistableBundle getCarrierConfig(in CarrierIdentifier id);
+ oneway void getCarrierConfig(in CarrierIdentifier id, in ResultReceiver result);
}
diff --git a/core/java/android/service/settings/suggestions/Suggestion.java b/core/java/android/service/settings/suggestions/Suggestion.java
index f27cc2e..cfeb7fc 100644
--- a/core/java/android/service/settings/suggestions/Suggestion.java
+++ b/core/java/android/service/settings/suggestions/Suggestion.java
@@ -16,12 +16,17 @@
package android.service.settings.suggestions;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.app.PendingIntent;
+import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Data object that has information about a device suggestion.
*
@@ -30,9 +35,27 @@
@SystemApi
public final class Suggestion implements Parcelable {
+ /**
+ * @hide
+ */
+ @IntDef(flag = true, value = {
+ FLAG_HAS_BUTTON,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Flags {
+ }
+
+ /**
+ * Flag for suggestion type with a single button
+ */
+ public static final int FLAG_HAS_BUTTON = 1 << 0;
+
private final String mId;
private final CharSequence mTitle;
private final CharSequence mSummary;
+ private final Icon mIcon;
+ @Flags
+ private final int mFlags;
private final PendingIntent mPendingIntent;
/**
@@ -57,6 +80,22 @@
}
/**
+ * Optional icon for this suggestion.
+ */
+ public Icon getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * Optional flags for this suggestion. This will influence UI when rendering suggestion in
+ * different style.
+ */
+ @Flags
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
* The Intent to launch when the suggestion is activated.
*/
public PendingIntent getPendingIntent() {
@@ -67,6 +106,8 @@
mId = builder.mId;
mTitle = builder.mTitle;
mSummary = builder.mSummary;
+ mIcon = builder.mIcon;
+ mFlags = builder.mFlags;
mPendingIntent = builder.mPendingIntent;
}
@@ -74,6 +115,8 @@
mId = in.readString();
mTitle = in.readCharSequence();
mSummary = in.readCharSequence();
+ mIcon = in.readParcelable(Icon.class.getClassLoader());
+ mFlags = in.readInt();
mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader());
}
@@ -99,6 +142,8 @@
dest.writeString(mId);
dest.writeCharSequence(mTitle);
dest.writeCharSequence(mSummary);
+ dest.writeParcelable(mIcon, flags);
+ dest.writeInt(mFlags);
dest.writeParcelable(mPendingIntent, flags);
}
@@ -109,6 +154,9 @@
private final String mId;
private CharSequence mTitle;
private CharSequence mSummary;
+ private Icon mIcon;
+ @Flags
+ private int mFlags;
private PendingIntent mPendingIntent;
public Builder(String id) {
@@ -135,6 +183,23 @@
}
/**
+ * Sets icon for the suggestion.
+ */
+ public Builder setIcon(Icon icon) {
+ mIcon = icon;
+ return this;
+ }
+
+ /**
+ * Sets a UI type for this suggestion. This will influence UI when rendering suggestion in
+ * different style.
+ */
+ public Builder setFlags(@Flags int flags) {
+ mFlags = flags;
+ return this;
+ }
+
+ /**
* Sets suggestion intent
*/
public Builder setPendingIntent(PendingIntent pendingIntent) {
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
index c38fab1..fef9223 100644
--- a/core/java/android/service/vr/IVrManager.aidl
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -93,5 +93,13 @@
* currently, else return the display id of the virtual display
*/
int getVr2dDisplayId();
+
+ /**
+ * Set the component name of the compositor service to bind.
+ *
+ * @param componentName flattened string representing a ComponentName of a Service in the
+ * application's compositor process to bind to, or null to clear the current binding.
+ */
+ void setAndBindCompositor(in String componentName);
}
diff --git a/core/java/android/slice/Slice.java b/core/java/android/slice/Slice.java
new file mode 100644
index 0000000..bb810e6
--- /dev/null
+++ b/core/java/android/slice/Slice.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2017 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.slice;
+
+import static android.slice.SliceItem.TYPE_ACTION;
+import static android.slice.SliceItem.TYPE_COLOR;
+import static android.slice.SliceItem.TYPE_IMAGE;
+import static android.slice.SliceItem.TYPE_REMOTE_INPUT;
+import static android.slice.SliceItem.TYPE_REMOTE_VIEW;
+import static android.slice.SliceItem.TYPE_SLICE;
+import static android.slice.SliceItem.TYPE_TEXT;
+import static android.slice.SliceItem.TYPE_TIMESTAMP;
+
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.widget.RemoteViews;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A slice is a piece of app content and actions that can be surfaced outside of the app.
+ *
+ * <p>They are constructed using {@link Builder} in a tree structure
+ * that provides the OS some information about how the content should be displayed.
+ * @hide
+ */
+public final class Slice implements Parcelable {
+
+ /**
+ * @hide
+ */
+ @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED,
+ HINT_SOURCE, HINT_MESSAGE, HINT_HORIZONTAL, HINT_NO_TINT})
+ public @interface SliceHint{ }
+
+ /**
+ * Hint that this content is a title of other content in the slice.
+ */
+ public static final String HINT_TITLE = "title";
+ /**
+ * Hint that all sub-items/sub-slices within this content should be considered
+ * to have {@link #HINT_LIST_ITEM}.
+ */
+ public static final String HINT_LIST = "list";
+ /**
+ * Hint that this item is part of a list and should be formatted as if is part
+ * of a list.
+ */
+ public static final String HINT_LIST_ITEM = "list_item";
+ /**
+ * Hint that this content is important and should be larger when displayed if
+ * possible.
+ */
+ public static final String HINT_LARGE = "large";
+ /**
+ * Hint that this slice contains a number of actions that can be grouped together
+ * in a sort of controls area of the UI.
+ */
+ public static final String HINT_ACTIONS = "actions";
+ /**
+ * Hint indicating that this item (and its sub-items) are the current selection.
+ */
+ public static final String HINT_SELECTED = "selected";
+ /**
+ * Hint to indicate that this is a message as part of a communication
+ * sequence in this slice.
+ */
+ public static final String HINT_MESSAGE = "message";
+ /**
+ * Hint to tag the source (i.e. sender) of a {@link #HINT_MESSAGE}.
+ */
+ public static final String HINT_SOURCE = "source";
+ /**
+ * Hint that list items within this slice or subslice would appear better
+ * if organized horizontally.
+ */
+ public static final String HINT_HORIZONTAL = "horizontal";
+ /**
+ * Hint to indicate that this content should not be tinted.
+ */
+ public static final String HINT_NO_TINT = "no_tint";
+
+ // These two are coming over from prototyping, but we probably don't want in
+ // public API, at least not right now.
+ /**
+ * @hide
+ */
+ public static final String HINT_ALT = "alt";
+ /**
+ * @hide
+ */
+ public static final String HINT_PARTIAL = "partial";
+
+ private final SliceItem[] mItems;
+ private final @SliceHint String[] mHints;
+ private Uri mUri;
+
+ /**
+ * @hide
+ */
+ public Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri) {
+ mHints = hints;
+ mItems = items.toArray(new SliceItem[items.size()]);
+ mUri = uri;
+ }
+
+ protected Slice(Parcel in) {
+ mHints = in.readStringArray();
+ int n = in.readInt();
+ mItems = new SliceItem[n];
+ for (int i = 0; i < n; i++) {
+ mItems[i] = SliceItem.CREATOR.createFromParcel(in);
+ }
+ mUri = Uri.CREATOR.createFromParcel(in);
+ }
+
+ /**
+ * @return The Uri that this slice represents.
+ */
+ public Uri getUri() {
+ return mUri;
+ }
+
+ /**
+ * @return All child {@link SliceItem}s that this Slice contains.
+ */
+ public SliceItem[] getItems() {
+ return mItems;
+ }
+
+ /**
+ * @return All hints associated with this Slice.
+ */
+ public @SliceHint String[] getHints() {
+ return mHints;
+ }
+
+ /**
+ * @hide
+ */
+ public SliceItem getPrimaryIcon() {
+ for (SliceItem item : getItems()) {
+ if (item.getType() == TYPE_IMAGE) {
+ return item;
+ }
+ if (!(item.getType() == TYPE_SLICE && item.hasHint(Slice.HINT_LIST))
+ && !item.hasHint(Slice.HINT_ACTIONS)
+ && !item.hasHint(Slice.HINT_LIST_ITEM)
+ && (item.getType() != TYPE_ACTION)) {
+ SliceItem icon = SliceQuery.find(item, TYPE_IMAGE);
+ if (icon != null) return icon;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStringArray(mHints);
+ dest.writeInt(mItems.length);
+ for (int i = 0; i < mItems.length; i++) {
+ mItems[i].writeToParcel(dest, flags);
+ }
+ mUri.writeToParcel(dest, 0);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * A Builder used to construct {@link Slice}s
+ */
+ public static class Builder {
+
+ private final Uri mUri;
+ private ArrayList<SliceItem> mItems = new ArrayList<>();
+ private @SliceHint ArrayList<String> mHints = new ArrayList<>();
+
+ /**
+ * Create a builder which will construct a {@link Slice} for the Given Uri.
+ * @param uri Uri to tag for this slice.
+ */
+ public Builder(@NonNull Uri uri) {
+ mUri = uri;
+ }
+
+ /**
+ * Create a builder for a {@link Slice} that is a sub-slice of the slice
+ * being constructed by the provided builder.
+ * @param parent The builder constructing the parent slice
+ */
+ public Builder(@NonNull Slice.Builder parent) {
+ mUri = parent.mUri.buildUpon().appendPath("_gen")
+ .appendPath(String.valueOf(mItems.size())).build();
+ }
+
+ /**
+ * Add hints to the Slice being constructed
+ */
+ public Builder addHints(@SliceHint String... hints) {
+ mHints.addAll(Arrays.asList(hints));
+ return this;
+ }
+
+ /**
+ * Add a sub-slice to the slice being constructed
+ */
+ public Builder addSubSlice(@NonNull Slice slice) {
+ mItems.add(new SliceItem(slice, TYPE_SLICE, slice.getHints()));
+ return this;
+ }
+
+ /**
+ * Add an action to the slice being constructed
+ */
+ public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s) {
+ mItems.add(new SliceItem(action, s, TYPE_ACTION, new String[0]));
+ return this;
+ }
+
+ /**
+ * Add text to the slice being constructed
+ */
+ public Builder addText(CharSequence text, @SliceHint String... hints) {
+ mItems.add(new SliceItem(text, TYPE_TEXT, hints));
+ return this;
+ }
+
+ /**
+ * Add an image to the slice being constructed
+ */
+ public Builder addIcon(Icon icon, @SliceHint String... hints) {
+ mItems.add(new SliceItem(icon, TYPE_IMAGE, hints));
+ return this;
+ }
+
+ /**
+ * @hide This isn't final
+ */
+ public Builder addRemoteView(RemoteViews remoteView, @SliceHint String... hints) {
+ mItems.add(new SliceItem(remoteView, TYPE_REMOTE_VIEW, hints));
+ return this;
+ }
+
+ /**
+ * Add remote input to the slice being constructed
+ */
+ public Slice.Builder addRemoteInput(RemoteInput remoteInput, @SliceHint String... hints) {
+ mItems.add(new SliceItem(remoteInput, TYPE_REMOTE_INPUT, hints));
+ return this;
+ }
+
+ /**
+ * Add a color to the slice being constructed
+ */
+ public Builder addColor(int color, @SliceHint String... hints) {
+ mItems.add(new SliceItem(color, TYPE_COLOR, hints));
+ return this;
+ }
+
+ /**
+ * Add a timestamp to the slice being constructed
+ */
+ public Slice.Builder addTimestamp(long time, @SliceHint String... hints) {
+ mItems.add(new SliceItem(time, TYPE_TIMESTAMP, hints));
+ return this;
+ }
+
+ /**
+ * Construct the slice.
+ */
+ public Slice build() {
+ return new Slice(mItems, mHints.toArray(new String[mHints.size()]), mUri);
+ }
+ }
+
+ public static final Creator<Slice> CREATOR = new Creator<Slice>() {
+ @Override
+ public Slice createFromParcel(Parcel in) {
+ return new Slice(in);
+ }
+
+ @Override
+ public Slice[] newArray(int size) {
+ return new Slice[size];
+ }
+ };
+}
diff --git a/core/java/android/slice/SliceItem.java b/core/java/android/slice/SliceItem.java
new file mode 100644
index 0000000..16f7dc6
--- /dev/null
+++ b/core/java/android/slice/SliceItem.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2017 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.slice;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.slice.Slice.SliceHint;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.widget.RemoteViews;
+
+import com.android.internal.util.ArrayUtils;
+
+
+/**
+ * A SliceItem is a single unit in the tree structure of a {@link Slice}.
+ *
+ * A SliceItem a piece of content and some hints about what that content
+ * means or how it should be displayed. The types of content can be:
+ * <li>{@link #TYPE_SLICE}</li>
+ * <li>{@link #TYPE_TEXT}</li>
+ * <li>{@link #TYPE_IMAGE}</li>
+ * <li>{@link #TYPE_ACTION}</li>
+ * <li>{@link #TYPE_COLOR}</li>
+ * <li>{@link #TYPE_TIMESTAMP}</li>
+ * <li>{@link #TYPE_REMOTE_INPUT}</li>
+ *
+ * The hints that a {@link SliceItem} are a set of strings which annotate
+ * the content. The hints that are guaranteed to be understood by the system
+ * are defined on {@link Slice}.
+ * @hide
+ */
+public final class SliceItem implements Parcelable {
+
+ /**
+ * @hide
+ */
+ @IntDef({TYPE_SLICE, TYPE_TEXT, TYPE_IMAGE, TYPE_ACTION, TYPE_COLOR,
+ TYPE_TIMESTAMP, TYPE_REMOTE_INPUT})
+ public @interface SliceType {}
+
+ /**
+ * A {@link SliceItem} that contains a {@link Slice}
+ */
+ public static final int TYPE_SLICE = 1;
+ /**
+ * A {@link SliceItem} that contains a {@link CharSequence}
+ */
+ public static final int TYPE_TEXT = 2;
+ /**
+ * A {@link SliceItem} that contains an {@link Icon}
+ */
+ public static final int TYPE_IMAGE = 3;
+ /**
+ * A {@link SliceItem} that contains a {@link PendingIntent}
+ *
+ * Note: Actions contain 2 pieces of data, In addition to the pending intent, the
+ * item contains a {@link Slice} that the action applies to.
+ */
+ public static final int TYPE_ACTION = 4;
+ /**
+ * @hide This isn't final
+ */
+ public static final int TYPE_REMOTE_VIEW = 5;
+ /**
+ * A {@link SliceItem} that contains a Color int.
+ */
+ public static final int TYPE_COLOR = 6;
+ /**
+ * A {@link SliceItem} that contains a timestamp.
+ */
+ public static final int TYPE_TIMESTAMP = 8;
+ /**
+ * A {@link SliceItem} that contains a {@link RemoteInput}.
+ */
+ public static final int TYPE_REMOTE_INPUT = 9;
+
+ /**
+ * @hide
+ */
+ protected @SliceHint String[] mHints;
+ private final int mType;
+ private final Object mObj;
+
+ /**
+ * @hide
+ */
+ public SliceItem(Object obj, @SliceType int type, @SliceHint String[] hints) {
+ mHints = hints;
+ mType = type;
+ mObj = obj;
+ }
+
+ /**
+ * @hide
+ */
+ public SliceItem(PendingIntent intent, Slice slice, int type, @SliceHint String[] hints) {
+ this(new Pair<>(intent, slice), type, hints);
+ }
+
+ /**
+ * Gets all hints associated with this SliceItem.
+ * @return Array of hints.
+ */
+ public @NonNull @SliceHint String[] getHints() {
+ return mHints;
+ }
+
+ /**
+ * @hide
+ */
+ public void addHint(@SliceHint String hint) {
+ mHints = ArrayUtils.appendElement(String.class, mHints, hint);
+ }
+
+ public @SliceType int getType() {
+ return mType;
+ }
+
+ /**
+ * @return The text held by this {@link #TYPE_TEXT} SliceItem
+ */
+ public CharSequence getText() {
+ return (CharSequence) mObj;
+ }
+
+ /**
+ * @return The icon held by this {@link #TYPE_IMAGE} SliceItem
+ */
+ public Icon getIcon() {
+ return (Icon) mObj;
+ }
+
+ /**
+ * @return The pending intent held by this {@link #TYPE_ACTION} SliceItem
+ */
+ public PendingIntent getAction() {
+ return ((Pair<PendingIntent, Slice>) mObj).first;
+ }
+
+ /**
+ * @hide This isn't final
+ */
+ public RemoteViews getRemoteView() {
+ return (RemoteViews) mObj;
+ }
+
+ /**
+ * @return The remote input held by this {@link #TYPE_REMOTE_INPUT} SliceItem
+ */
+ public RemoteInput getRemoteInput() {
+ return (RemoteInput) mObj;
+ }
+
+ /**
+ * @return The color held by this {@link #TYPE_COLOR} SliceItem
+ */
+ public int getColor() {
+ return (Integer) mObj;
+ }
+
+ /**
+ * @return The slice held by this {@link #TYPE_ACTION} or {@link #TYPE_SLICE} SliceItem
+ */
+ public Slice getSlice() {
+ if (getType() == TYPE_ACTION) {
+ return ((Pair<PendingIntent, Slice>) mObj).second;
+ }
+ return (Slice) mObj;
+ }
+
+ /**
+ * @return The timestamp held by this {@link #TYPE_TIMESTAMP} SliceItem
+ */
+ public long getTimestamp() {
+ return (Long) mObj;
+ }
+
+ /**
+ * @param hint The hint to check for
+ * @return true if this item contains the given hint
+ */
+ public boolean hasHint(@SliceHint String hint) {
+ return ArrayUtils.contains(mHints, hint);
+ }
+
+ /**
+ * @hide
+ */
+ public SliceItem(Parcel in) {
+ mHints = in.readStringArray();
+ mType = in.readInt();
+ mObj = readObj(mType, in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStringArray(mHints);
+ dest.writeInt(mType);
+ writeObj(dest, flags, mObj, mType);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasHints(@SliceHint String[] hints) {
+ if (hints == null) return true;
+ for (String hint : hints) {
+ if (!ArrayUtils.contains(mHints, hint)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasAnyHints(@SliceHint String[] hints) {
+ if (hints == null) return true;
+ for (String hint : hints) {
+ if (ArrayUtils.contains(mHints, hint)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void writeObj(Parcel dest, int flags, Object obj, int type) {
+ switch (type) {
+ case TYPE_SLICE:
+ case TYPE_REMOTE_VIEW:
+ case TYPE_IMAGE:
+ case TYPE_REMOTE_INPUT:
+ ((Parcelable) obj).writeToParcel(dest, flags);
+ break;
+ case TYPE_ACTION:
+ ((Pair<PendingIntent, Slice>) obj).first.writeToParcel(dest, flags);
+ ((Pair<PendingIntent, Slice>) obj).second.writeToParcel(dest, flags);
+ break;
+ case TYPE_TEXT:
+ TextUtils.writeToParcel((CharSequence) mObj, dest, flags);
+ break;
+ case TYPE_COLOR:
+ dest.writeInt((Integer) mObj);
+ break;
+ case TYPE_TIMESTAMP:
+ dest.writeLong((Long) mObj);
+ break;
+ }
+ }
+
+ private static Object readObj(int type, Parcel in) {
+ switch (type) {
+ case TYPE_SLICE:
+ return Slice.CREATOR.createFromParcel(in);
+ case TYPE_TEXT:
+ return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ case TYPE_IMAGE:
+ return Icon.CREATOR.createFromParcel(in);
+ case TYPE_ACTION:
+ return new Pair<PendingIntent, Slice>(
+ PendingIntent.CREATOR.createFromParcel(in),
+ Slice.CREATOR.createFromParcel(in));
+ case TYPE_REMOTE_VIEW:
+ return RemoteViews.CREATOR.createFromParcel(in);
+ case TYPE_COLOR:
+ return in.readInt();
+ case TYPE_TIMESTAMP:
+ return in.readLong();
+ case TYPE_REMOTE_INPUT:
+ return RemoteInput.CREATOR.createFromParcel(in);
+ }
+ throw new RuntimeException("Unsupported type " + type);
+ }
+
+ public static final Creator<SliceItem> CREATOR = new Creator<SliceItem>() {
+ @Override
+ public SliceItem createFromParcel(Parcel in) {
+ return new SliceItem(in);
+ }
+
+ @Override
+ public SliceItem[] newArray(int size) {
+ return new SliceItem[size];
+ }
+ };
+}
diff --git a/core/java/android/slice/SliceProvider.java b/core/java/android/slice/SliceProvider.java
new file mode 100644
index 0000000..4e21371
--- /dev/null
+++ b/core/java/android/slice/SliceProvider.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 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.slice;
+
+import android.Manifest.permission;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A SliceProvider allows app to provide content to be displayed in system
+ * spaces. This content is templated and can contain actions, and the behavior
+ * of how it is surfaced is specific to the system surface.
+ *
+ * <p>Slices are not currently live content. They are bound once and shown to the
+ * user. If the content changes due to a callback from user interaction, then
+ * {@link ContentResolver#notifyChange(Uri, ContentObserver)}
+ * should be used to notify the system.</p>
+ *
+ * <p>The provider needs to be declared in the manifest to provide the authority
+ * for the app. The authority for most slices is expected to match the package
+ * of the application.</p>
+ * <pre class="prettyprint">
+ * {@literal
+ * <provider
+ * android:name="com.android.mypkg.MySliceProvider"
+ * android:authorities="com.android.mypkg" />}
+ * </pre>
+ *
+ * @see Slice
+ * @hide
+ */
+public abstract class SliceProvider extends ContentProvider {
+
+ private static final String TAG = "SliceProvider";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_BIND_URI = "slice_uri";
+ /**
+ * @hide
+ */
+ public static final String METHOD_SLICE = "bind_slice";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_SLICE = "slice";
+
+ private static final boolean DEBUG = false;
+
+ /**
+ * Implemented to create a slice. Will be called on the main thread.
+ * @see {@link Slice}.
+ */
+ public abstract Slice onBindSlice(Uri sliceUri);
+
+ @Override
+ public final int update(Uri uri, ContentValues values, String selection,
+ String[] selectionArgs) {
+ if (DEBUG) Log.d(TAG, "update " + uri);
+ return 0;
+ }
+
+ @Override
+ public final int delete(Uri uri, String selection, String[] selectionArgs) {
+ if (DEBUG) Log.d(TAG, "delete " + uri);
+ return 0;
+ }
+
+ @Override
+ public final Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ if (DEBUG) Log.d(TAG, "query " + uri);
+ return null;
+ }
+
+ @Override
+ public final Cursor query(Uri uri, String[] projection, String selection, String[]
+ selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
+ if (DEBUG) Log.d(TAG, "query " + uri);
+ return null;
+ }
+
+ @Override
+ public final Cursor query(Uri uri, String[] projection, Bundle queryArgs,
+ CancellationSignal cancellationSignal) {
+ if (DEBUG) Log.d(TAG, "query " + uri);
+ return null;
+ }
+
+ @Override
+ public final Uri insert(Uri uri, ContentValues values) {
+ if (DEBUG) Log.d(TAG, "insert " + uri);
+ return null;
+ }
+
+ @Override
+ public final String getType(Uri uri) {
+ if (DEBUG) Log.d(TAG, "getType " + uri);
+ return null;
+ }
+
+ @Override
+ public final Bundle call(String method, String arg, Bundle extras) {
+ if (method.equals(METHOD_SLICE)) {
+ getContext().enforceCallingPermission(permission.BIND_SLICE,
+ "Slice binding requires the permission BIND_SLICE");
+ Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+
+ Slice s = handleBindSlice(uri);
+ Bundle b = new Bundle();
+ b.putParcelable(EXTRA_SLICE, s);
+ return b;
+ }
+ return super.call(method, arg, extras);
+ }
+
+ private Slice handleBindSlice(Uri sliceUri) {
+ Slice[] output = new Slice[1];
+ CountDownLatch latch = new CountDownLatch(1);
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ mainHandler.post(() -> {
+ output[0] = onBindSlice(sliceUri);
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ return output[0];
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/core/java/android/slice/SliceQuery.java b/core/java/android/slice/SliceQuery.java
new file mode 100644
index 0000000..edac0cc
--- /dev/null
+++ b/core/java/android/slice/SliceQuery.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 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.slice;
+
+import static android.slice.SliceItem.TYPE_ACTION;
+import static android.slice.SliceItem.TYPE_SLICE;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Spliterators;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+/**
+ * A bunch of utilities for searching the contents of a slice.
+ * @hide
+ */
+public class SliceQuery {
+ private static final String TAG = "SliceQuery";
+
+ /**
+ * @hide
+ */
+ public static SliceItem findNotContaining(SliceItem container, List<SliceItem> list) {
+ SliceItem ret = null;
+ while (ret == null && list.size() != 0) {
+ SliceItem remove = list.remove(0);
+ if (!contains(container, remove)) {
+ ret = remove;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * @hide
+ */
+ private static boolean contains(SliceItem container, SliceItem item) {
+ if (container == null || item == null) return false;
+ return stream(container).filter(s -> (s == item)).findAny().isPresent();
+ }
+
+ /**
+ * @hide
+ */
+ public static List<SliceItem> findAll(SliceItem s, int type, String hints, String nonHints) {
+ return findAll(s, type, new String[]{ hints }, new String[]{ nonHints });
+ }
+
+ /**
+ * @hide
+ */
+ public static List<SliceItem> findAll(SliceItem s, int type, String[] hints,
+ String[] nonHints) {
+ return stream(s).filter(item -> (type == -1 || item.getType() == type)
+ && (item.hasHints(hints) && !item.hasAnyHints(nonHints)))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * @hide
+ */
+ public static SliceItem find(Slice s, int type, String hints, String nonHints) {
+ return find(s, type, new String[]{ hints }, new String[]{ nonHints });
+ }
+
+ /**
+ * @hide
+ */
+ public static SliceItem find(SliceItem s, int type) {
+ return find(s, type, (String[]) null, null);
+ }
+
+ /**
+ * @hide
+ */
+ public static SliceItem find(SliceItem s, int type, String hints, String nonHints) {
+ return find(s, type, new String[]{ hints }, new String[]{ nonHints });
+ }
+
+ /**
+ * @hide
+ */
+ public static SliceItem find(Slice s, int type, String[] hints, String[] nonHints) {
+ return find(new SliceItem(s, TYPE_SLICE, s.getHints()), type, hints, nonHints);
+ }
+
+ /**
+ * @hide
+ */
+ public static SliceItem find(SliceItem s, int type, String[] hints, String[] nonHints) {
+ return stream(s).filter(item -> (item.getType() == type || type == -1)
+ && (item.hasHints(hints) && !item.hasAnyHints(nonHints))).findFirst().orElse(null);
+ }
+
+ /**
+ * @hide
+ */
+ public static Stream<SliceItem> stream(SliceItem slice) {
+ Queue<SliceItem> items = new LinkedList();
+ items.add(slice);
+ Iterator<SliceItem> iterator = new Iterator<SliceItem>() {
+ @Override
+ public boolean hasNext() {
+ return items.size() != 0;
+ }
+
+ @Override
+ public SliceItem next() {
+ SliceItem item = items.poll();
+ if (item.getType() == TYPE_SLICE || item.getType() == TYPE_ACTION) {
+ items.addAll(Arrays.asList(item.getSlice().getItems()));
+ }
+ return item;
+ }
+ };
+ return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
+ }
+}
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 5e40935..24260c4 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -384,7 +384,7 @@
private DynamicLayout(@NonNull Builder b) {
super(createEllipsizer(b.mEllipsize, b.mDisplay),
- b.mPaint, b.mWidth, b.mAlignment, b.mSpacingMult, b.mSpacingAdd);
+ b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd);
mDisplay = b.mDisplay;
mIncludePad = b.mIncludePad;
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index ea1100e..ad26f23 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -16,7 +16,12 @@
package android.text;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -24,9 +29,6 @@
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.MappedByteBuffer;
-import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Locale;
@@ -37,39 +39,19 @@
* @hide
*/
public class Hyphenator {
- // This class has deliberately simple lifetime management (no finalizer) because in
- // the common case a process will use a very small number of locales.
-
private static String TAG = "Hyphenator";
- // TODO: Confirm that these are the best values. Various sources suggest (1, 1), but
- // that appears too small.
- private static final int INDIC_MIN_PREFIX = 2;
- private static final int INDIC_MIN_SUFFIX = 2;
-
private final static Object sLock = new Object();
@GuardedBy("sLock")
final static HashMap<Locale, Hyphenator> sMap = new HashMap<Locale, Hyphenator>();
- // Reasonable enough values for cases where we have no hyphenation patterns but may be able to
- // do some automatic hyphenation based on characters. These values would be used very rarely.
- private static final int DEFAULT_MIN_PREFIX = 2;
- private static final int DEFAULT_MIN_SUFFIX = 2;
- final static Hyphenator sEmptyHyphenator =
- new Hyphenator(StaticLayout.nLoadHyphenator(
- null, 0, DEFAULT_MIN_PREFIX, DEFAULT_MIN_SUFFIX),
- null);
+ private final long mNativePtr;
+ private final HyphenationData mData;
- final private long mNativePtr;
-
- // We retain a reference to the buffer to keep the memory mapping valid
- @SuppressWarnings("unused")
- final private ByteBuffer mBuffer;
-
- private Hyphenator(long nativePtr, ByteBuffer b) {
+ private Hyphenator(long nativePtr, HyphenationData data) {
mNativePtr = nativePtr;
- mBuffer = b;
+ mData = data;
}
public long getNativePtr() {
@@ -90,8 +72,7 @@
new Locale(locale.getLanguage(), "", variant);
result = sMap.get(languageAndVariantOnlyLocale);
if (result != null) {
- sMap.put(locale, result);
- return result;
+ return putAlias(locale, result);
}
}
@@ -99,8 +80,7 @@
final Locale languageOnlyLocale = new Locale(locale.getLanguage());
result = sMap.get(languageOnlyLocale);
if (result != null) {
- sMap.put(locale, result);
- return result;
+ return putAlias(locale, result);
}
// Fall back to script-only, if available
@@ -112,135 +92,94 @@
.build();
result = sMap.get(scriptOnlyLocale);
if (result != null) {
- sMap.put(locale, result);
- return result;
+ return putAlias(locale, result);
}
}
- sMap.put(locale, sEmptyHyphenator); // To remember we found nothing.
+ return putEmptyAlias(locale);
}
- return sEmptyHyphenator;
}
private static class HyphenationData {
- final String mLanguageTag;
- final int mMinPrefix, mMinSuffix;
+ private static final String SYSTEM_HYPHENATOR_LOCATION = "/system/usr/hyphen-data";
+
+ public final int mMinPrefix, mMinSuffix;
+ public final long mDataAddress;
+
+ // Reasonable enough values for cases where we have no hyphenation patterns but may be able
+ // to do some automatic hyphenation based on characters. These values would be used very
+ // rarely.
+ private static final int DEFAULT_MIN_PREFIX = 2;
+ private static final int DEFAULT_MIN_SUFFIX = 2;
+
+ public static final HyphenationData sEmptyData =
+ new HyphenationData(DEFAULT_MIN_PREFIX, DEFAULT_MIN_SUFFIX);
+
+ // Create empty HyphenationData.
+ private HyphenationData(int minPrefix, int minSuffix) {
+ mMinPrefix = minPrefix;
+ mMinSuffix = minSuffix;
+ mDataAddress = 0;
+ }
+
HyphenationData(String languageTag, int minPrefix, int minSuffix) {
- this.mLanguageTag = languageTag;
- this.mMinPrefix = minPrefix;
- this.mMinSuffix = minSuffix;
- }
- }
+ mMinPrefix = minPrefix;
+ mMinSuffix = minSuffix;
- private static Hyphenator loadHyphenator(HyphenationData data) {
- String patternFilename = "hyph-" + data.mLanguageTag.toLowerCase(Locale.US) + ".hyb";
- File patternFile = new File(getSystemHyphenatorLocation(), patternFilename);
- if (!patternFile.canRead()) {
- Log.e(TAG, "hyphenation patterns for " + patternFile + " not found or unreadable");
- return null;
- }
- try {
- RandomAccessFile f = new RandomAccessFile(patternFile, "r");
- try {
- FileChannel fc = f.getChannel();
- MappedByteBuffer buf = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
- long nativePtr = StaticLayout.nLoadHyphenator(
- buf, 0, data.mMinPrefix, data.mMinSuffix);
- return new Hyphenator(nativePtr, buf);
- } finally {
- f.close();
+ final String patternFilename = "hyph-" + languageTag.toLowerCase(Locale.US) + ".hyb";
+ final File patternFile = new File(SYSTEM_HYPHENATOR_LOCATION, patternFilename);
+ if (!patternFile.canRead()) {
+ Log.e(TAG, "hyphenation patterns for " + patternFile + " not found or unreadable");
+ mDataAddress = 0;
+ } else {
+ long address;
+ try (RandomAccessFile f = new RandomAccessFile(patternFile, "r")) {
+ address = Os.mmap(0, f.length(), OsConstants.PROT_READ,
+ OsConstants.MAP_SHARED, f.getFD(), 0 /* offset */);
+ } catch (IOException | ErrnoException e) {
+ Log.e(TAG, "error loading hyphenation " + patternFile, e);
+ address = 0;
+ }
+ mDataAddress = address;
}
- } catch (IOException e) {
- Log.e(TAG, "error loading hyphenation " + patternFile, e);
- return null;
}
}
- private static File getSystemHyphenatorLocation() {
- return new File("/system/usr/hyphen-data");
+ // Do not call this method outside of init method.
+ private static Hyphenator putNewHyphenator(Locale loc, HyphenationData data) {
+ final Hyphenator hyphenator = new Hyphenator(nBuildHyphenator(
+ data.mDataAddress, loc.getLanguage(), data.mMinPrefix, data.mMinSuffix), data);
+ sMap.put(loc, hyphenator);
+ return hyphenator;
}
- // This array holds pairs of language tags that are used to prefill the map from locale to
- // hyphenation data: The hyphenation data for the first field will be prefilled from the
- // hyphenation data for the second field.
- //
- // The aliases that are computable by the get() method above are not included.
- private static final String[][] LOCALE_FALLBACK_DATA = {
- // English locales that fall back to en-US. The data is
- // from CLDR. It's all English locales, minus the locales whose
- // parent is en-001 (from supplementalData.xml, under <parentLocales>).
- // TODO: Figure out how to get this from ICU.
- {"en-AS", "en-US"}, // English (American Samoa)
- {"en-GU", "en-US"}, // English (Guam)
- {"en-MH", "en-US"}, // English (Marshall Islands)
- {"en-MP", "en-US"}, // English (Northern Mariana Islands)
- {"en-PR", "en-US"}, // English (Puerto Rico)
- {"en-UM", "en-US"}, // English (United States Minor Outlying Islands)
- {"en-VI", "en-US"}, // English (Virgin Islands)
+ // Do not call this method outside of init method.
+ private static void loadData(String langTag, int minPrefix, int maxPrefix) {
+ final HyphenationData data = new HyphenationData(langTag, minPrefix, maxPrefix);
+ putNewHyphenator(Locale.forLanguageTag(langTag), data);
+ }
- // All English locales other than those falling back to en-US are mapped to en-GB.
- {"en", "en-GB"},
+ // Caller must acquire sLock before calling this method.
+ // The Hyphenator for the baseLangTag must exists.
+ private static Hyphenator addAliasByTag(String langTag, String baseLangTag) {
+ return putAlias(Locale.forLanguageTag(langTag),
+ sMap.get(Locale.forLanguageTag(baseLangTag)));
+ }
- // For German, we're assuming the 1996 (and later) orthography by default.
- {"de", "de-1996"},
- // Liechtenstein uses the Swiss hyphenation rules for the 1901 orthography.
- {"de-LI-1901", "de-CH-1901"},
+ // Caller must acquire sLock before calling this method.
+ private static Hyphenator putAlias(Locale locale, Hyphenator base) {
+ return putNewHyphenator(locale, base.mData);
+ }
- // Norwegian is very probably Norwegian Bokmål.
- {"no", "nb"},
+ // Caller must acquire sLock before calling this method.
+ private static Hyphenator putEmptyAlias(Locale locale) {
+ return putNewHyphenator(locale, HyphenationData.sEmptyData);
+ }
- // Use mn-Cyrl. According to CLDR's likelySubtags.xml, mn is most likely to be mn-Cyrl.
- {"mn", "mn-Cyrl"}, // Mongolian
-
- // Fall back to Ethiopic script for languages likely to be written in Ethiopic.
- // Data is from CLDR's likelySubtags.xml.
- // TODO: Convert this to a mechanism using ICU4J's ULocale#addLikelySubtags().
- {"am", "und-Ethi"}, // Amharic
- {"byn", "und-Ethi"}, // Blin
- {"gez", "und-Ethi"}, // Geʻez
- {"ti", "und-Ethi"}, // Tigrinya
- {"wal", "und-Ethi"}, // Wolaytta
- };
-
- private static final HyphenationData[] AVAILABLE_LANGUAGES = {
- new HyphenationData("as", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Assamese
- new HyphenationData("bg", 2, 2), // Bulgarian
- new HyphenationData("bn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Bengali
- new HyphenationData("cu", 1, 2), // Church Slavonic
- new HyphenationData("cy", 2, 3), // Welsh
- new HyphenationData("da", 2, 2), // Danish
- new HyphenationData("de-1901", 2, 2), // German 1901 orthography
- new HyphenationData("de-1996", 2, 2), // German 1996 orthography
- new HyphenationData("de-CH-1901", 2, 2), // Swiss High German 1901 orthography
- new HyphenationData("en-GB", 2, 3), // British English
- new HyphenationData("en-US", 2, 3), // American English
- new HyphenationData("es", 2, 2), // Spanish
- new HyphenationData("et", 2, 3), // Estonian
- new HyphenationData("eu", 2, 2), // Basque
- new HyphenationData("fr", 2, 3), // French
- new HyphenationData("ga", 2, 3), // Irish
- new HyphenationData("gu", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Gujarati
- new HyphenationData("hi", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Hindi
- new HyphenationData("hr", 2, 2), // Croatian
- new HyphenationData("hu", 2, 2), // Hungarian
- // texhyphen sources say Armenian may be (1, 2), but that it needs confirmation.
- // Going with a more conservative value of (2, 2) for now.
- new HyphenationData("hy", 2, 2), // Armenian
- new HyphenationData("kn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Kannada
- new HyphenationData("ml", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Malayalam
- new HyphenationData("mn-Cyrl", 2, 2), // Mongolian in Cyrillic script
- new HyphenationData("mr", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Marathi
- new HyphenationData("nb", 2, 2), // Norwegian Bokmål
- new HyphenationData("nn", 2, 2), // Norwegian Nynorsk
- new HyphenationData("or", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Oriya
- new HyphenationData("pa", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Punjabi
- new HyphenationData("pt", 2, 3), // Portuguese
- new HyphenationData("sl", 2, 2), // Slovenian
- new HyphenationData("ta", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Tamil
- new HyphenationData("te", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Telugu
- new HyphenationData("tk", 2, 2), // Turkmen
- new HyphenationData("und-Ethi", 1, 1), // Any language in Ethiopic script
- };
+ // TODO: Confirm that these are the best values. Various sources suggest (1, 1), but
+ // that appears too small.
+ private static final int INDIC_MIN_PREFIX = 2;
+ private static final int INDIC_MIN_SUFFIX = 2;
/**
* Load hyphenation patterns at initialization time. We want to have patterns
@@ -250,20 +189,85 @@
* @hide
*/
public static void init() {
- sMap.put(null, null);
+ synchronized (sLock) {
+ sMap.put(null, null);
- for (int i = 0; i < AVAILABLE_LANGUAGES.length; i++) {
- HyphenationData data = AVAILABLE_LANGUAGES[i];
- Hyphenator h = loadHyphenator(data);
- if (h != null) {
- sMap.put(Locale.forLanguageTag(data.mLanguageTag), h);
- }
- }
+ loadData("as", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Assamese
+ loadData("bg", 2, 2); // Bulgarian
+ loadData("bn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Bengali
+ loadData("cu", 1, 2); // Church Slavonic
+ loadData("cy", 2, 3); // Welsh
+ loadData("da", 2, 2); // Danish
+ loadData("de-1901", 2, 2); // German 1901 orthography
+ loadData("de-1996", 2, 2); // German 1996 orthography
+ loadData("de-CH-1901", 2, 2); // Swiss High German 1901 orthography
+ loadData("en-GB", 2, 3); // British English
+ loadData("en-US", 2, 3); // American English
+ loadData("es", 2, 2); // Spanish
+ loadData("et", 2, 3); // Estonian
+ loadData("eu", 2, 2); // Basque
+ loadData("fr", 2, 3); // French
+ loadData("ga", 2, 3); // Irish
+ loadData("gu", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Gujarati
+ loadData("hi", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Hindi
+ loadData("hr", 2, 2); // Croatian
+ loadData("hu", 2, 2); // Hungarian
+ // texhyphen sources say Armenian may be (1, 2); but that it needs confirmation.
+ // Going with a more conservative value of (2, 2) for now.
+ loadData("hy", 2, 2); // Armenian
+ loadData("kn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Kannada
+ loadData("ml", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Malayalam
+ loadData("mn-Cyrl", 2, 2); // Mongolian in Cyrillic script
+ loadData("mr", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Marathi
+ loadData("nb", 2, 2); // Norwegian Bokmål
+ loadData("nn", 2, 2); // Norwegian Nynorsk
+ loadData("or", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Oriya
+ loadData("pa", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Punjabi
+ loadData("pt", 2, 3); // Portuguese
+ loadData("sl", 2, 2); // Slovenian
+ loadData("ta", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Tamil
+ loadData("te", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Telugu
+ loadData("tk", 2, 2); // Turkmen
+ loadData("und-Ethi", 1, 1); // Any language in Ethiopic script
- for (int i = 0; i < LOCALE_FALLBACK_DATA.length; i++) {
- String language = LOCALE_FALLBACK_DATA[i][0];
- String fallback = LOCALE_FALLBACK_DATA[i][1];
- sMap.put(Locale.forLanguageTag(language), sMap.get(Locale.forLanguageTag(fallback)));
+ // English locales that fall back to en-US. The data is
+ // from CLDR. It's all English locales, minus the locales whose
+ // parent is en-001 (from supplementalData.xml, under <parentLocales>).
+ // TODO: Figure out how to get this from ICU.
+ addAliasByTag("en-AS", "en-US"); // English (American Samoa)
+ addAliasByTag("en-GU", "en-US"); // English (Guam)
+ addAliasByTag("en-MH", "en-US"); // English (Marshall Islands)
+ addAliasByTag("en-MP", "en-US"); // English (Northern Mariana Islands)
+ addAliasByTag("en-PR", "en-US"); // English (Puerto Rico)
+ addAliasByTag("en-UM", "en-US"); // English (United States Minor Outlying Islands)
+ addAliasByTag("en-VI", "en-US"); // English (Virgin Islands)
+
+ // All English locales other than those falling back to en-US are mapped to en-GB.
+ addAliasByTag("en", "en-GB");
+
+ // For German, we're assuming the 1996 (and later) orthography by default.
+ addAliasByTag("de", "de-1996");
+ // Liechtenstein uses the Swiss hyphenation rules for the 1901 orthography.
+ addAliasByTag("de-LI-1901", "de-CH-1901");
+
+ // Norwegian is very probably Norwegian Bokmål.
+ addAliasByTag("no", "nb");
+
+ // Use mn-Cyrl. According to CLDR's likelySubtags.xml, mn is most likely to be mn-Cyrl.
+ addAliasByTag("mn", "mn-Cyrl"); // Mongolian
+
+ // Fall back to Ethiopic script for languages likely to be written in Ethiopic.
+ // Data is from CLDR's likelySubtags.xml.
+ // TODO: Convert this to a mechanism using ICU4J's ULocale#addLikelySubtags().
+ addAliasByTag("am", "und-Ethi"); // Amharic
+ addAliasByTag("byn", "und-Ethi"); // Blin
+ addAliasByTag("gez", "und-Ethi"); // Geʻez
+ addAliasByTag("ti", "und-Ethi"); // Tigrinya
+ addAliasByTag("wal", "und-Ethi"); // Wolaytta
}
- }
+ };
+
+ private static native long nBuildHyphenator(/* non-zero */ long dataAddress,
+ @NonNull String langTag, @IntRange(from = 1) int minPrefix,
+ @IntRange(from = 1) int minSuffix);
}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 25f791b..60fff73 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1915,8 +1915,7 @@
return margin;
}
- /* package */
- static float measurePara(TextPaint paint, CharSequence text, int start, int end,
+ private static float measurePara(TextPaint paint, CharSequence text, int start, int end,
TextDirectionHeuristic textDir) {
MeasuredText mt = MeasuredText.obtain();
TextLine tl = TextLine.obtain();
@@ -2146,18 +2145,14 @@
* text within the layout of a line.
*/
public static class Directions {
- // Directions represents directional runs within a line of text.
- // Runs are pairs of ints listed in visual order, starting from the
- // leading margin. The first int of each pair is the offset from
- // the first character of the line to the start of the run. The
- // second int represents both the length and level of the run.
- // The length is in the lower bits, accessed by masking with
- // DIR_LENGTH_MASK. The level is in the higher bits, accessed
- // by shifting by DIR_LEVEL_SHIFT and masking by DIR_LEVEL_MASK.
- // To simply test for an RTL direction, test the bit using
- // DIR_RTL_FLAG, if set then the direction is rtl.
-
/**
+ * Directions represents directional runs within a line of text. Runs are pairs of ints
+ * listed in visual order, starting from the leading margin. The first int of each pair is
+ * the offset from the first character of the line to the start of the run. The second int
+ * represents both the length and level of the run. The length is in the lower bits,
+ * accessed by masking with RUN_LENGTH_MASK. The level is in the higher bits, accessed by
+ * shifting by RUN_LEVEL_SHIFT and masking by RUN_LEVEL_MASK. To simply test for an RTL
+ * direction, test the bit using RUN_RTL_FLAG, if set then the direction is rtl.
* @hide
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index ce3e282..b09ccc2 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -90,10 +90,6 @@
}
}
- void setPos(int pos) {
- mPos = pos - mTextStart;
- }
-
/**
* Analyzes text for bidirectional runs. Allocates working buffers.
*/
@@ -160,47 +156,43 @@
}
}
+ /**
+ * Apply the style.
+ *
+ * If StaticLyaout.Builder is not provided in setPara() method, this method measures the styled
+ * text width.
+ * If StaticLayout.Builder is provided in setPara() method, this method just passes the style
+ * information to native code by calling StaticLayout.Builder.addstyleRun() and returns 0.
+ */
float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) {
if (fm != null) {
paint.getFontMetricsInt(fm);
}
- int p = mPos;
+ final int p = mPos;
mPos = p + len;
- // try to do widths measurement in native code, but use Java if paint has been subclassed
- // FIXME: may want to eliminate special case for subclass
- float[] widths = null;
- if (mBuilder == null || paint.getClass() != TextPaint.class) {
- widths = mWidths;
- }
if (mEasy) {
- boolean isRtl = mDir != Layout.DIR_LEFT_TO_RIGHT;
- float width = 0;
- if (widths != null) {
- width = paint.getTextRunAdvances(mChars, p, len, p, len, isRtl, widths, p);
- if (mBuilder != null) {
- mBuilder.addMeasuredRun(p, p + len, widths);
- }
+ final boolean isRtl = mDir != Layout.DIR_LEFT_TO_RIGHT;
+ if (mBuilder == null) {
+ return paint.getTextRunAdvances(mChars, p, len, p, len, isRtl, mWidths, p);
} else {
- width = mBuilder.addStyleRun(paint, p, p + len, isRtl);
+ mBuilder.addStyleRun(paint, p, p + len, isRtl);
+ return 0.0f; // Builder.addStyleRun doesn't return the width.
}
- return width;
}
float totalAdvance = 0;
int level = mLevels[p];
for (int q = p, i = p + 1, e = p + len;; ++i) {
if (i == e || mLevels[i] != level) {
- boolean isRtl = (level & 0x1) != 0;
- if (widths != null) {
+ final boolean isRtl = (level & 0x1) != 0;
+ if (mBuilder == null) {
totalAdvance +=
- paint.getTextRunAdvances(mChars, q, i - q, q, i - q, isRtl, widths, q);
- if (mBuilder != null) {
- mBuilder.addMeasuredRun(q, i, widths);
- }
+ paint.getTextRunAdvances(mChars, q, i - q, q, i - q, isRtl, mWidths, q);
} else {
- totalAdvance += mBuilder.addStyleRun(paint, q, i, isRtl);
+ // Builder.addStyleRun doesn't return the width.
+ mBuilder.addStyleRun(paint, q, i, isRtl);
}
if (i == e) {
break;
@@ -209,7 +201,7 @@
level = mLevels[i];
}
}
- return totalAdvance;
+ return totalAdvance; // If mBuilder is null, the result is zero.
}
float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len,
@@ -243,7 +235,7 @@
for (int i = mPos + 1, e = mPos + len; i < e; i++)
w[i] = 0;
} else {
- mBuilder.addReplacementRun(mPos, mPos + len, wid);
+ mBuilder.addReplacementRun(paint, mPos, mPos + len, wid);
}
mPos += len;
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index c124c7f..961cd8ee 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -28,12 +28,12 @@
import android.text.style.MetricAffectingSpan;
import android.text.style.TabStopSpan;
import android.util.Log;
+import android.util.Pair;
import android.util.Pools.SynchronizedPool;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
-import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Locale;
@@ -101,6 +101,7 @@
b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
b.mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;
+ b.mLocales = null;
b.mMeasuredText = MeasuredText.obtain();
return b;
@@ -117,6 +118,9 @@
b.mMeasuredText = null;
b.mLeftIndents = null;
b.mRightIndents = null;
+ b.mLocales = null;
+ b.mLeftPaddings = null;
+ b.mRightPaddings = null;
nFinishBuilder(b.mNativePtr);
sPool.release(b);
}
@@ -128,6 +132,8 @@
mPaint = null;
mLeftIndents = null;
mRightIndents = null;
+ mLeftPaddings = null;
+ mRightPaddings = null;
mMeasuredText.finish();
}
@@ -356,6 +362,28 @@
}
/**
+ * Set available paddings to draw overhanging text on. Arguments are arrays holding the
+ * amount of padding available, one per line, measured in pixels. For lines past the last
+ * element in the array, the last element repeats.
+ *
+ * The individual padding amounts should be non-negative. The result of passing negative
+ * paddings is undefined.
+ *
+ * @param leftPaddings array of amounts of available padding for left margin, in pixels
+ * @param rightPaddings array of amounts of available padding for right margin, in pixels
+ * @return this builder, useful for chaining
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setAvailablePaddings(@Nullable int[] leftPaddings,
+ @Nullable int[] rightPaddings) {
+ mLeftPaddings = leftPaddings;
+ mRightPaddings = rightPaddings;
+ return this;
+ }
+
+ /**
* Set paragraph justification mode. The default value is
* {@link Layout#JUSTIFICATION_MODE_NONE}. If the last line is too short for justification,
* the last line will be displayed with the alignment set by {@link #setAlignment}.
@@ -401,10 +429,8 @@
* future).
*
* Then, for each run within the paragraph:
- * - setLocales (this must be done at least for the first run, optional afterwards)
* - one of the following, depending on the type of run:
* + addStyleRun (a text run, to be measured in native code)
- * + addMeasuredRun (a run already measured in Java, passed into native code)
* + addReplacementRun (a replacement run, width is given)
*
* After measurement, nGetWidths() is valid if the widths are needed (eg for ellipsis).
@@ -413,24 +439,29 @@
* After all paragraphs, call finish() to release expensive buffers.
*/
- private void setLocales(LocaleList locales) {
+ private Pair<String, long[]> getLocaleAndHyphenatorIfChanged(TextPaint paint) {
+ final LocaleList locales = paint.getTextLocales();
+ final String languageTags;
+ long[] hyphenators;
if (!locales.equals(mLocales)) {
- nSetLocales(mNativePtr, locales.toLanguageTags(), getHyphenators(locales));
mLocales = locales;
+ return new Pair(locales.toLanguageTags(), getHyphenators(locales));
+ } else {
+ // passing null means keep current locale.
+ // TODO: move locale change detection to native.
+ return new Pair(null, null);
}
}
- /* package */ float addStyleRun(TextPaint paint, int start, int end, boolean isRtl) {
- setLocales(paint.getTextLocales());
- return nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl);
+ /* package */ void addStyleRun(TextPaint paint, int start, int end, boolean isRtl) {
+ Pair<String, long[]> locHyph = getLocaleAndHyphenatorIfChanged(paint);
+ nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl, locHyph.first,
+ locHyph.second);
}
- /* package */ void addMeasuredRun(int start, int end, float[] widths) {
- nAddMeasuredRun(mNativePtr, start, end, widths);
- }
-
- /* package */ void addReplacementRun(int start, int end, float width) {
- nAddReplacementRun(mNativePtr, start, end, width);
+ /* package */ void addReplacementRun(TextPaint paint, int start, int end, float width) {
+ Pair<String, long[]> locHyph = getLocaleAndHyphenatorIfChanged(paint);
+ nAddReplacementRun(mNativePtr, start, end, width, locHyph.first, locHyph.second);
}
/**
@@ -478,6 +509,8 @@
private int mHyphenationFrequency;
@Nullable private int[] mLeftIndents;
@Nullable private int[] mRightIndents;
+ @Nullable private int[] mLeftPaddings;
+ @Nullable private int[] mRightPaddings;
private int mJustificationMode;
private boolean mAddLastLineLineSpacing;
@@ -616,7 +649,7 @@
: (b.mText instanceof Spanned)
? new SpannedEllipsizer(b.mText)
: new Ellipsizer(b.mText),
- b.mPaint, b.mWidth, b.mAlignment, b.mSpacingMult, b.mSpacingAdd);
+ b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd);
if (b.mEllipsize != null) {
Ellipsizer e = (Ellipsizer) getText();
@@ -638,6 +671,8 @@
mLeftIndents = b.mLeftIndents;
mRightIndents = b.mRightIndents;
+ mLeftPaddings = b.mLeftPaddings;
+ mRightPaddings = b.mRightPaddings;
setJustificationMode(b.mJustificationMode);
generate(b, b.mIncludePad, b.mIncludePad);
@@ -662,7 +697,6 @@
// store fontMetrics per span range
// must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
int[] fmCache = new int[4 * 4];
- b.setLocales(paint.getTextLocales());
mLineCount = 0;
mEllipsized = false;
@@ -776,11 +810,17 @@
}
}
+ // TODO: Move locale tracking code to native.
+ b.mLocales = null; // Reset the locale tracking.
+
nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart,
firstWidth, firstWidthLineCount, restWidth,
variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency,
// TODO: Support more justification mode, e.g. letter spacing, stretching.
- b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE, indents, mLineCount);
+ b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
+ // TODO: indents and paddings don't need to get passed to native code for every
+ // paragraph. Pass them to native code just once.
+ indents, mLeftPaddings, mRightPaddings, mLineCount);
// measurement has to be done before performing line breaking
// but we don't want to recompute fontmetrics or span ranges the
@@ -1491,28 +1531,25 @@
private static native void nFreeBuilder(long nativePtr);
private static native void nFinishBuilder(long nativePtr);
- /* package */ static native long nLoadHyphenator(ByteBuffer buf, int offset,
- int minPrefix, int minSuffix);
-
- private static native void nSetLocales(long nativePtr, String locales,
- long[] nativeHyphenators);
-
// Set up paragraph text and settings; done as one big method to minimize jni crossings
private static native void nSetupParagraph(
- @NonNull long nativePtr, @NonNull char[] text, @IntRange(from = 0) int length,
+ /* non zero */ long nativePtr, @NonNull char[] text, @IntRange(from = 0) int length,
@FloatRange(from = 0.0f) float firstWidth, @IntRange(from = 0) int firstWidthLineCount,
@FloatRange(from = 0.0f) float restWidth, @Nullable int[] variableTabStops,
int defaultTabStop, @BreakStrategy int breakStrategy,
@HyphenationFrequency int hyphenationFrequency, boolean isJustified,
- @Nullable int[] indents, @IntRange(from = 0) int indentsOffset);
+ @Nullable int[] indents, @Nullable int[] leftPaddings, @Nullable int[] rightPaddings,
+ @IntRange(from = 0) int indentsOffset);
- private static native float nAddStyleRun(long nativePtr, long nativePaint, int start, int end,
- boolean isRtl);
+ private static native void nAddStyleRun(
+ /* non-zero */ long nativePtr, /* non-zero */ long nativePaint,
+ @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean isRtl,
+ @Nullable String languageTags, @Nullable long[] hyphenators);
- private static native void nAddMeasuredRun(long nativePtr,
- int start, int end, float[] widths);
-
- private static native void nAddReplacementRun(long nativePtr, int start, int end, float width);
+ private static native void nAddReplacementRun(/* non-zero */ long nativePtr,
+ @IntRange(from = 0) int start, @IntRange(from = 0) int end,
+ @FloatRange(from = 0.0f) float width, @Nullable String languageTags,
+ @Nullable long[] hyphenators);
private static native void nGetWidths(long nativePtr, float[] widths);
@@ -1590,4 +1627,6 @@
@Nullable private int[] mLeftIndents;
@Nullable private int[] mRightIndents;
+ @Nullable private int[] mLeftPaddings;
+ @Nullable private int[] mRightPaddings;
}
diff --git a/core/java/android/transition/TransitionUtils.java b/core/java/android/transition/TransitionUtils.java
index 4951237..084b79d 100644
--- a/core/java/android/transition/TransitionUtils.java
+++ b/core/java/android/transition/TransitionUtils.java
@@ -101,7 +101,7 @@
ImageView copy = new ImageView(view.getContext());
copy.setScaleType(ImageView.ScaleType.CENTER_CROP);
- Bitmap bitmap = createViewBitmap(view, matrix, bounds);
+ Bitmap bitmap = createViewBitmap(view, matrix, bounds, sceneRoot);
if (bitmap != null) {
copy.setImageBitmap(bitmap);
}
@@ -115,7 +115,7 @@
/**
* Get a copy of bitmap of given drawable, return null if intrinsic size is zero
*/
- public static Bitmap createDrawableBitmap(Drawable drawable) {
+ public static Bitmap createDrawableBitmap(Drawable drawable, View hostView) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
if (width <= 0 || height <= 0) {
@@ -128,7 +128,7 @@
}
int bitmapWidth = (int) (width * scale);
int bitmapHeight = (int) (height * scale);
- final RenderNode node = RenderNode.create("TransitionUtils", null);
+ final RenderNode node = RenderNode.create("TransitionUtils", hostView);
node.setLeftTopRightBottom(0, 0, width, height);
node.setClipToBounds(false);
final DisplayListCanvas canvas = node.start(width, height);
@@ -156,20 +156,30 @@
* returning.
* @param bounds The bounds of the bitmap in the destination coordinate system (where the
* view should be presented. Typically, this is matrix.mapRect(viewBounds);
+ * @param sceneRoot A ViewGroup that is attached to the window to temporarily contain the view
+ * if it isn't attached to the window.
* @return A bitmap of the given view or null if bounds has no width or height.
*/
- public static Bitmap createViewBitmap(View view, Matrix matrix, RectF bounds) {
+ public static Bitmap createViewBitmap(View view, Matrix matrix, RectF bounds,
+ ViewGroup sceneRoot) {
+ final boolean addToOverlay = !view.isAttachedToWindow();
+ if (addToOverlay) {
+ if (sceneRoot == null || !sceneRoot.isAttachedToWindow()) {
+ return null;
+ }
+ sceneRoot.getOverlay().add(view);
+ }
Bitmap bitmap = null;
int bitmapWidth = Math.round(bounds.width());
int bitmapHeight = Math.round(bounds.height());
if (bitmapWidth > 0 && bitmapHeight > 0) {
- float scale = Math.min(1f, ((float)MAX_IMAGE_SIZE) / (bitmapWidth * bitmapHeight));
+ float scale = Math.min(1f, ((float) MAX_IMAGE_SIZE) / (bitmapWidth * bitmapHeight));
bitmapWidth *= scale;
bitmapHeight *= scale;
matrix.postTranslate(-bounds.left, -bounds.top);
matrix.postScale(scale, scale);
- final RenderNode node = RenderNode.create("TransitionUtils", null);
+ final RenderNode node = RenderNode.create("TransitionUtils", view);
node.setLeftTopRightBottom(0, 0, bitmapWidth, bitmapHeight);
node.setClipToBounds(false);
final DisplayListCanvas canvas = node.start(bitmapWidth, bitmapHeight);
@@ -178,6 +188,9 @@
node.end(canvas);
bitmap = ThreadedRenderer.createHardwareBitmap(node, bitmapWidth, bitmapHeight);
}
+ if (addToOverlay) {
+ sceneRoot.getOverlay().remove(view);
+ }
return bitmap;
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 5838f95..fc1d487 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -50,6 +50,13 @@
}
/**
+ * Override feature flag to new state.
+ */
+ public static void setEnabled(String feature, boolean enabled) {
+ SystemProperties.set(FFLAG_OVERRIDE_PREFIX + feature, enabled ? "true" : "false");
+ }
+
+ /**
* Returns all feature flags in their raw form.
*/
public static Map<String, String> getAllFeatureFlags() {
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 8691136..0299865 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -392,7 +392,7 @@
// and the length of the tag.
// Note: we implicitly accept possible truncation for Modified-UTF8 differences. It
// is too expensive to compute that ahead of time.
- int bufferSize = NoPreloadHolder.LOGGER_ENTRY_MAX_PAYLOAD // Base.
+ int bufferSize = PreloadHolder.LOGGER_ENTRY_MAX_PAYLOAD // Base.
- 2 // Two terminators.
- (tag != null ? tag.length() : 0) // Tag length.
- 32; // Some slack.
@@ -429,10 +429,10 @@
}
/**
- * NoPreloadHelper class. Caches the LOGGER_ENTRY_MAX_PAYLOAD value to avoid
+ * PreloadHelper class. Caches the LOGGER_ENTRY_MAX_PAYLOAD value to avoid
* a JNI call during logging.
*/
- static class NoPreloadHolder {
+ static class PreloadHolder {
public final static int LOGGER_ENTRY_MAX_PAYLOAD =
logger_entry_max_payload_native();
}
diff --git a/core/java/android/util/StatsLogKey.java b/core/java/android/util/StatsLogKey.java
new file mode 100644
index 0000000..9ad0a23
--- /dev/null
+++ b/core/java/android/util/StatsLogKey.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// THIS FILE IS AUTO-GENERATED.
+// DO NOT MODIFY.
+
+package android.util;
+
+/** @hide */
+public class StatsLogKey {
+ private StatsLogKey() {}
+
+ /** Constants for android.os.statsd.ScreenStateChange. */
+
+ /** display_state */
+ public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE = 1;
+
+ /** Constants for android.os.statsd.ProcessStateChange. */
+
+ /** state */
+ public static final int PROCESS_STATE_CHANGE__STATE = 1;
+
+ /** uid */
+ public static final int PROCESS_STATE_CHANGE__UID = 2;
+
+ /** package_name */
+ public static final int PROCESS_STATE_CHANGE__PACKAGE_NAME = 1002;
+
+ /** package_version */
+ public static final int PROCESS_STATE_CHANGE__PACKAGE_VERSION = 3;
+
+ /** package_version_string */
+ public static final int PROCESS_STATE_CHANGE__PACKAGE_VERSION_STRING = 4;
+
+}
diff --git a/core/java/android/util/StatsLogTag.java b/core/java/android/util/StatsLogTag.java
new file mode 100644
index 0000000..5e5a828
--- /dev/null
+++ b/core/java/android/util/StatsLogTag.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// THIS FILE IS AUTO-GENERATED.
+// DO NOT MODIFY.
+
+package android.util;
+
+/** @hide */
+public class StatsLogTag {
+ private StatsLogTag() {}
+
+ /** android.os.statsd.ScreenStateChange. */
+ public static final int SCREEN_STATE_CHANGE = 2;
+
+ /** android.os.statsd.ProcessStateChange. */
+ public static final int PROCESS_STATE_CHANGE = 1112;
+
+}
diff --git a/core/java/android/util/StatsLogValue.java b/core/java/android/util/StatsLogValue.java
new file mode 100644
index 0000000..05b9d93
--- /dev/null
+++ b/core/java/android/util/StatsLogValue.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// THIS FILE IS AUTO-GENERATED.
+// DO NOT MODIFY.
+
+package android.util;
+
+/** @hide */
+public class StatsLogValue {
+ private StatsLogValue() {}
+
+ /** Constants for android.os.statsd.ScreenStateChange. */
+
+ /** display_state: STATE_UNKNOWN */
+ public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_UNKNOWN = 0;
+
+ /** display_state: STATE_OFF */
+ public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF = 1;
+
+ /** display_state: STATE_ON */
+ public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON = 2;
+
+ /** display_state: STATE_DOZE */
+ public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_DOZE = 3;
+
+ /** display_state: STATE_DOZE_SUSPEND */
+ public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_DOZE_SUSPEND = 4;
+
+ /** display_state: STATE_VR */
+ public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_VR = 5;
+
+ /** Constants for android.os.statsd.ProcessStateChange. */
+
+ /** state: START */
+ public static final int PROCESS_STATE_CHANGE__STATE__START = 1;
+
+ /** state: CRASH */
+ public static final int PROCESS_STATE_CHANGE__STATE__CRASH = 2;
+
+}
diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java
index 9afa56d..43a9789 100644
--- a/core/java/android/util/proto/ProtoOutputStream.java
+++ b/core/java/android/util/proto/ProtoOutputStream.java
@@ -29,8 +29,8 @@
* Class to write to a protobuf stream.
*
* Each write method takes an ID code from the protoc generated classes
- * and the value to write. To make a nested object, call startObject
- * and then endObject when you are done.
+ * and the value to write. To make a nested object, call #start
+ * and then #end when you are done.
*
* The ID codes have type information embedded into them, so if you call
* the incorrect function you will get an IllegalArgumentException.
@@ -60,16 +60,16 @@
* Message objects. We need to find another way.
*
* So what we do here is to let the calling code write the data into a
- * byte[] (actually a collection of them wrapped in the EncodedBuffer) class,
+ * byte[] (actually a collection of them wrapped in the EncodedBuffer class),
* but not do the varint encoding of the sub-message sizes. Then, we do a
* recursive traversal of the buffer itself, calculating the sizes (which are
* then knowable, although still not the actual sizes in the buffer because of
* possible further nesting). Then we do a third pass, compacting the
* buffer and varint encoding the sizes.
*
- * This gets us a relatively small number number of fixed-size allocations,
+ * This gets us a relatively small number of fixed-size allocations,
* which is less likely to cause memory fragmentation or churn the GC, and
- * the same number of data copies as would have gotten with setting it
+ * the same number of data copies as we would have gotten with setting it
* field-by-field in generated code, and no code bloat from generated code.
* The final data copy is also done with System.arraycopy, which will be
* more efficient, in general, than doing the individual fields twice (as in
@@ -77,26 +77,26 @@
*
* To accomplish the multiple passes, whenever we write a
* WIRE_TYPE_LENGTH_DELIMITED field, we write the size occupied in our
- * buffer as a fixed 32 bit int (called childRawSize), not variable length
+ * buffer as a fixed 32 bit int (called childRawSize), not a variable length
* one. We reserve another 32 bit slot for the computed size (called
* childEncodedSize). If we know the size up front, as we do for strings
* and byte[], then we also put that into childEncodedSize, if we don't, we
- * write the negative of childRawSize, as a sentiel that we need to
+ * write the negative of childRawSize, as a sentinel that we need to
* compute it during the second pass and recursively compact it during the
* third pass.
*
- * Unsgigned size varints can be up to five bytes long, but we reserve eight
+ * Unsigned size varints can be up to five bytes long, but we reserve eight
* bytes for overhead, so we know that when we compact the buffer, there
* will always be space for the encoded varint.
*
* When we can figure out the size ahead of time, we do, in order
* to save overhead with recalculating it, and with the later arraycopy.
*
- * During the period between when the caller has called startObject, but
- * not yet called endObject, we maintain a linked list of the tokens
- * returned by startObject, stored in those 8 bytes of size storage space.
+ * During the period between when the caller has called #start, but
+ * not yet called #end, we maintain a linked list of the tokens
+ * returned by #start, stored in those 8 bytes of size storage space.
* We use that linked list of tokens to ensure that the caller has
- * correctly matched pairs of startObject and endObject calls, and issue
+ * correctly matched pairs of #start and #end calls, and issue
* errors if they are not matched.
*/
@TestApi
@@ -2375,6 +2375,9 @@
if (countString == null) {
countString = "fieldCount=" + fieldCount;
}
+ if (countString.length() > 0) {
+ countString += " ";
+ }
final long fieldType = fieldId & FIELD_TYPE_MASK;
String typeString = getFieldTypeString(fieldType);
@@ -2382,7 +2385,7 @@
typeString = "fieldType=" + fieldType;
}
- return fieldCount + " " + typeString + " tag=" + ((int)fieldId)
+ return countString + typeString + " tag=" + ((int) fieldId)
+ " fieldId=0x" + Long.toHexString(fieldId);
}
diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java
new file mode 100644
index 0000000..449baca
--- /dev/null
+++ b/core/java/android/util/proto/ProtoUtils.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 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.util.proto;
+
+import android.util.AggStats;
+
+/**
+ * This class contains a list of helper functions to write common proto in
+ * //frameworks/base/core/proto/android/base directory
+ */
+public class ProtoUtils {
+
+ /**
+ * Dump AggStats to ProtoOutputStream
+ * @hide
+ */
+ public static void toAggStatsProto(ProtoOutputStream proto, long fieldId,
+ long min, long average, long max) {
+ final long aggStatsToken = proto.start(fieldId);
+ proto.write(AggStats.MIN, min);
+ proto.write(AggStats.AVERAGE, average);
+ proto.write(AggStats.MAX, max);
+ proto.end(aggStatsToken);
+ }
+}
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index 16f2d7d..2c9f871 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -103,7 +103,7 @@
* <tr>
* <td>ACTION_DRAG_ENDED</td>
* <td style="text-align: center;"> </td>
- * <td style="text-align: center;"> </td>
+ * <td style="text-align: center;">X</td>
* <td style="text-align: center;"> </td>
* <td style="text-align: center;"> </td>
* <td style="text-align: center;"> </td>
@@ -112,6 +112,7 @@
* </table>
* <p>
* The {@link android.view.DragEvent#getAction()},
+ * {@link android.view.DragEvent#getLocalState()}
* {@link android.view.DragEvent#describeContents()},
* {@link android.view.DragEvent#writeToParcel(Parcel,int)}, and
* {@link android.view.DragEvent#toString()} methods always return valid data.
@@ -397,7 +398,7 @@
* operation. In all other activities this method will return null
* </p>
* <p>
- * This method returns valid data for all event actions except for {@link #ACTION_DRAG_ENDED}.
+ * This method returns valid data for all event actions.
* </p>
* @return The local state object sent to the system by startDragAndDrop().
*/
diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java
index 324a1ae..232ff25 100644
--- a/core/java/android/view/Gravity.java
+++ b/core/java/android/view/Gravity.java
@@ -440,4 +440,57 @@
}
return result;
}
+
+ /**
+ * @hide
+ */
+ public static String toString(int gravity) {
+ final StringBuilder result = new StringBuilder();
+ if ((gravity & FILL) != 0) {
+ result.append("FILL").append(' ');
+ } else {
+ if ((gravity & FILL_VERTICAL) != 0) {
+ result.append("FILL_VERTICAL").append(' ');
+ } else {
+ if ((gravity & TOP) != 0) {
+ result.append("TOP").append(' ');
+ }
+ if ((gravity & BOTTOM) != 0) {
+ result.append("BOTTOM").append(' ');
+ }
+ }
+ if ((gravity & FILL_HORIZONTAL) != 0) {
+ result.append("FILL_HORIZONTAL").append(' ');
+ } else {
+ if ((gravity & START) != 0) {
+ result.append("START").append(' ');
+ } else if ((gravity & LEFT) != 0) {
+ result.append("LEFT").append(' ');
+ }
+ if ((gravity & END) != 0) {
+ result.append("END").append(' ');
+ } else if ((gravity & RIGHT) != 0) {
+ result.append("RIGHT").append(' ');
+ }
+ }
+ }
+ if ((gravity & CENTER) != 0) {
+ result.append("CENTER").append(' ');
+ } else {
+ if ((gravity & CENTER_VERTICAL) != 0) {
+ result.append("CENTER_VERTICAL").append(' ');
+ }
+ if ((gravity & CENTER_HORIZONTAL) != 0) {
+ result.append("CENTER_HORIZONTAL").append(' ');
+ }
+ }
+ if ((gravity & DISPLAY_CLIP_VERTICAL) != 0) {
+ result.append("DISPLAY_CLIP_VERTICAL").append(' ');
+ }
+ if ((gravity & DISPLAY_CLIP_VERTICAL) != 0) {
+ result.append("DISPLAY_CLIP_VERTICAL").append(' ');
+ }
+ result.deleteCharAt(result.length() - 1);
+ return result.toString();
+ }
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index e576a0f..6e49bac 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -358,10 +358,10 @@
* Updates the dim layer used while resizing.
*
* @param visible Whether the dim layer should be visible.
- * @param targetStackId The id of the task stack the dim layer should be placed on.
+ * @param targetWindowingMode The windowing mode of the stack the dim layer should be placed on.
* @param alpha The translucency of the dim layer, between 0 and 1.
*/
- void setResizeDimLayer(boolean visible, int targetStackId, float alpha);
+ void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha);
/**
* Requests Keyboard Shortcuts from the displayed window.
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 7198f37..31daeff 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -18,6 +18,7 @@
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
+import android.annotation.Size;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
@@ -65,6 +66,7 @@
private static native void nativeSetSize(long nativeObject, int w, int h);
private static native void nativeSetTransparentRegionHint(long nativeObject, Region region);
private static native void nativeSetAlpha(long nativeObject, float alpha);
+ private static native void nativeSetColor(long nativeObject, float[] color);
private static native void nativeSetMatrix(long nativeObject, float dsdx, float dtdx,
float dtdy, float dsdy);
private static native void nativeSetFlags(long nativeObject, int flags, int mask);
@@ -552,6 +554,15 @@
nativeSetAlpha(mNativeObject, alpha);
}
+ /**
+ * Sets a color for the Surface.
+ * @param color A float array with three values to represent r, g, b in range [0..1]
+ */
+ public void setColor(@Size(3) float[] color) {
+ checkNotReleased();
+ nativeSetColor(mNativeObject, color);
+ }
+
public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
checkNotReleased();
nativeSetMatrix(mNativeObject, dsdx, dtdx, dtdy, dsdy);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1556297..b6be296 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3742,15 +3742,90 @@
* @hide
*/
@ViewDebug.ExportedProperty(flagMapping = {
- @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LOW_PROFILE,
- equals = SYSTEM_UI_FLAG_LOW_PROFILE,
- name = "SYSTEM_UI_FLAG_LOW_PROFILE", outputIf = true),
- @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_HIDE_NAVIGATION,
- equals = SYSTEM_UI_FLAG_HIDE_NAVIGATION,
- name = "SYSTEM_UI_FLAG_HIDE_NAVIGATION", outputIf = true),
- @ViewDebug.FlagToString(mask = PUBLIC_STATUS_BAR_VISIBILITY_MASK,
- equals = SYSTEM_UI_FLAG_VISIBLE,
- name = "SYSTEM_UI_FLAG_VISIBLE", outputIf = true)
+ @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LOW_PROFILE,
+ equals = SYSTEM_UI_FLAG_LOW_PROFILE,
+ name = "LOW_PROFILE"),
+ @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_HIDE_NAVIGATION,
+ equals = SYSTEM_UI_FLAG_HIDE_NAVIGATION,
+ name = "HIDE_NAVIGATION"),
+ @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_FULLSCREEN,
+ equals = SYSTEM_UI_FLAG_FULLSCREEN,
+ name = "FULLSCREEN"),
+ @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LAYOUT_STABLE,
+ equals = SYSTEM_UI_FLAG_LAYOUT_STABLE,
+ name = "LAYOUT_STABLE"),
+ @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION,
+ equals = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION,
+ name = "LAYOUT_HIDE_NAVIGATION"),
+ @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,
+ equals = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,
+ name = "LAYOUT_FULLSCREEN"),
+ @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_IMMERSIVE,
+ equals = SYSTEM_UI_FLAG_IMMERSIVE,
+ name = "IMMERSIVE"),
+ @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_IMMERSIVE_STICKY,
+ equals = SYSTEM_UI_FLAG_IMMERSIVE_STICKY,
+ name = "IMMERSIVE_STICKY"),
+ @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
+ equals = SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
+ name = "LIGHT_STATUS_BAR"),
+ @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ equals = SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ name = "LIGHT_NAVIGATION_BAR"),
+ @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_EXPAND,
+ equals = STATUS_BAR_DISABLE_EXPAND,
+ name = "STATUS_BAR_DISABLE_EXPAND"),
+ @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_NOTIFICATION_ICONS,
+ equals = STATUS_BAR_DISABLE_NOTIFICATION_ICONS,
+ name = "STATUS_BAR_DISABLE_NOTIFICATION_ICONS"),
+ @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_NOTIFICATION_ALERTS,
+ equals = STATUS_BAR_DISABLE_NOTIFICATION_ALERTS,
+ name = "STATUS_BAR_DISABLE_NOTIFICATION_ALERTS"),
+ @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_NOTIFICATION_TICKER,
+ equals = STATUS_BAR_DISABLE_NOTIFICATION_TICKER,
+ name = "STATUS_BAR_DISABLE_NOTIFICATION_TICKER"),
+ @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_SYSTEM_INFO,
+ equals = STATUS_BAR_DISABLE_SYSTEM_INFO,
+ name = "STATUS_BAR_DISABLE_SYSTEM_INFO"),
+ @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_HOME,
+ equals = STATUS_BAR_DISABLE_HOME,
+ name = "STATUS_BAR_DISABLE_HOME"),
+ @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_BACK,
+ equals = STATUS_BAR_DISABLE_BACK,
+ name = "STATUS_BAR_DISABLE_BACK"),
+ @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_CLOCK,
+ equals = STATUS_BAR_DISABLE_CLOCK,
+ name = "STATUS_BAR_DISABLE_CLOCK"),
+ @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_RECENT,
+ equals = STATUS_BAR_DISABLE_RECENT,
+ name = "STATUS_BAR_DISABLE_RECENT"),
+ @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_SEARCH,
+ equals = STATUS_BAR_DISABLE_SEARCH,
+ name = "STATUS_BAR_DISABLE_SEARCH"),
+ @ViewDebug.FlagToString(mask = STATUS_BAR_TRANSIENT,
+ equals = STATUS_BAR_TRANSIENT,
+ name = "STATUS_BAR_TRANSIENT"),
+ @ViewDebug.FlagToString(mask = NAVIGATION_BAR_TRANSIENT,
+ equals = NAVIGATION_BAR_TRANSIENT,
+ name = "NAVIGATION_BAR_TRANSIENT"),
+ @ViewDebug.FlagToString(mask = STATUS_BAR_UNHIDE,
+ equals = STATUS_BAR_UNHIDE,
+ name = "STATUS_BAR_UNHIDE"),
+ @ViewDebug.FlagToString(mask = NAVIGATION_BAR_UNHIDE,
+ equals = NAVIGATION_BAR_UNHIDE,
+ name = "NAVIGATION_BAR_UNHIDE"),
+ @ViewDebug.FlagToString(mask = STATUS_BAR_TRANSLUCENT,
+ equals = STATUS_BAR_TRANSLUCENT,
+ name = "STATUS_BAR_TRANSLUCENT"),
+ @ViewDebug.FlagToString(mask = NAVIGATION_BAR_TRANSLUCENT,
+ equals = NAVIGATION_BAR_TRANSLUCENT,
+ name = "NAVIGATION_BAR_TRANSLUCENT"),
+ @ViewDebug.FlagToString(mask = NAVIGATION_BAR_TRANSPARENT,
+ equals = NAVIGATION_BAR_TRANSPARENT,
+ name = "NAVIGATION_BAR_TRANSPARENT"),
+ @ViewDebug.FlagToString(mask = STATUS_BAR_TRANSPARENT,
+ equals = STATUS_BAR_TRANSPARENT,
+ name = "STATUS_BAR_TRANSPARENT")
}, formatToHexString = true)
int mSystemUiVisibility;
@@ -15454,7 +15529,13 @@
* {@code dirty}.
*
* @param dirty the rectangle representing the bounds of the dirty region
+ *
+ * @deprecated The switch to hardware accelerated rendering in API 14 reduced
+ * the importance of the dirty rectangle. In API 21 the given rectangle is
+ * ignored entirely in favor of an internally-calculated area instead.
+ * Because of this, clients are encouraged to just call {@link #invalidate()}.
*/
+ @Deprecated
public void invalidate(Rect dirty) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
@@ -15475,7 +15556,13 @@
* @param t the top position of the dirty region
* @param r the right position of the dirty region
* @param b the bottom position of the dirty region
+ *
+ * @deprecated The switch to hardware accelerated rendering in API 14 reduced
+ * the importance of the dirty rectangle. In API 21 the given rectangle is
+ * ignored entirely in favor of an internally-calculated area instead.
+ * Because of this, clients are encouraged to just call {@link #invalidate()}.
*/
+ @Deprecated
public void invalidate(int l, int t, int r, int b) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 66c0578..3426485 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1375,6 +1375,81 @@
}
}
+ /**
+ * Converts an integer from a field that is mapped with {@link IntToString} to its string
+ * representation.
+ *
+ * @param clazz The class the field is defined on.
+ * @param field The field on which the {@link ExportedProperty} is defined on.
+ * @param integer The value to convert.
+ * @return The value converted into its string representation.
+ * @hide
+ */
+ public static String intToString(Class<?> clazz, String field, int integer) {
+ final IntToString[] mapping = getMapping(clazz, field);
+ if (mapping == null) {
+ return Integer.toString(integer);
+ }
+ final int count = mapping.length;
+ for (int j = 0; j < count; j++) {
+ final IntToString map = mapping[j];
+ if (map.from() == integer) {
+ return map.to();
+ }
+ }
+ return Integer.toString(integer);
+ }
+
+ /**
+ * Converts a set of flags from a field that is mapped with {@link FlagToString} to its string
+ * representation.
+ *
+ * @param clazz The class the field is defined on.
+ * @param field The field on which the {@link ExportedProperty} is defined on.
+ * @param flags The flags to convert.
+ * @return The flags converted into their string representations.
+ * @hide
+ */
+ public static String flagsToString(Class<?> clazz, String field, int flags) {
+ final FlagToString[] mapping = getFlagMapping(clazz, field);
+ if (mapping == null) {
+ return Integer.toHexString(flags);
+ }
+ final StringBuilder result = new StringBuilder();
+ final int count = mapping.length;
+ for (int j = 0; j < count; j++) {
+ final FlagToString flagMapping = mapping[j];
+ final boolean ifTrue = flagMapping.outputIf();
+ final int maskResult = flags & flagMapping.mask();
+ final boolean test = maskResult == flagMapping.equals();
+ if (test && ifTrue) {
+ final String name = flagMapping.name();
+ result.append(name).append(' ');
+ }
+ }
+ if (result.length() > 0) {
+ result.deleteCharAt(result.length() - 1);
+ }
+ return result.toString();
+ }
+
+ private static FlagToString[] getFlagMapping(Class<?> clazz, String field) {
+ try {
+ return clazz.getDeclaredField(field).getAnnotation(ExportedProperty.class)
+ .flagMapping();
+ } catch (NoSuchFieldException e) {
+ return null;
+ }
+ }
+
+ private static IntToString[] getMapping(Class<?> clazz, String field) {
+ try {
+ return clazz.getDeclaredField(field).getAnnotation(ExportedProperty.class).mapping();
+ } catch (NoSuchFieldException e) {
+ return null;
+ }
+ }
+
private static void exportUnrolledArray(Context context, BufferedWriter out,
ExportedProperty property, int[] array, String prefix, String suffix)
throws IOException {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 415aad5..71106ad 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -366,7 +366,7 @@
// These can be accessed by any thread, must be protected with a lock.
// Surface can never be reassigned or cleared (use Surface.clear()).
- final Surface mSurface = new Surface();
+ public final Surface mSurface = new Surface();
boolean mAdded;
boolean mAddedTouchMode;
@@ -512,7 +512,7 @@
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
if (!sCompatibilityDone) {
- sAlwaysAssignFocus = true;
+ sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
sCompatibilityDone = true;
}
@@ -7714,7 +7714,7 @@
public void onAccessibilityStateChanged(boolean enabled) {
if (enabled) {
ensureConnection();
- if (mAttachInfo.mHasWindowFocus) {
+ if (mAttachInfo.mHasWindowFocus && (mView != null)) {
mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
View focusedView = mView.findFocus();
if (focusedView != null && focusedView != mView) {
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 0ecd20d..f671c34 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -378,7 +378,7 @@
*
* <p>Typically used when the view is a container for an HTML document.
*
- * @param domain URL representing the domain; only the host part will be used.
+ * @param domain RFC 2396-compliant URI representing the domain.
*/
public abstract void setWebDomain(@Nullable String domain);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e56a82f..c29a1da 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.content.pm.ActivityInfo.COLOR_MODE_DEFAULT;
+
import android.Manifest.permission;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -268,93 +270,93 @@
*/
@ViewDebug.ExportedProperty(mapping = {
@ViewDebug.IntToString(from = TYPE_BASE_APPLICATION,
- to = "TYPE_BASE_APPLICATION"),
+ to = "BASE_APPLICATION"),
@ViewDebug.IntToString(from = TYPE_APPLICATION,
- to = "TYPE_APPLICATION"),
+ to = "APPLICATION"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_STARTING,
- to = "TYPE_APPLICATION_STARTING"),
+ to = "APPLICATION_STARTING"),
@ViewDebug.IntToString(from = TYPE_DRAWN_APPLICATION,
- to = "TYPE_DRAWN_APPLICATION"),
+ to = "DRAWN_APPLICATION"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_PANEL,
- to = "TYPE_APPLICATION_PANEL"),
+ to = "APPLICATION_PANEL"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA,
- to = "TYPE_APPLICATION_MEDIA"),
+ to = "APPLICATION_MEDIA"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_SUB_PANEL,
- to = "TYPE_APPLICATION_SUB_PANEL"),
+ to = "APPLICATION_SUB_PANEL"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_ABOVE_SUB_PANEL,
- to = "TYPE_APPLICATION_ABOVE_SUB_PANEL"),
+ to = "APPLICATION_ABOVE_SUB_PANEL"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_ATTACHED_DIALOG,
- to = "TYPE_APPLICATION_ATTACHED_DIALOG"),
+ to = "APPLICATION_ATTACHED_DIALOG"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA_OVERLAY,
- to = "TYPE_APPLICATION_MEDIA_OVERLAY"),
+ to = "APPLICATION_MEDIA_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_STATUS_BAR,
- to = "TYPE_STATUS_BAR"),
+ to = "STATUS_BAR"),
@ViewDebug.IntToString(from = TYPE_SEARCH_BAR,
- to = "TYPE_SEARCH_BAR"),
+ to = "SEARCH_BAR"),
@ViewDebug.IntToString(from = TYPE_PHONE,
- to = "TYPE_PHONE"),
+ to = "PHONE"),
@ViewDebug.IntToString(from = TYPE_SYSTEM_ALERT,
- to = "TYPE_SYSTEM_ALERT"),
+ to = "SYSTEM_ALERT"),
@ViewDebug.IntToString(from = TYPE_TOAST,
- to = "TYPE_TOAST"),
+ to = "TOAST"),
@ViewDebug.IntToString(from = TYPE_SYSTEM_OVERLAY,
- to = "TYPE_SYSTEM_OVERLAY"),
+ to = "SYSTEM_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_PRIORITY_PHONE,
- to = "TYPE_PRIORITY_PHONE"),
+ to = "PRIORITY_PHONE"),
@ViewDebug.IntToString(from = TYPE_SYSTEM_DIALOG,
- to = "TYPE_SYSTEM_DIALOG"),
+ to = "SYSTEM_DIALOG"),
@ViewDebug.IntToString(from = TYPE_KEYGUARD_DIALOG,
- to = "TYPE_KEYGUARD_DIALOG"),
+ to = "KEYGUARD_DIALOG"),
@ViewDebug.IntToString(from = TYPE_SYSTEM_ERROR,
- to = "TYPE_SYSTEM_ERROR"),
+ to = "SYSTEM_ERROR"),
@ViewDebug.IntToString(from = TYPE_INPUT_METHOD,
- to = "TYPE_INPUT_METHOD"),
+ to = "INPUT_METHOD"),
@ViewDebug.IntToString(from = TYPE_INPUT_METHOD_DIALOG,
- to = "TYPE_INPUT_METHOD_DIALOG"),
+ to = "INPUT_METHOD_DIALOG"),
@ViewDebug.IntToString(from = TYPE_WALLPAPER,
- to = "TYPE_WALLPAPER"),
+ to = "WALLPAPER"),
@ViewDebug.IntToString(from = TYPE_STATUS_BAR_PANEL,
- to = "TYPE_STATUS_BAR_PANEL"),
+ to = "STATUS_BAR_PANEL"),
@ViewDebug.IntToString(from = TYPE_SECURE_SYSTEM_OVERLAY,
- to = "TYPE_SECURE_SYSTEM_OVERLAY"),
+ to = "SECURE_SYSTEM_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_DRAG,
- to = "TYPE_DRAG"),
+ to = "DRAG"),
@ViewDebug.IntToString(from = TYPE_STATUS_BAR_SUB_PANEL,
- to = "TYPE_STATUS_BAR_SUB_PANEL"),
+ to = "STATUS_BAR_SUB_PANEL"),
@ViewDebug.IntToString(from = TYPE_POINTER,
- to = "TYPE_POINTER"),
+ to = "POINTER"),
@ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR,
- to = "TYPE_NAVIGATION_BAR"),
+ to = "NAVIGATION_BAR"),
@ViewDebug.IntToString(from = TYPE_VOLUME_OVERLAY,
- to = "TYPE_VOLUME_OVERLAY"),
+ to = "VOLUME_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_BOOT_PROGRESS,
- to = "TYPE_BOOT_PROGRESS"),
+ to = "BOOT_PROGRESS"),
@ViewDebug.IntToString(from = TYPE_INPUT_CONSUMER,
- to = "TYPE_INPUT_CONSUMER"),
+ to = "INPUT_CONSUMER"),
@ViewDebug.IntToString(from = TYPE_DREAM,
- to = "TYPE_DREAM"),
+ to = "DREAM"),
@ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR_PANEL,
- to = "TYPE_NAVIGATION_BAR_PANEL"),
+ to = "NAVIGATION_BAR_PANEL"),
@ViewDebug.IntToString(from = TYPE_DISPLAY_OVERLAY,
- to = "TYPE_DISPLAY_OVERLAY"),
+ to = "DISPLAY_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_MAGNIFICATION_OVERLAY,
- to = "TYPE_MAGNIFICATION_OVERLAY"),
+ to = "MAGNIFICATION_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_PRESENTATION,
- to = "TYPE_PRESENTATION"),
+ to = "PRESENTATION"),
@ViewDebug.IntToString(from = TYPE_PRIVATE_PRESENTATION,
- to = "TYPE_PRIVATE_PRESENTATION"),
+ to = "PRIVATE_PRESENTATION"),
@ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION,
- to = "TYPE_VOICE_INTERACTION"),
+ to = "VOICE_INTERACTION"),
@ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING,
- to = "TYPE_VOICE_INTERACTION_STARTING"),
+ to = "VOICE_INTERACTION_STARTING"),
@ViewDebug.IntToString(from = TYPE_DOCK_DIVIDER,
- to = "TYPE_DOCK_DIVIDER"),
+ to = "DOCK_DIVIDER"),
@ViewDebug.IntToString(from = TYPE_QS_DIALOG,
- to = "TYPE_QS_DIALOG"),
+ to = "QS_DIALOG"),
@ViewDebug.IntToString(from = TYPE_SCREENSHOT,
- to = "TYPE_SCREENSHOT"),
+ to = "SCREENSHOT"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_OVERLAY,
- to = "TYPE_APPLICATION_OVERLAY")
+ to = "APPLICATION_OVERLAY")
})
public int type;
@@ -1198,63 +1200,69 @@
*/
@ViewDebug.ExportedProperty(flagMapping = {
@ViewDebug.FlagToString(mask = FLAG_ALLOW_LOCK_WHILE_SCREEN_ON, equals = FLAG_ALLOW_LOCK_WHILE_SCREEN_ON,
- name = "FLAG_ALLOW_LOCK_WHILE_SCREEN_ON"),
+ name = "ALLOW_LOCK_WHILE_SCREEN_ON"),
@ViewDebug.FlagToString(mask = FLAG_DIM_BEHIND, equals = FLAG_DIM_BEHIND,
- name = "FLAG_DIM_BEHIND"),
+ name = "DIM_BEHIND"),
@ViewDebug.FlagToString(mask = FLAG_BLUR_BEHIND, equals = FLAG_BLUR_BEHIND,
- name = "FLAG_BLUR_BEHIND"),
+ name = "BLUR_BEHIND"),
@ViewDebug.FlagToString(mask = FLAG_NOT_FOCUSABLE, equals = FLAG_NOT_FOCUSABLE,
- name = "FLAG_NOT_FOCUSABLE"),
+ name = "NOT_FOCUSABLE"),
@ViewDebug.FlagToString(mask = FLAG_NOT_TOUCHABLE, equals = FLAG_NOT_TOUCHABLE,
- name = "FLAG_NOT_TOUCHABLE"),
+ name = "NOT_TOUCHABLE"),
@ViewDebug.FlagToString(mask = FLAG_NOT_TOUCH_MODAL, equals = FLAG_NOT_TOUCH_MODAL,
- name = "FLAG_NOT_TOUCH_MODAL"),
+ name = "NOT_TOUCH_MODAL"),
@ViewDebug.FlagToString(mask = FLAG_TOUCHABLE_WHEN_WAKING, equals = FLAG_TOUCHABLE_WHEN_WAKING,
- name = "FLAG_TOUCHABLE_WHEN_WAKING"),
+ name = "TOUCHABLE_WHEN_WAKING"),
@ViewDebug.FlagToString(mask = FLAG_KEEP_SCREEN_ON, equals = FLAG_KEEP_SCREEN_ON,
- name = "FLAG_KEEP_SCREEN_ON"),
+ name = "KEEP_SCREEN_ON"),
@ViewDebug.FlagToString(mask = FLAG_LAYOUT_IN_SCREEN, equals = FLAG_LAYOUT_IN_SCREEN,
- name = "FLAG_LAYOUT_IN_SCREEN"),
+ name = "LAYOUT_IN_SCREEN"),
@ViewDebug.FlagToString(mask = FLAG_LAYOUT_NO_LIMITS, equals = FLAG_LAYOUT_NO_LIMITS,
- name = "FLAG_LAYOUT_NO_LIMITS"),
+ name = "LAYOUT_NO_LIMITS"),
@ViewDebug.FlagToString(mask = FLAG_FULLSCREEN, equals = FLAG_FULLSCREEN,
- name = "FLAG_FULLSCREEN"),
+ name = "FULLSCREEN"),
@ViewDebug.FlagToString(mask = FLAG_FORCE_NOT_FULLSCREEN, equals = FLAG_FORCE_NOT_FULLSCREEN,
- name = "FLAG_FORCE_NOT_FULLSCREEN"),
+ name = "FORCE_NOT_FULLSCREEN"),
@ViewDebug.FlagToString(mask = FLAG_DITHER, equals = FLAG_DITHER,
- name = "FLAG_DITHER"),
+ name = "DITHER"),
@ViewDebug.FlagToString(mask = FLAG_SECURE, equals = FLAG_SECURE,
- name = "FLAG_SECURE"),
+ name = "SECURE"),
@ViewDebug.FlagToString(mask = FLAG_SCALED, equals = FLAG_SCALED,
- name = "FLAG_SCALED"),
+ name = "SCALED"),
@ViewDebug.FlagToString(mask = FLAG_IGNORE_CHEEK_PRESSES, equals = FLAG_IGNORE_CHEEK_PRESSES,
- name = "FLAG_IGNORE_CHEEK_PRESSES"),
+ name = "IGNORE_CHEEK_PRESSES"),
@ViewDebug.FlagToString(mask = FLAG_LAYOUT_INSET_DECOR, equals = FLAG_LAYOUT_INSET_DECOR,
- name = "FLAG_LAYOUT_INSET_DECOR"),
+ name = "LAYOUT_INSET_DECOR"),
@ViewDebug.FlagToString(mask = FLAG_ALT_FOCUSABLE_IM, equals = FLAG_ALT_FOCUSABLE_IM,
- name = "FLAG_ALT_FOCUSABLE_IM"),
+ name = "ALT_FOCUSABLE_IM"),
@ViewDebug.FlagToString(mask = FLAG_WATCH_OUTSIDE_TOUCH, equals = FLAG_WATCH_OUTSIDE_TOUCH,
- name = "FLAG_WATCH_OUTSIDE_TOUCH"),
+ name = "WATCH_OUTSIDE_TOUCH"),
@ViewDebug.FlagToString(mask = FLAG_SHOW_WHEN_LOCKED, equals = FLAG_SHOW_WHEN_LOCKED,
- name = "FLAG_SHOW_WHEN_LOCKED"),
+ name = "SHOW_WHEN_LOCKED"),
@ViewDebug.FlagToString(mask = FLAG_SHOW_WALLPAPER, equals = FLAG_SHOW_WALLPAPER,
- name = "FLAG_SHOW_WALLPAPER"),
+ name = "SHOW_WALLPAPER"),
@ViewDebug.FlagToString(mask = FLAG_TURN_SCREEN_ON, equals = FLAG_TURN_SCREEN_ON,
- name = "FLAG_TURN_SCREEN_ON"),
+ name = "TURN_SCREEN_ON"),
@ViewDebug.FlagToString(mask = FLAG_DISMISS_KEYGUARD, equals = FLAG_DISMISS_KEYGUARD,
- name = "FLAG_DISMISS_KEYGUARD"),
+ name = "DISMISS_KEYGUARD"),
@ViewDebug.FlagToString(mask = FLAG_SPLIT_TOUCH, equals = FLAG_SPLIT_TOUCH,
- name = "FLAG_SPLIT_TOUCH"),
+ name = "SPLIT_TOUCH"),
@ViewDebug.FlagToString(mask = FLAG_HARDWARE_ACCELERATED, equals = FLAG_HARDWARE_ACCELERATED,
- name = "FLAG_HARDWARE_ACCELERATED"),
- @ViewDebug.FlagToString(mask = FLAG_LOCAL_FOCUS_MODE, equals = FLAG_LOCAL_FOCUS_MODE,
- name = "FLAG_LOCAL_FOCUS_MODE"),
+ name = "HARDWARE_ACCELERATED"),
+ @ViewDebug.FlagToString(mask = FLAG_LAYOUT_IN_OVERSCAN, equals = FLAG_LAYOUT_IN_OVERSCAN,
+ name = "LOCAL_FOCUS_MODE"),
@ViewDebug.FlagToString(mask = FLAG_TRANSLUCENT_STATUS, equals = FLAG_TRANSLUCENT_STATUS,
- name = "FLAG_TRANSLUCENT_STATUS"),
+ name = "TRANSLUCENT_STATUS"),
@ViewDebug.FlagToString(mask = FLAG_TRANSLUCENT_NAVIGATION, equals = FLAG_TRANSLUCENT_NAVIGATION,
- name = "FLAG_TRANSLUCENT_NAVIGATION"),
+ name = "TRANSLUCENT_NAVIGATION"),
+ @ViewDebug.FlagToString(mask = FLAG_LOCAL_FOCUS_MODE, equals = FLAG_LOCAL_FOCUS_MODE,
+ name = "LOCAL_FOCUS_MODE"),
+ @ViewDebug.FlagToString(mask = FLAG_SLIPPERY, equals = FLAG_SLIPPERY,
+ name = "FLAG_SLIPPERY"),
+ @ViewDebug.FlagToString(mask = FLAG_LAYOUT_ATTACHED_IN_DECOR, equals = FLAG_LAYOUT_ATTACHED_IN_DECOR,
+ name = "FLAG_LAYOUT_ATTACHED_IN_DECOR"),
@ViewDebug.FlagToString(mask = FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, equals = FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- name = "FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS")
+ name = "DRAWS_SYSTEM_BAR_BACKGROUNDS")
}, formatToHexString = true)
public int flags;
@@ -1438,6 +1446,88 @@
* Control flags that are private to the platform.
* @hide
*/
+ @ViewDebug.ExportedProperty(flagMapping = {
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED,
+ equals = PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED,
+ name = "FAKE_HARDWARE_ACCELERATED"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED,
+ equals = PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED,
+ name = "FORCE_HARDWARE_ACCELERATED"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS,
+ equals = PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS,
+ name = "WANTS_OFFSET_NOTIFICATIONS"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_SHOW_FOR_ALL_USERS,
+ equals = PRIVATE_FLAG_SHOW_FOR_ALL_USERS,
+ name = "SHOW_FOR_ALL_USERS"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_NO_MOVE_ANIMATION,
+ equals = PRIVATE_FLAG_NO_MOVE_ANIMATION,
+ name = "NO_MOVE_ANIMATION"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_COMPATIBLE_WINDOW,
+ equals = PRIVATE_FLAG_COMPATIBLE_WINDOW,
+ name = "COMPATIBLE_WINDOW"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_SYSTEM_ERROR,
+ equals = PRIVATE_FLAG_SYSTEM_ERROR,
+ name = "SYSTEM_ERROR"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR,
+ equals = PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR,
+ name = "INHERIT_TRANSLUCENT_DECOR"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_KEYGUARD,
+ equals = PRIVATE_FLAG_KEYGUARD,
+ name = "KEYGUARD"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
+ equals = PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
+ name = "DISABLE_WALLPAPER_TOUCH_EVENTS"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT,
+ equals = PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT,
+ name = "FORCE_STATUS_BAR_VISIBLE_TRANSPARENT"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_PRESERVE_GEOMETRY,
+ equals = PRIVATE_FLAG_PRESERVE_GEOMETRY,
+ name = "PRESERVE_GEOMETRY"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY,
+ equals = PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY,
+ name = "FORCE_DECOR_VIEW_VISIBILITY"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH,
+ equals = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH,
+ name = "WILL_NOT_REPLACE_ON_RELAUNCH"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME,
+ equals = PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME,
+ name = "LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND,
+ equals = PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND,
+ name = "FORCE_DRAW_STATUS_BAR_BACKGROUND"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE,
+ equals = PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE,
+ name = "SUSTAINED_PERFORMANCE_MODE"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+ equals = PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+ name = "HIDE_NON_SYSTEM_OVERLAY_WINDOWS"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
+ equals = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
+ name = "IS_ROUNDED_CORNERS_OVERLAY"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN,
+ equals = PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN,
+ name = "ACQUIRES_SLEEP_TOKEN")
+ })
@TestApi
public int privateFlags;
@@ -1977,7 +2067,7 @@
* @hide
*/
@ActivityInfo.ColorMode
- private int mColorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+ private int mColorMode = COLOR_MODE_DEFAULT;
public LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
@@ -2442,9 +2532,15 @@
@Override
public String toString() {
+ return toString("");
+ }
+
+ /**
+ * @hide
+ */
+ public String toString(String prefix) {
StringBuilder sb = new StringBuilder(256);
- sb.append("WM.LayoutParams{");
- sb.append("(");
+ sb.append("{(");
sb.append(x);
sb.append(',');
sb.append(y);
@@ -2464,26 +2560,19 @@
sb.append(verticalMargin);
}
if (gravity != 0) {
- sb.append(" gr=#");
- sb.append(Integer.toHexString(gravity));
+ sb.append(" gr=");
+ sb.append(Gravity.toString(gravity));
}
if (softInputMode != 0) {
- sb.append(" sim=#");
- sb.append(Integer.toHexString(softInputMode));
+ sb.append(" sim={");
+ sb.append(softInputModeToString(softInputMode));
+ sb.append('}');
}
sb.append(" ty=");
- sb.append(type);
- sb.append(" fl=#");
- sb.append(Integer.toHexString(flags));
- if (privateFlags != 0) {
- if ((privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0) {
- sb.append(" compatible=true");
- }
- sb.append(" pfl=0x").append(Integer.toHexString(privateFlags));
- }
+ sb.append(ViewDebug.intToString(LayoutParams.class, "type", type));
if (format != PixelFormat.OPAQUE) {
sb.append(" fmt=");
- sb.append(format);
+ sb.append(PixelFormat.formatToString(format));
}
if (windowAnimations != 0) {
sb.append(" wanim=0x");
@@ -2491,7 +2580,7 @@
}
if (screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
sb.append(" or=");
- sb.append(screenOrientation);
+ sb.append(ActivityInfo.screenOrientationToString(screenOrientation));
}
if (alpha != 1.0f) {
sb.append(" alpha=");
@@ -2507,7 +2596,7 @@
}
if (rotationAnimation != ROTATION_ANIMATION_ROTATE) {
sb.append(" rotAnim=");
- sb.append(rotationAnimation);
+ sb.append(rotationAnimationToString(rotationAnimation));
}
if (preferredRefreshRate != 0) {
sb.append(" preferredRefreshRate=");
@@ -2517,20 +2606,12 @@
sb.append(" preferredDisplayMode=");
sb.append(preferredDisplayModeId);
}
- if (systemUiVisibility != 0) {
- sb.append(" sysui=0x");
- sb.append(Integer.toHexString(systemUiVisibility));
- }
- if (subtreeSystemUiVisibility != 0) {
- sb.append(" vsysui=0x");
- sb.append(Integer.toHexString(subtreeSystemUiVisibility));
- }
if (hasSystemUiListeners) {
sb.append(" sysuil=");
sb.append(hasSystemUiListeners);
}
if (inputFeatures != 0) {
- sb.append(" if=0x").append(Integer.toHexString(inputFeatures));
+ sb.append(" if=").append(inputFeatureToString(inputFeatures));
}
if (userActivityTimeout >= 0) {
sb.append(" userActivityTimeout=").append(userActivityTimeout);
@@ -2546,11 +2627,30 @@
sb.append(" (!preservePreviousSurfaceInsets)");
}
}
- if (needsMenuKey != NEEDS_MENU_UNSET) {
- sb.append(" needsMenuKey=");
- sb.append(needsMenuKey);
+ if (needsMenuKey == NEEDS_MENU_SET_TRUE) {
+ sb.append(" needsMenuKey");
}
- sb.append(" colorMode=").append(mColorMode);
+ if (mColorMode != COLOR_MODE_DEFAULT) {
+ sb.append(" colorMode=").append(ActivityInfo.colorModeToString(mColorMode));
+ }
+ sb.append(System.lineSeparator());
+ sb.append(prefix).append(" fl=").append(
+ ViewDebug.flagsToString(LayoutParams.class, "flags", flags));
+ if (privateFlags != 0) {
+ sb.append(System.lineSeparator());
+ sb.append(prefix).append(" pfl=").append(ViewDebug.flagsToString(
+ LayoutParams.class, "privateFlags", privateFlags));
+ }
+ if (systemUiVisibility != 0) {
+ sb.append(System.lineSeparator());
+ sb.append(prefix).append(" sysui=").append(ViewDebug.flagsToString(
+ View.class, "mSystemUiVisibility", systemUiVisibility));
+ }
+ if (subtreeSystemUiVisibility != 0) {
+ sb.append(System.lineSeparator());
+ sb.append(prefix).append(" vsysui=").append(ViewDebug.flagsToString(
+ View.class, "mSystemUiVisibility", subtreeSystemUiVisibility));
+ }
sb.append('}');
return sb.toString();
}
@@ -2634,5 +2734,88 @@
&& width == WindowManager.LayoutParams.MATCH_PARENT
&& height == WindowManager.LayoutParams.MATCH_PARENT;
}
+
+ private static String softInputModeToString(@SoftInputModeFlags int softInputMode) {
+ final StringBuilder result = new StringBuilder();
+ final int state = softInputMode & SOFT_INPUT_MASK_STATE;
+ if (state != 0) {
+ result.append("state=");
+ switch (state) {
+ case SOFT_INPUT_STATE_UNCHANGED:
+ result.append("unchanged");
+ break;
+ case SOFT_INPUT_STATE_HIDDEN:
+ result.append("hidden");
+ break;
+ case SOFT_INPUT_STATE_ALWAYS_HIDDEN:
+ result.append("always_hidden");
+ break;
+ case SOFT_INPUT_STATE_VISIBLE:
+ result.append("visible");
+ break;
+ case SOFT_INPUT_STATE_ALWAYS_VISIBLE:
+ result.append("always_visible");
+ break;
+ default:
+ result.append(state);
+ break;
+ }
+ result.append(' ');
+ }
+ final int adjust = softInputMode & SOFT_INPUT_MASK_ADJUST;
+ if (adjust != 0) {
+ result.append("adjust=");
+ switch (adjust) {
+ case SOFT_INPUT_ADJUST_RESIZE:
+ result.append("resize");
+ break;
+ case SOFT_INPUT_ADJUST_PAN:
+ result.append("pan");
+ break;
+ case SOFT_INPUT_ADJUST_NOTHING:
+ result.append("nothing");
+ break;
+ default:
+ result.append(adjust);
+ break;
+ }
+ result.append(' ');
+ }
+ if ((softInputMode & SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
+ result.append("forwardNavigation").append(' ');
+ }
+ result.deleteCharAt(result.length() - 1);
+ return result.toString();
+ }
+
+ private static String rotationAnimationToString(int rotationAnimation) {
+ switch (rotationAnimation) {
+ case ROTATION_ANIMATION_UNSPECIFIED:
+ return "UNSPECIFIED";
+ case ROTATION_ANIMATION_ROTATE:
+ return "ROTATE";
+ case ROTATION_ANIMATION_CROSSFADE:
+ return "CROSSFADE";
+ case ROTATION_ANIMATION_JUMPCUT:
+ return "JUMPCUT";
+ case ROTATION_ANIMATION_SEAMLESS:
+ return "SEAMLESS";
+ default:
+ return Integer.toString(rotationAnimation);
+ }
+ }
+
+ private static String inputFeatureToString(int inputFeature) {
+ switch (inputFeature) {
+ case INPUT_FEATURE_DISABLE_POINTER_GESTURES:
+ return "DISABLE_POINTER_GESTURES";
+ case INPUT_FEATURE_NO_INPUT_CHANNEL:
+ return "NO_INPUT_CHANNEL";
+ case INPUT_FEATURE_DISABLE_USER_ACTIVITY:
+ return "DISABLE_USER_ACTIVITY";
+ default:
+ return Integer.toString(inputFeature);
+ }
+ }
}
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 4131fd1..da72535 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -592,9 +592,10 @@
int getDockedDividerInsetsLw();
/**
- * Retrieves the {@param outBounds} from the stack with id {@param stackId}.
+ * Retrieves the {@param outBounds} from the stack matching the {@param windowingMode} and
+ * {@param activityType}.
*/
- void getStackBounds(int stackId, Rect outBounds);
+ void getStackBounds(int windowingMode, int activityType, Rect outBounds);
/**
* Notifies window manager that {@link #isShowingDreamLw} has changed.
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index f888ba2..4fb2a99 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -937,10 +937,7 @@
}
private AutofillClient getClientLocked() {
- if (mContext instanceof AutofillClient) {
- return (AutofillClient) mContext;
- }
- return null;
+ return mContext.getAutofillClient();
}
/** @hide */
@@ -1342,11 +1339,17 @@
}
}
- private void setSessionFinished() {
- if (sVerbose) Log.v(TAG, "setSessionFinished()");
+ /**
+ * Marks the state of the session as finished.
+ *
+ * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
+ * FillResponse) or {@link #STATE_UNKNOWN} (because the session was removed).
+ */
+ private void setSessionFinished(int newState) {
synchronized (mLock) {
+ if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
resetSessionLocked();
- mState = STATE_FINISHED;
+ mState = newState;
}
}
@@ -1413,7 +1416,7 @@
if (sessionFinished) {
// Callback call was "hijacked" to also update the session state.
- setSessionFinished();
+ setSessionFinished(STATE_FINISHED);
}
}
@@ -1898,10 +1901,10 @@
}
@Override
- public void setSessionFinished() {
+ public void setSessionFinished(int newState) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.post(() -> afm.setSessionFinished());
+ afm.post(() -> afm.setSessionFinished(newState));
}
}
}
diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java
index 5f47638..b4688bb 100644
--- a/core/java/android/view/autofill/AutofillPopupWindow.java
+++ b/core/java/android/view/autofill/AutofillPopupWindow.java
@@ -47,6 +47,19 @@
private final WindowPresenter mWindowPresenter;
private WindowManager.LayoutParams mWindowLayoutParams;
+ private final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ /* ignore - handled by the super class */
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ dismiss();
+ }
+ };
+
/**
* Creates a popup window with a presenter owning the window and responsible for
* showing/hiding/updating the backing window. This can be useful of the window is
@@ -208,7 +221,21 @@
p.packageName = anchor.getContext().getPackageName();
mWindowPresenter.show(p, getTransitionEpicenter(), isLayoutInsetDecor(),
anchor.getLayoutDirection());
- return;
+ }
+
+ @Override
+ protected void attachToAnchor(View anchor, int xoff, int yoff, int gravity) {
+ super.attachToAnchor(anchor, xoff, yoff, gravity);
+ anchor.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
+ }
+
+ @Override
+ protected void detachFromAnchor() {
+ final View anchor = getAnchor();
+ if (anchor != null) {
+ anchor.removeOnAttachStateChangeListener(mOnAttachStateChangeListener);
+ }
+ super.detachFromAnchor();
}
@Override
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index db6855a..3dabcec 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -82,8 +82,9 @@
void setSaveUiState(int sessionId, boolean shown);
/**
- * Marks the state of the session as finished (because the AutofillService returned a null
- * FillResponse).
+ * Marks the state of the session as finished.
+ * @param newState STATE_FINISHED (because the autofill service returned a null
+ * FillResponse) or STATE_UNKNOWN (because the session was removed).
*/
- void setSessionFinished();
+ void setSessionFinished(int newState);
}
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 1849368..8c3b8a2 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -28,6 +28,7 @@
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -41,10 +42,10 @@
static final TextClassification EMPTY = new TextClassification.Builder().build();
@NonNull private final String mText;
- @Nullable private final Drawable mIcon;
- @Nullable private final String mLabel;
- @Nullable private final Intent mIntent;
- @Nullable private final OnClickListener mOnClickListener;
+ @NonNull private final List<Drawable> mIcons;
+ @NonNull private final List<String> mLabels;
+ @NonNull private final List<Intent> mIntents;
+ @NonNull private final List<OnClickListener> mOnClickListeners;
@NonNull private final EntityConfidence<String> mEntityConfidence;
@NonNull private final List<String> mEntities;
private int mLogType;
@@ -52,18 +53,21 @@
private TextClassification(
@Nullable String text,
- @Nullable Drawable icon,
- @Nullable String label,
- @Nullable Intent intent,
- @Nullable OnClickListener onClickListener,
+ @NonNull List<Drawable> icons,
+ @NonNull List<String> labels,
+ @NonNull List<Intent> intents,
+ @NonNull List<OnClickListener> onClickListeners,
@NonNull EntityConfidence<String> entityConfidence,
int logType,
@NonNull String versionInfo) {
+ Preconditions.checkArgument(labels.size() == intents.size());
+ Preconditions.checkArgument(icons.size() == intents.size());
+ Preconditions.checkArgument(onClickListeners.size() == intents.size());
mText = text;
- mIcon = icon;
- mLabel = label;
- mIntent = intent;
- mOnClickListener = onClickListener;
+ mIcons = icons;
+ mLabels = labels;
+ mIntents = intents;
+ mOnClickListeners = onClickListeners;
mEntityConfidence = new EntityConfidence<>(entityConfidence);
mEntities = mEntityConfidence.getEntities();
mLogType = logType;
@@ -109,35 +113,106 @@
}
/**
- * Returns an icon that may be rendered on a widget used to act on the classified text.
+ * Returns the number of actions that are available to act on the classified text.
+ * @see #getIntent(int)
+ * @see #getLabel(int)
+ * @see #getIcon(int)
+ * @see #getOnClickListener(int)
+ */
+ @IntRange(from = 0)
+ public int getActionCount() {
+ return mIntents.size();
+ }
+
+ /**
+ * Returns one of the icons that maybe rendered on a widget used to act on the classified text.
+ * @param index Index of the action to get the icon for.
+ * @throws IndexOutOfBoundsException if the specified index is out of range.
+ * @see #getActionCount() for the number of entities available.
+ * @see #getIntent(int)
+ * @see #getLabel(int)
+ * @see #getOnClickListener(int)
+ */
+ @Nullable
+ public Drawable getIcon(int index) {
+ return mIcons.get(index);
+ }
+
+ /**
+ * Returns an icon for the default intent that may be rendered on a widget used to act on the
+ * classified text.
*/
@Nullable
public Drawable getIcon() {
- return mIcon;
+ return mIcons.isEmpty() ? null : mIcons.get(0);
}
/**
- * Returns a label that may be rendered on a widget used to act on the classified text.
+ * Returns one of the labels that may be rendered on a widget used to act on the classified
+ * text.
+ * @param index Index of the action to get the label for.
+ * @throws IndexOutOfBoundsException if the specified index is out of range.
+ * @see #getActionCount()
+ * @see #getIntent(int)
+ * @see #getIcon(int)
+ * @see #getOnClickListener(int)
+ */
+ @Nullable
+ public CharSequence getLabel(int index) {
+ return mLabels.get(index);
+ }
+
+ /**
+ * Returns a label for the default intent that may be rendered on a widget used to act on the
+ * classified text.
*/
@Nullable
public CharSequence getLabel() {
- return mLabel;
+ return mLabels.isEmpty() ? null : mLabels.get(0);
}
/**
- * Returns an intent that may be fired to act on the classified text.
+ * Returns one of the intents that may be fired to act on the classified text.
+ * @param index Index of the action to get the intent for.
+ * @throws IndexOutOfBoundsException if the specified index is out of range.
+ * @see #getActionCount()
+ * @see #getLabel(int)
+ * @see #getIcon(int)
+ * @see #getOnClickListener(int)
+ */
+ @Nullable
+ public Intent getIntent(int index) {
+ return mIntents.get(index);
+ }
+
+ /**
+ * Returns the default intent that may be fired to act on the classified text.
*/
@Nullable
public Intent getIntent() {
- return mIntent;
+ return mIntents.isEmpty() ? null : mIntents.get(0);
}
/**
- * Returns an OnClickListener that may be triggered to act on the classified text.
+ * Returns one of the OnClickListeners that may be triggered to act on the classified text.
+ * @param index Index of the action to get the click listener for.
+ * @throws IndexOutOfBoundsException if the specified index is out of range.
+ * @see #getActionCount()
+ * @see #getIntent(int)
+ * @see #getLabel(int)
+ * @see #getIcon(int)
+ */
+ @Nullable
+ public OnClickListener getOnClickListener(int index) {
+ return mOnClickListeners.get(index);
+ }
+
+ /**
+ * Returns the default OnClickListener that may be triggered to act on the classified text.
*/
@Nullable
public OnClickListener getOnClickListener() {
- return mOnClickListener;
+ return mOnClickListeners.isEmpty() ? null : mOnClickListeners.get(0);
}
/**
@@ -160,8 +235,8 @@
@Override
public String toString() {
return String.format("TextClassification {"
- + "text=%s, entities=%s, label=%s, intent=%s}",
- mText, mEntityConfidence, mLabel, mIntent);
+ + "text=%s, entities=%s, labels=%s, intents=%s}",
+ mText, mEntityConfidence, mLabels, mIntents);
}
/**
@@ -184,10 +259,10 @@
public static final class Builder {
@NonNull private String mText;
- @Nullable private Drawable mIcon;
- @Nullable private String mLabel;
- @Nullable private Intent mIntent;
- @Nullable private OnClickListener mOnClickListener;
+ @NonNull private final List<Drawable> mIcons = new ArrayList<>();
+ @NonNull private final List<String> mLabels = new ArrayList<>();
+ @NonNull private final List<Intent> mIntents = new ArrayList<>();
+ @NonNull private final List<OnClickListener> mOnClickListeners = new ArrayList<>();
@NonNull private final EntityConfidence<String> mEntityConfidence =
new EntityConfidence<>();
private int mLogType;
@@ -216,26 +291,57 @@
}
/**
- * Sets an icon that may be rendered on a widget used to act on the classified text.
+ * Adds an action that may be performed on the classified text. The label and icon are used
+ * for rendering of widgets that offer the intent. Actions should be added in order of
+ * priority and the first one will be treated as the default.
+ */
+ public Builder addAction(
+ Intent intent, @Nullable String label, @Nullable Drawable icon,
+ @Nullable OnClickListener onClickListener) {
+ mIntents.add(intent);
+ mLabels.add(label);
+ mIcons.add(icon);
+ mOnClickListeners.add(onClickListener);
+ return this;
+ }
+
+ /**
+ * Removes all actions.
+ */
+ public Builder clearActions() {
+ mIntents.clear();
+ mOnClickListeners.clear();
+ mLabels.clear();
+ mIcons.clear();
+ return this;
+ }
+
+ /**
+ * Sets the icon for the default action that may be rendered on a widget used to act on the
+ * classified text.
*/
public Builder setIcon(@Nullable Drawable icon) {
- mIcon = icon;
+ ensureDefaultActionAvailable();
+ mIcons.set(0, icon);
return this;
}
/**
- * Sets a label that may be rendered on a widget used to act on the classified text.
+ * Sets the label for the default action that may be rendered on a widget used to act on the
+ * classified text.
*/
public Builder setLabel(@Nullable String label) {
- mLabel = label;
+ ensureDefaultActionAvailable();
+ mLabels.set(0, label);
return this;
}
/**
- * Sets an intent that may be fired to act on the classified text.
+ * Sets the intent for the default action that may be fired to act on the classified text.
*/
public Builder setIntent(@Nullable Intent intent) {
- mIntent = intent;
+ ensureDefaultActionAvailable();
+ mIntents.set(0, intent);
return this;
}
@@ -249,10 +355,12 @@
}
/**
- * Sets an OnClickListener that may be triggered to act on the classified text.
+ * Sets the OnClickListener for the default action that may be triggered to act on the
+ * classified text.
*/
public Builder setOnClickListener(@Nullable OnClickListener onClickListener) {
- mOnClickListener = onClickListener;
+ ensureDefaultActionAvailable();
+ mOnClickListeners.set(0, onClickListener);
return this;
}
@@ -266,11 +374,21 @@
}
/**
+ * Ensures that we have at we have storage for the default action.
+ */
+ private void ensureDefaultActionAvailable() {
+ if (mIntents.isEmpty()) mIntents.add(null);
+ if (mLabels.isEmpty()) mLabels.add(null);
+ if (mIcons.isEmpty()) mIcons.add(null);
+ if (mOnClickListeners.isEmpty()) mOnClickListeners.add(null);
+ }
+
+ /**
* Builds and returns a {@link TextClassification} object.
*/
public TextClassification build() {
return new TextClassification(
- mText, mIcon, mLabel, mIntent, mOnClickListener, mEntityConfidence,
+ mText, mIcons, mLabels, mIntents, mOnClickListeners, mEntityConfidence,
mLogType, mVersionInfo);
}
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 7e93b78..2aa81a2 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -29,6 +29,7 @@
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.provider.Browser;
+import android.provider.ContactsContract;
import android.text.Spannable;
import android.text.TextUtils;
import android.text.method.WordIterator;
@@ -356,7 +357,16 @@
final String type = getHighestScoringType(classifications);
builder.setLogType(IntentFactory.getLogType(type));
- final Intent intent = IntentFactory.create(mContext, type, text.toString());
+ final List<Intent> intents = IntentFactory.create(mContext, type, text.toString());
+ for (Intent intent : intents) {
+ extendClassificationWithIntent(intent, builder);
+ }
+
+ return builder.setVersionInfo(getVersionInfo()).build();
+ }
+
+ /** Extends the classification with the intent if it can be resolved. */
+ private void extendClassificationWithIntent(Intent intent, TextClassification.Builder builder) {
final PackageManager pm;
final ResolveInfo resolveInfo;
if (intent != null) {
@@ -367,30 +377,29 @@
resolveInfo = null;
}
if (resolveInfo != null && resolveInfo.activityInfo != null) {
- builder.setIntent(intent)
- .setOnClickListener(TextClassification.createStartActivityOnClickListener(
- mContext, intent));
-
final String packageName = resolveInfo.activityInfo.packageName;
+ CharSequence label;
+ Drawable icon;
if ("android".equals(packageName)) {
// Requires the chooser to find an activity to handle the intent.
- builder.setLabel(IntentFactory.getLabel(mContext, type));
+ label = IntentFactory.getLabel(mContext, intent);
+ icon = null;
} else {
// A default activity will handle the intent.
intent.setComponent(new ComponentName(packageName, resolveInfo.activityInfo.name));
- Drawable icon = resolveInfo.activityInfo.loadIcon(pm);
+ icon = resolveInfo.activityInfo.loadIcon(pm);
if (icon == null) {
icon = resolveInfo.loadIcon(pm);
}
- builder.setIcon(icon);
- CharSequence label = resolveInfo.activityInfo.loadLabel(pm);
+ label = resolveInfo.activityInfo.loadLabel(pm);
if (label == null) {
label = resolveInfo.loadLabel(pm);
}
- builder.setLabel(label != null ? label.toString() : null);
}
+ builder.addAction(
+ intent, label != null ? label.toString() : null, icon,
+ TextClassification.createStartActivityOnClickListener(mContext, intent));
}
- return builder.setVersionInfo(getVersionInfo()).build();
}
private static int getHintFlags(CharSequence text, int start, int end) {
@@ -477,10 +486,11 @@
if (results.length > 0) {
final String type = getHighestScoringType(results);
if (matches(type, linkMask)) {
- final Intent intent = IntentFactory.create(
+ // For links without disambiguation, we simply use the default intent.
+ final List<Intent> intents = IntentFactory.create(
context, type, text.substring(selectionStart, selectionEnd));
- if (hasActivityHandler(context, intent)) {
- final ClickableSpan span = createSpan(context, intent);
+ if (!intents.isEmpty() && hasActivityHandler(context, intents.get(0))) {
+ final ClickableSpan span = createSpan(context, intents.get(0));
spans.add(new SpanSpec(selectionStart, selectionEnd, span));
}
}
@@ -564,7 +574,7 @@
};
}
- private static boolean hasActivityHandler(Context context, @Nullable Intent intent) {
+ private static boolean hasActivityHandler(Context context, Intent intent) {
if (intent == null) {
return false;
}
@@ -625,20 +635,32 @@
private IntentFactory() {}
- @Nullable
- public static Intent create(Context context, String type, String text) {
+ @NonNull
+ public static List<Intent> create(Context context, String type, String text) {
+ final List<Intent> intents = new ArrayList<>();
type = type.trim().toLowerCase(Locale.ENGLISH);
text = text.trim();
switch (type) {
case TextClassifier.TYPE_EMAIL:
- return new Intent(Intent.ACTION_SENDTO)
- .setData(Uri.parse(String.format("mailto:%s", text)));
+ intents.add(new Intent(Intent.ACTION_SENDTO)
+ .setData(Uri.parse(String.format("mailto:%s", text))));
+ intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT)
+ .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
+ .putExtra(ContactsContract.Intents.Insert.EMAIL, text));
+ break;
case TextClassifier.TYPE_PHONE:
- return new Intent(Intent.ACTION_DIAL)
- .setData(Uri.parse(String.format("tel:%s", text)));
+ intents.add(new Intent(Intent.ACTION_DIAL)
+ .setData(Uri.parse(String.format("tel:%s", text))));
+ intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT)
+ .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
+ .putExtra(ContactsContract.Intents.Insert.PHONE, text));
+ intents.add(new Intent(Intent.ACTION_SENDTO)
+ .setData(Uri.parse(String.format("smsto:%s", text))));
+ break;
case TextClassifier.TYPE_ADDRESS:
- return new Intent(Intent.ACTION_VIEW)
- .setData(Uri.parse(String.format("geo:0,0?q=%s", text)));
+ intents.add(new Intent(Intent.ACTION_VIEW)
+ .setData(Uri.parse(String.format("geo:0,0?q=%s", text))));
+ break;
case TextClassifier.TYPE_URL:
final String httpPrefix = "http://";
final String httpsPrefix = "https://";
@@ -649,25 +671,47 @@
} else {
text = httpPrefix + text;
}
- return new Intent(Intent.ACTION_VIEW, Uri.parse(text))
- .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
- default:
- return null;
+ intents.add(new Intent(Intent.ACTION_VIEW, Uri.parse(text))
+ .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()));
+ break;
}
+ return intents;
}
@Nullable
- public static String getLabel(Context context, String type) {
- type = type.trim().toLowerCase(Locale.ENGLISH);
- switch (type) {
- case TextClassifier.TYPE_EMAIL:
- return context.getString(com.android.internal.R.string.email);
- case TextClassifier.TYPE_PHONE:
+ public static String getLabel(Context context, @Nullable Intent intent) {
+ if (intent == null || intent.getAction() == null) {
+ return null;
+ }
+ switch (intent.getAction()) {
+ case Intent.ACTION_DIAL:
return context.getString(com.android.internal.R.string.dial);
- case TextClassifier.TYPE_ADDRESS:
- return context.getString(com.android.internal.R.string.map);
- case TextClassifier.TYPE_URL:
- return context.getString(com.android.internal.R.string.browse);
+ case Intent.ACTION_SENDTO:
+ switch (intent.getScheme()) {
+ case "mailto":
+ return context.getString(com.android.internal.R.string.email);
+ case "smsto":
+ return context.getString(com.android.internal.R.string.sms);
+ default:
+ return null;
+ }
+ case Intent.ACTION_INSERT_OR_EDIT:
+ switch (intent.getDataString()) {
+ case ContactsContract.Contacts.CONTENT_ITEM_TYPE:
+ return context.getString(com.android.internal.R.string.add_contact);
+ default:
+ return null;
+ }
+ case Intent.ACTION_VIEW:
+ switch (intent.getScheme()) {
+ case "geo":
+ return context.getString(com.android.internal.R.string.map);
+ case "http": // fall through
+ case "https":
+ return context.getString(com.android.internal.R.string.browse);
+ default:
+ return null;
+ }
default:
return null;
}
diff --git a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
index 8d88ba6..83af19b 100644
--- a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
+++ b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
@@ -581,6 +581,7 @@
case ActionType.SMART_SHARE: // fall through
case ActionType.DRAG: // fall through
case ActionType.ABANDON: // fall through
+ case ActionType.OTHER: // fall through
return true;
default:
return false;
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index b839420..fc76029 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -16,13 +16,14 @@
package android.webkit;
+import android.annotation.Nullable;
+
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
-
/**
* Manages the HTTP cache used by an application's {@link WebView} instances.
* @deprecated Access to the HTTP cache will be removed in a future release.
@@ -233,6 +234,7 @@
* @deprecated This method no longer has any effect and always returns {@code null}.
*/
@Deprecated
+ @Nullable
public static File getCacheFileBaseDir() {
return null;
}
@@ -287,6 +289,7 @@
* @deprecated This method no longer has any effect and always returns {@code null}.
*/
@Deprecated
+ @Nullable
public static CacheResult getCacheFile(String url,
Map<String, String> headers) {
return null;
diff --git a/core/java/android/webkit/ClientCertRequest.java b/core/java/android/webkit/ClientCertRequest.java
index de17534..0fc47f1 100644
--- a/core/java/android/webkit/ClientCertRequest.java
+++ b/core/java/android/webkit/ClientCertRequest.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.Nullable;
+
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
@@ -42,14 +44,16 @@
public ClientCertRequest() { }
/**
- * Returns the acceptable types of asymmetric keys (can be {@code null}).
+ * Returns the acceptable types of asymmetric keys.
*/
+ @Nullable
public abstract String[] getKeyTypes();
/**
* Returns the acceptable certificate issuers for the certificate
- * matching the private key (can be {@code null}).
+ * matching the private key.
*/
+ @Nullable
public abstract Principal[] getPrincipals();
/**
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 8989293..ae6a2fd 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.WebAddress;
@@ -116,7 +117,8 @@
* HTTP response header
* @param callback a callback to be executed when the cookie has been set
*/
- public abstract void setCookie(String url, String value, ValueCallback<Boolean> callback);
+ public abstract void setCookie(String url, String value, @Nullable ValueCallback<Boolean>
+ callback);
/**
* Gets the cookies for the given URL.
@@ -175,7 +177,7 @@
* method from a thread without a Looper.
* @param callback a callback which is executed when the session cookies have been removed
*/
- public abstract void removeSessionCookies(ValueCallback<Boolean> callback);
+ public abstract void removeSessionCookies(@Nullable ValueCallback<Boolean> callback);
/**
* Removes all cookies.
@@ -197,7 +199,7 @@
* method from a thread without a Looper.
* @param callback a callback which is executed when the cookies have been removed
*/
- public abstract void removeAllCookies(ValueCallback<Boolean> callback);
+ public abstract void removeAllCookies(@Nullable ValueCallback<Boolean> callback);
/**
* Gets whether there are stored cookies.
diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java
index 71f85d7..e011d51 100644
--- a/core/java/android/webkit/FindActionModeCallback.java
+++ b/core/java/android/webkit/FindActionModeCallback.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.res.Resources;
@@ -69,7 +70,7 @@
mActionMode.finish();
}
- /*
+ /**
* Place text in the text field so it can be searched for. Need to press
* the find next or find previous button to find all of the matches.
*/
@@ -87,10 +88,12 @@
mMatchesFound = false;
}
- /*
- * Set the WebView to search. Must be non null.
+ /**
+ * Set the WebView to search.
+ *
+ * @param webView an implementation of WebView
*/
- public void setWebView(WebView webView) {
+ public void setWebView(@NonNull WebView webView) {
if (null == webView) {
throw new AssertionError("WebView supplied to "
+ "FindActionModeCallback cannot be null");
@@ -107,7 +110,7 @@
}
}
- /*
+ /**
* Move the highlight to the next match.
* @param next If {@code true}, find the next match further down in the document.
* If {@code false}, find the previous match, up in the document.
@@ -130,7 +133,7 @@
updateMatchesString();
}
- /*
+ /**
* Highlight all the instances of the string from mEditText in mWebView.
*/
public void findAll() {
@@ -169,7 +172,7 @@
}
}
- /*
+ /**
* Update the string which tells the user how many matches were found, and
* which match is currently highlighted.
*/
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index e172c02..39874e8 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.Nullable;
import android.text.TextUtils;
import libcore.net.MimeUtils;
@@ -86,6 +87,7 @@
* @param extension A file extension without the leading '.'
* @return The MIME type for the given extension or {@code null} iff there is none.
*/
+ @Nullable
public String getMimeTypeFromExtension(String extension) {
return MimeUtils.guessMimeTypeFromExtension(extension);
}
@@ -111,6 +113,7 @@
* @param mimeType A MIME type (i.e. text/plain)
* @return The extension for the given MIME type or {@code null} iff there is none.
*/
+ @Nullable
public String getExtensionFromMimeType(String mimeType) {
return MimeUtils.guessExtensionFromMimeType(mimeType);
}
@@ -125,7 +128,7 @@
* @param contentDisposition Content-disposition header given by the server.
* @return The MIME type that should be used for this data.
*/
- /* package */ String remapGenericMimeType(String mimeType, String url,
+ /* package */ String remapGenericMimeType(@Nullable String mimeType, String url,
String contentDisposition) {
// If we have one of "generic" MIME types, try to deduce
// the right MIME type from the file extension (if any):
diff --git a/core/java/android/webkit/Plugin.java b/core/java/android/webkit/Plugin.java
index 072e02a..29a5edc 100644
--- a/core/java/android/webkit/Plugin.java
+++ b/core/java/android/webkit/Plugin.java
@@ -16,12 +16,12 @@
package android.webkit;
-import com.android.internal.R;
-
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
+import com.android.internal.R;
+
/**
* Represents a plugin (Java equivalent of the PluginPackageAndroid
* C++ class in libs/WebKitLib/WebKit/WebCore/plugins/android/)
@@ -32,13 +32,13 @@
*/
@Deprecated
public class Plugin {
- /*
+ /**
* @hide
* @deprecated This interface was intended to be used by Gears. Since Gears was
* deprecated, so is this class.
*/
public interface PreferencesClickHandler {
- /*
+ /**
* @hide
* @deprecated This interface was intended to be used by Gears. Since Gears was
* deprecated, so is this class.
diff --git a/core/java/android/webkit/ServiceWorkerClient.java b/core/java/android/webkit/ServiceWorkerClient.java
index b4964fd..d6e8f36 100644
--- a/core/java/android/webkit/ServiceWorkerClient.java
+++ b/core/java/android/webkit/ServiceWorkerClient.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.Nullable;
+
/**
* Base class for clients to capture Service Worker related callbacks,
* see {@link ServiceWorkerController} for usage example.
@@ -37,6 +39,7 @@
* resource itself.
* @see WebViewClient#shouldInterceptRequest(WebView, WebResourceRequest)
*/
+ @Nullable
public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
return null;
}
diff --git a/core/java/android/webkit/TokenBindingService.java b/core/java/android/webkit/TokenBindingService.java
index 43565c2..b37e1b8 100644
--- a/core/java/android/webkit/TokenBindingService.java
+++ b/core/java/android/webkit/TokenBindingService.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.Uri;
@@ -84,31 +86,30 @@
* The user can pass {@code null} if any algorithm is acceptable.
*
* @param origin The origin for the server.
- * @param algorithm The list of algorithms. Can be {@code null}. An
- * IllegalArgumentException is thrown if array is empty.
+ * @param algorithm The list of algorithms. An IllegalArgumentException is thrown if array is
+ * empty.
* @param callback The callback that will be called when key is available.
- * Cannot be {@code null}.
*/
public abstract void getKey(Uri origin,
- String[] algorithm,
- ValueCallback<TokenBindingKey> callback);
+ @Nullable String[] algorithm,
+ @NonNull ValueCallback<TokenBindingKey> callback);
/**
* Deletes specified key (for use when associated cookie is cleared).
*
* @param origin The origin of the server.
* @param callback The callback that will be called when key is deleted. The
* callback parameter (Boolean) will indicate if operation is
- * successful or if failed. The callback can be {@code null}.
+ * successful or if failed.
*/
public abstract void deleteKey(Uri origin,
- ValueCallback<Boolean> callback);
+ @Nullable ValueCallback<Boolean> callback);
/**
* Deletes all the keys (for use when cookies are cleared).
*
* @param callback The callback that will be called when keys are deleted.
* The callback parameter (Boolean) will indicate if operation is
- * successful or if failed. The callback can be {@code null}.
+ * successful or if failed.
*/
- public abstract void deleteAllKeys(ValueCallback<Boolean> callback);
+ public abstract void deleteAllKeys(@Nullable ValueCallback<Boolean> callback);
}
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index c8bfb77..c2f121a 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.Nullable;
import android.net.ParseException;
import android.net.Uri;
import android.net.WebAddress;
@@ -300,8 +301,8 @@
*/
public static final String guessFileName(
String url,
- String contentDisposition,
- String mimeType) {
+ @Nullable String contentDisposition,
+ @Nullable String mimeType) {
String filename = null;
String extension = null;
@@ -388,7 +389,7 @@
Pattern.compile("attachment;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\1\\s*$",
Pattern.CASE_INSENSITIVE);
- /*
+ /**
* Parse the Content-Disposition HTTP Header. The format of the header
* is defined here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html
* This header provides a filename for content that is going to be
diff --git a/core/java/android/webkit/UrlInterceptHandler.java b/core/java/android/webkit/UrlInterceptHandler.java
index aa5c6dc..0a6e51f 100644
--- a/core/java/android/webkit/UrlInterceptHandler.java
+++ b/core/java/android/webkit/UrlInterceptHandler.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.Nullable;
import android.webkit.CacheManager.CacheResult;
import android.webkit.PluginData;
@@ -35,14 +36,15 @@
* not interested.
*
* @param url URL string.
- * @param headers The headers associated with the request. May be {@code null}.
+ * @param headers The headers associated with the request.
* @return The CacheResult containing the surrogate response.
*
* @hide
* @deprecated Do not use, this interface is deprecated.
*/
@Deprecated
- public CacheResult service(String url, Map<String, String> headers);
+ @Nullable
+ CacheResult service(String url, @Nullable Map<String, String> headers);
/**
* Given an URL, returns the PluginData which contains the
@@ -50,12 +52,13 @@
* not interested.
*
* @param url URL string.
- * @param headers The headers associated with the request. May be {@code null}.
+ * @param headers The headers associated with the request.
* @return The PluginData containing the surrogate response.
*
* @hide
* @deprecated Do not use, this interface is deprecated.
*/
@Deprecated
- public PluginData getPluginData(String url, Map<String, String> headers);
+ @Nullable
+ PluginData getPluginData(String url, @Nullable Map<String, String> headers);
}
diff --git a/core/java/android/webkit/UrlInterceptRegistry.java b/core/java/android/webkit/UrlInterceptRegistry.java
index 67af2ad..700d6d9 100644
--- a/core/java/android/webkit/UrlInterceptRegistry.java
+++ b/core/java/android/webkit/UrlInterceptRegistry.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.Nullable;
import android.webkit.CacheManager.CacheResult;
import android.webkit.PluginData;
import android.webkit.UrlInterceptHandler;
@@ -121,6 +122,7 @@
* deprecated, so is this class.
*/
@Deprecated
+ @Nullable
public static synchronized CacheResult getSurrogate(
String url, Map<String, String> headers) {
if (urlInterceptDisabled()) {
@@ -149,6 +151,7 @@
* deprecated, so is this class.
*/
@Deprecated
+ @Nullable
public static synchronized PluginData getPluginData(
String url, Map<String, String> headers) {
if (urlInterceptDisabled()) {
diff --git a/core/java/android/webkit/WebBackForwardList.java b/core/java/android/webkit/WebBackForwardList.java
index 3349b06..0c34e3c 100644
--- a/core/java/android/webkit/WebBackForwardList.java
+++ b/core/java/android/webkit/WebBackForwardList.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.Nullable;
+
import java.io.Serializable;
/**
@@ -29,6 +31,7 @@
* empty.
* @return The current history item.
*/
+ @Nullable
public abstract WebHistoryItem getCurrentItem();
/**
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 742daa9..4aa1c4a 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -383,6 +384,7 @@
* @return Bitmap The image to use as a default poster, or {@code null} if no such image is
* available.
*/
+ @Nullable
public Bitmap getDefaultVideoPoster() {
return null;
}
@@ -394,6 +396,7 @@
*
* @return View The View to be displayed whilst the video is loading.
*/
+ @Nullable
public View getVideoLoadingProgressView() {
return null;
}
@@ -452,6 +455,7 @@
* @return the Uris of selected file(s) or {@code null} if the resultCode indicates
* activity canceled or any other error.
*/
+ @Nullable
public static Uri[] parseResult(int resultCode, Intent data) {
return WebViewFactory.getProvider().getStatics().parseFileChooserResult(resultCode, data);
}
@@ -477,14 +481,16 @@
public abstract boolean isCaptureEnabled();
/**
- * Returns the title to use for this file selector, or null. If {@code null} a default
- * title should be used.
+ * Returns the title to use for this file selector. If {@code null} a default title should
+ * be used.
*/
+ @Nullable
public abstract CharSequence getTitle();
/**
* The file name of a default selection if specified, or {@code null}.
*/
+ @Nullable
public abstract String getFilenameHint();
/**
diff --git a/core/java/android/webkit/WebHistoryItem.java b/core/java/android/webkit/WebHistoryItem.java
index 1591833..74db039 100644
--- a/core/java/android/webkit/WebHistoryItem.java
+++ b/core/java/android/webkit/WebHistoryItem.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.graphics.Bitmap;
@@ -70,6 +71,7 @@
* Note: The VM ensures 32-bit atomic read/write operations so we don't have
* to synchronize this method.
*/
+ @Nullable
public abstract Bitmap getFavicon();
/**
diff --git a/core/java/android/webkit/WebMessage.java b/core/java/android/webkit/WebMessage.java
index 7fe66dc8..bfc00e7 100644
--- a/core/java/android/webkit/WebMessage.java
+++ b/core/java/android/webkit/WebMessage.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.Nullable;
+
/**
* The Java representation of the HTML5 PostMessage event. See
* https://html.spec.whatwg.org/multipage/comms.html#the-messageevent-interfaces
@@ -56,6 +58,7 @@
* Returns the ports that are sent with the message, or {@code null} if no port
* is sent.
*/
+ @Nullable
public WebMessagePort[] getPorts() {
return mPorts;
}
diff --git a/core/java/android/webkit/WebResourceResponse.java b/core/java/android/webkit/WebResourceResponse.java
index 80c43c1..7bc7b07 100644
--- a/core/java/android/webkit/WebResourceResponse.java
+++ b/core/java/android/webkit/WebResourceResponse.java
@@ -16,12 +16,13 @@
package android.webkit;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.util.Map;
-import android.annotation.SystemApi;
-
/**
* Encapsulates a resource response. Applications can return an instance of this
* class from {@link WebViewClient#shouldInterceptRequest} to provide a custom
@@ -63,15 +64,15 @@
* @param encoding the resource response's encoding
* @param statusCode the status code needs to be in the ranges [100, 299], [400, 599].
* Causing a redirect by specifying a 3xx code is not supported.
- * @param reasonPhrase the phrase describing the status code, for example "OK". Must be non-null
- * and not empty.
+ * @param reasonPhrase the phrase describing the status code, for example "OK". Must be
+ * non-empty.
* @param responseHeaders the resource response's headers represented as a mapping of header
* name -> header value.
* @param data the input stream that provides the resource response's data. Must not be a
* StringBufferInputStream.
*/
public WebResourceResponse(String mimeType, String encoding, int statusCode,
- String reasonPhrase, Map<String, String> responseHeaders, InputStream data) {
+ @NonNull String reasonPhrase, Map<String, String> responseHeaders, InputStream data) {
this(mimeType, encoding, data);
setStatusCodeAndReasonPhrase(statusCode, reasonPhrase);
setResponseHeaders(responseHeaders);
@@ -121,10 +122,10 @@
*
* @param statusCode the status code needs to be in the ranges [100, 299], [400, 599].
* Causing a redirect by specifying a 3xx code is not supported.
- * @param reasonPhrase the phrase describing the status code, for example "OK". Must be non-null
- * and not empty.
+ * @param reasonPhrase the phrase describing the status code, for example "OK". Must be
+ * non-empty.
*/
- public void setStatusCodeAndReasonPhrase(int statusCode, String reasonPhrase) {
+ public void setStatusCodeAndReasonPhrase(int statusCode, @NonNull String reasonPhrase) {
checkImmutable();
if (statusCode < 100)
throw new IllegalArgumentException("statusCode can't be less than 100.");
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 22d8561..203de9c 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -17,6 +17,7 @@
package android.webkit;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
@@ -1238,7 +1239,7 @@
*
* @param ua new user-agent string
*/
- public abstract void setUserAgentString(String ua);
+ public abstract void setUserAgentString(@Nullable String ua);
/**
* Gets the WebView's user-agent string.
diff --git a/core/java/android/webkit/WebSyncManager.java b/core/java/android/webkit/WebSyncManager.java
index 801be12..03b94e7 100644
--- a/core/java/android/webkit/WebSyncManager.java
+++ b/core/java/android/webkit/WebSyncManager.java
@@ -18,7 +18,7 @@
import android.content.Context;
-/*
+/**
* @deprecated The WebSyncManager no longer does anything.
*/
@Deprecated
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 3a4bfd6..dfc81b2 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -438,7 +438,7 @@
* @deprecated Deprecated due to internal changes.
*/
@Deprecated
- public void onNewPicture(WebView view, Picture picture);
+ void onNewPicture(WebView view, @Nullable Picture picture);
}
public static class HitTestResult {
@@ -529,6 +529,7 @@
*
* @return additional type-dependant information about the result
*/
+ @Nullable
public String getExtra() {
return mExtra;
}
@@ -717,6 +718,7 @@
*
* @return the SSL certificate for the main top-level page
*/
+ @Nullable
public SslCertificate getCertificate() {
checkThread();
return mProvider.getCertificate();
@@ -785,6 +787,7 @@
* @deprecated Use {@link WebViewDatabase#getHttpAuthUsernamePassword} instead
*/
@Deprecated
+ @Nullable
public String[] getHttpAuthUsernamePassword(String host, String realm) {
checkThread();
return mProvider.getHttpAuthUsernamePassword(host, realm);
@@ -853,9 +856,10 @@
* called.
*
* @param outState the Bundle to store this WebView's state
- * @return the same copy of the back/forward list used to save the state. If
- * saveState fails, the returned list will be {@code null}.
+ * @return the same copy of the back/forward list used to save the state, {@code null} if the
+ * method fails.
*/
+ @Nullable
public WebBackForwardList saveState(Bundle outState) {
checkThread();
return mProvider.saveState(outState);
@@ -906,6 +910,7 @@
* @param inState the incoming Bundle of state
* @return the restored back/forward list or {@code null} if restoreState failed
*/
+ @Nullable
public WebBackForwardList restoreState(Bundle inState) {
checkThread();
return mProvider.restoreState(inState);
@@ -985,10 +990,11 @@
* always overrides that specified in the HTML or XML document itself.
*
* @param data a String of data in the given encoding
- * @param mimeType the MIME type of the data, e.g. 'text/html'
+ * @param mimeType the MIMEType of the data, e.g. 'text/html'. If {@code null},
+ * defaults to 'text/html'.
* @param encoding the encoding of the data
*/
- public void loadData(String data, String mimeType, String encoding) {
+ public void loadData(String data, @Nullable String mimeType, @Nullable String encoding) {
checkThread();
mProvider.loadData(data, mimeType, encoding);
}
@@ -1022,8 +1028,8 @@
* @param historyUrl the URL to use as the history entry. If {@code null} defaults
* to 'about:blank'. If non-null, this must be a valid URL.
*/
- public void loadDataWithBaseURL(String baseUrl, String data,
- String mimeType, String encoding, String historyUrl) {
+ public void loadDataWithBaseURL(@Nullable String baseUrl, String data,
+ @Nullable String mimeType, @Nullable String encoding, @Nullable String historyUrl) {
checkThread();
mProvider.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
@@ -1045,7 +1051,7 @@
* completes with the result of the execution (if any).
* May be {@code null} if no notification of the result is required.
*/
- public void evaluateJavascript(String script, ValueCallback<String> resultCallback) {
+ public void evaluateJavascript(String script, @Nullable ValueCallback<String> resultCallback) {
checkThread();
mProvider.evaluateJavaScript(script, resultCallback);
}
@@ -1072,7 +1078,8 @@
* under which the file was saved, or {@code null} if saving the
* file failed.
*/
- public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
+ public void saveWebArchive(String basename, boolean autoname, @Nullable ValueCallback<String>
+ callback) {
checkThread();
mProvider.saveWebArchive(basename, autoname, callback);
}
@@ -1395,7 +1402,7 @@
* returns the anchor's href attribute. "title" returns the
* anchor's text. "src" returns the image's src attribute.
*/
- public void requestFocusNodeHref(Message hrefMsg) {
+ public void requestFocusNodeHref(@Nullable Message hrefMsg) {
checkThread();
mProvider.requestFocusNodeHref(hrefMsg);
}
@@ -1615,10 +1622,9 @@
* shared by all the WebViews that are created by the embedder application.
*
* @param onCleared A runnable to be invoked when client certs are cleared.
- * The embedder can pass {@code null} if not interested in the
- * callback. The runnable will be called in UI thread.
+ * The runnable will be called in UI thread.
*/
- public static void clearClientCertPreferences(Runnable onCleared) {
+ public static void clearClientCertPreferences(@Nullable Runnable onCleared) {
getFactory().getStatics().clearClientCertPreferences(onCleared);
}
@@ -1640,7 +1646,8 @@
* @param callback will be called on the UI thread with {@code true} if initialization is
* successful, {@code false} otherwise.
*/
- public static void startSafeBrowsing(Context context, ValueCallback<Boolean> callback) {
+ public static void startSafeBrowsing(Context context,
+ @Nullable ValueCallback<Boolean> callback) {
getFactory().getStatics().initSafeBrowsing(context, callback);
}
@@ -1764,7 +1771,7 @@
* provides a more robust solution.
*/
@Deprecated
- public boolean showFindDialog(String text, boolean showIme) {
+ public boolean showFindDialog(@Nullable String text, boolean showIme) {
checkThread();
return mProvider.showFindDialog(text, showIme);
}
@@ -1791,6 +1798,7 @@
* @param addr the string to search for addresses
* @return the address, or if no address is found, {@code null}
*/
+ @Nullable
public static String findAddress(String addr) {
// TODO: Rewrite this in Java so it is not needed to start up chromium
// Could also be deprecated
@@ -1891,6 +1899,7 @@
* @return the WebChromeClient, or {@code null} if not yet set
* @see #setWebChromeClient
*/
+ @Nullable
public WebChromeClient getWebChromeClient() {
checkThread();
return mProvider.getWebChromeClient();
@@ -1973,7 +1982,7 @@
*
* @param name the name used to expose the object in JavaScript
*/
- public void removeJavascriptInterface(String name) {
+ public void removeJavascriptInterface(@NonNull String name) {
checkThread();
mProvider.removeJavascriptInterface(name);
}
@@ -2956,14 +2965,19 @@
* next time the app starts and loads WebView it will use the new WebView package instead.
* @return the current WebView package, or {@code null} if there is none.
*/
+ @Nullable
public static PackageInfo getCurrentWebViewPackage() {
PackageInfo webviewPackage = WebViewFactory.getLoadedPackageInfo();
if (webviewPackage != null) {
return webviewPackage;
}
+ IWebViewUpdateService service = WebViewFactory.getUpdateService();
+ if (service == null) {
+ return null;
+ }
try {
- return WebViewFactory.getUpdateService().getCurrentWebViewPackage();
+ return service.getCurrentWebViewPackage();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index af7026d..c5b64eb 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -17,6 +17,7 @@
package android.webkit;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.os.Message;
@@ -167,6 +168,7 @@
* shouldInterceptRequest(WebView, WebResourceRequest)} instead.
*/
@Deprecated
+ @Nullable
public WebResourceResponse shouldInterceptRequest(WebView view,
String url) {
return null;
@@ -191,6 +193,7 @@
* response information or {@code null} if the WebView should load the
* resource itself.
*/
+ @Nullable
public WebResourceResponse shouldInterceptRequest(WebView view,
WebResourceRequest request) {
return shouldInterceptRequest(view, request.getUrl().toString());
@@ -496,7 +499,7 @@
* @param args Authenticator specific arguments used to log in the user.
*/
public void onReceivedLoginRequest(WebView view, String realm,
- String account, String args) {
+ @Nullable String account, String args) {
}
/**
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index de75d5d0..f6166c5 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.Nullable;
import android.content.Context;
/**
@@ -135,6 +136,7 @@
* @see #hasHttpAuthUsernamePassword
* @see #clearHttpAuthUsernamePassword
*/
+ @Nullable
public abstract String[] getHttpAuthUsernamePassword(String host, String realm);
/**
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 994512f..95cb454 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -51,9 +51,6 @@
private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
- private static final String NULL_WEBVIEW_FACTORY =
- "com.android.webview.nullwebview.NullWebViewFactoryProvider";
-
public static final String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY =
"persist.sys.webview.vmsize";
@@ -66,6 +63,7 @@
private static WebViewFactoryProvider sProviderInstance;
private static final Object sProviderLock = new Object();
private static PackageInfo sPackageInfo;
+ private static Boolean sWebViewSupported;
// Error codes for loadWebViewNativeLibraryFromPackage
public static final int LIBLOAD_SUCCESS = 0;
@@ -105,6 +103,16 @@
public MissingWebViewPackageException(Exception e) { super(e); }
}
+ private static boolean isWebViewSupported() {
+ // No lock; this is a benign race as Boolean's state is final and the PackageManager call
+ // will always return the same value.
+ if (sWebViewSupported == null) {
+ sWebViewSupported = AppGlobals.getInitialApplication().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_WEBVIEW);
+ }
+ return sWebViewSupported;
+ }
+
/**
* @hide
*/
@@ -135,6 +143,10 @@
*/
public static int loadWebViewNativeLibraryFromPackage(String packageName,
ClassLoader clazzLoader) {
+ if (!isWebViewSupported()) {
+ return LIBLOAD_WRONG_PACKAGE_NAME;
+ }
+
WebViewProviderResponse response = null;
try {
response = getUpdateService().waitForAndGetProvider();
@@ -188,6 +200,11 @@
"For security reasons, WebView is not allowed in privileged processes");
}
+ if (!isWebViewSupported()) {
+ // Device doesn't support WebView; don't try to load it, just throw.
+ throw new UnsupportedOperationException();
+ }
+
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
try {
@@ -410,15 +427,6 @@
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (MissingWebViewPackageException e) {
- // If the package doesn't exist, then try loading the null WebView instead.
- // If that succeeds, then this is a device without WebView support; if it fails then
- // swallow the failure, complain that the real WebView is missing and rethrow the
- // original exception.
- try {
- return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
- } catch (ClassNotFoundException e2) {
- // Ignore.
- }
Log.e(LOGTAG, "Chromium WebView package does not exist", e);
throw new AndroidRuntimeException(e);
}
@@ -446,13 +454,13 @@
// waiting on relro creation.
if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");
- WebViewLibraryLoader.createRelroFile(false /* is64Bit */, nativeLibraryPaths);
+ WebViewLibraryLoader.createRelroFile(false /* is64Bit */, nativeLibraryPaths[0]);
numRelros++;
}
if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");
- WebViewLibraryLoader.createRelroFile(true /* is64Bit */, nativeLibraryPaths);
+ WebViewLibraryLoader.createRelroFile(true /* is64Bit */, nativeLibraryPaths[1]);
numRelros++;
}
return numRelros;
@@ -483,6 +491,15 @@
/** @hide */
public static IWebViewUpdateService getUpdateService() {
+ if (isWebViewSupported()) {
+ return getUpdateServiceUnchecked();
+ } else {
+ return null;
+ }
+ }
+
+ /** @hide */
+ static IWebViewUpdateService getUpdateServiceUnchecked() {
return IWebViewUpdateService.Stub.asInterface(
ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
}
diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java
index 6f9e8ec..fa1a390 100644
--- a/core/java/android/webkit/WebViewLibraryLoader.java
+++ b/core/java/android/webkit/WebViewLibraryLoader.java
@@ -62,25 +62,23 @@
boolean result = false;
boolean is64Bit = VMRuntime.getRuntime().is64Bit();
try {
- if (args.length != 2 || args[0] == null || args[1] == null) {
+ if (args.length != 1 || args[0] == null) {
Log.e(LOGTAG, "Invalid RelroFileCreator args: " + Arrays.toString(args));
return;
}
- Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), "
- + " 32-bit lib: " + args[0] + ", 64-bit lib: " + args[1]);
+ Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), lib: " + args[0]);
if (!sAddressSpaceReserved) {
Log.e(LOGTAG, "can't create relro file; address space not reserved");
return;
}
- result = nativeCreateRelroFile(args[0] /* path32 */,
- args[1] /* path64 */,
- CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
- CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
+ result = nativeCreateRelroFile(args[0] /* path */,
+ is64Bit ? CHROMIUM_WEBVIEW_NATIVE_RELRO_64 :
+ CHROMIUM_WEBVIEW_NATIVE_RELRO_32);
if (result && DEBUG) Log.v(LOGTAG, "created relro file");
} finally {
// We must do our best to always notify the update service, even if something fails.
try {
- WebViewFactory.getUpdateService().notifyRelroCreationCompleted();
+ WebViewFactory.getUpdateServiceUnchecked().notifyRelroCreationCompleted();
} catch (RemoteException e) {
Log.e(LOGTAG, "error notifying update service", e);
}
@@ -96,7 +94,7 @@
/**
* Create a single relro file by invoking an isolated process that to do the actual work.
*/
- static void createRelroFile(final boolean is64Bit, String[] nativeLibraryPaths) {
+ static void createRelroFile(final boolean is64Bit, String nativeLibraryPath) {
final String abi =
is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
@@ -114,13 +112,12 @@
};
try {
- if (nativeLibraryPaths == null
- || nativeLibraryPaths[0] == null || nativeLibraryPaths[1] == null) {
+ if (nativeLibraryPath == null) {
throw new IllegalArgumentException(
"Native library paths to the WebView RelRo process must not be null!");
}
int pid = LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess(
- RelroFileCreator.class.getName(), nativeLibraryPaths,
+ RelroFileCreator.class.getName(), new String[] { nativeLibraryPath },
"WebViewLoader-" + abi, abi, Process.SHARED_RELRO_UID, crashHandler);
if (pid <= 0) throw new Exception("Failed to start the relro file creator process");
} catch (Throwable t) {
@@ -217,8 +214,9 @@
final String libraryFileName =
WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo);
- int result = nativeLoadWithRelroFile(libraryFileName, CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
- CHROMIUM_WEBVIEW_NATIVE_RELRO_64, clazzLoader);
+ String relroPath = VMRuntime.getRuntime().is64Bit() ? CHROMIUM_WEBVIEW_NATIVE_RELRO_64 :
+ CHROMIUM_WEBVIEW_NATIVE_RELRO_32;
+ int result = nativeLoadWithRelroFile(libraryFileName, relroPath, clazzLoader);
if (result != WebViewFactory.LIBLOAD_SUCCESS) {
Log.w(LOGTAG, "failed to load with relro file, proceeding without");
} else if (DEBUG) {
@@ -313,8 +311,6 @@
}
static native boolean nativeReserveAddressSpace(long addressSpaceToReserve);
- static native boolean nativeCreateRelroFile(String lib32, String lib64,
- String relro32, String relro64);
- static native int nativeLoadWithRelroFile(String lib, String relro32, String relro64,
- ClassLoader clazzLoader);
+ static native boolean nativeCreateRelroFile(String lib, String relro);
+ static native int nativeLoadWithRelroFile(String lib, String relro, ClassLoader clazzLoader);
}
diff --git a/core/java/android/webkit/WebViewUpdateService.java b/core/java/android/webkit/WebViewUpdateService.java
index 2f7d685..629891c 100644
--- a/core/java/android/webkit/WebViewUpdateService.java
+++ b/core/java/android/webkit/WebViewUpdateService.java
@@ -31,8 +31,12 @@
* Fetch all packages that could potentially implement WebView.
*/
public static WebViewProviderInfo[] getAllWebViewPackages() {
+ IWebViewUpdateService service = getUpdateService();
+ if (service == null) {
+ return new WebViewProviderInfo[0];
+ }
try {
- return getUpdateService().getAllWebViewPackages();
+ return service.getAllWebViewPackages();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -42,8 +46,12 @@
* Fetch all packages that could potentially implement WebView and are currently valid.
*/
public static WebViewProviderInfo[] getValidWebViewPackages() {
+ IWebViewUpdateService service = getUpdateService();
+ if (service == null) {
+ return new WebViewProviderInfo[0];
+ }
try {
- return getUpdateService().getValidWebViewPackages();
+ return service.getValidWebViewPackages();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -53,8 +61,12 @@
* Used by DevelopmentSetting to get the name of the WebView provider currently in use.
*/
public static String getCurrentWebViewPackageName() {
+ IWebViewUpdateService service = getUpdateService();
+ if (service == null) {
+ return null;
+ }
try {
- return getUpdateService().getCurrentWebViewPackageName();
+ return service.getCurrentWebViewPackageName();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 0f61724..afd1188 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -119,6 +119,7 @@
import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.EditableInputConnection;
+import com.android.internal.widget.Magnifier;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -138,6 +139,9 @@
public class Editor {
private static final String TAG = "Editor";
private static final boolean DEBUG_UNDO = false;
+ // Specifies whether to use or not the magnifier when pressing the insertion or selection
+ // handles.
+ private static final boolean FLAG_USE_MAGNIFIER = true;
static final int BLINK = 500;
private static final int DRAG_SHADOW_MAX_TEXT_LENGTH = 20;
@@ -161,6 +165,17 @@
private static final int MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT = 11;
private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100;
+ private static final float MAGNIFIER_ZOOM = 1.5f;
+ @IntDef({MagnifierHandleTrigger.SELECTION_START,
+ MagnifierHandleTrigger.SELECTION_END,
+ MagnifierHandleTrigger.INSERTION})
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface MagnifierHandleTrigger {
+ int INSERTION = 0;
+ int SELECTION_START = 1;
+ int SELECTION_END = 2;
+ }
+
// Each Editor manages its own undo stack.
private final UndoManager mUndoManager = new UndoManager();
private UndoOwner mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this);
@@ -179,6 +194,8 @@
private final boolean mHapticTextHandleEnabled;
+ private final Magnifier mMagnifier;
+
// Used to highlight a word when it is corrected by the IME
private CorrectionHighlighter mCorrectionHighlighter;
@@ -250,7 +267,7 @@
SuggestionRangeSpan mSuggestionRangeSpan;
private Runnable mShowSuggestionRunnable;
- Drawable mCursorDrawable = null;
+ Drawable mDrawableForCursor = null;
private Drawable mSelectHandleLeft;
private Drawable mSelectHandleRight;
@@ -325,6 +342,8 @@
mProcessTextIntentActionsHandler = new ProcessTextIntentActionsHandler(this);
mHapticTextHandleEnabled = mTextView.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_enableHapticTextHandle);
+
+ mMagnifier = FLAG_USE_MAGNIFIER ? new Magnifier(mTextView) : null;
}
ParcelableParcel saveInstanceState() {
@@ -1678,7 +1697,7 @@
mCorrectionHighlighter.draw(canvas, cursorOffsetVertical);
}
- if (highlight != null && selectionStart == selectionEnd && mCursorDrawable != null) {
+ if (highlight != null && selectionStart == selectionEnd && mDrawableForCursor != null) {
drawCursor(canvas, cursorOffsetVertical);
// Rely on the drawable entirely, do not draw the cursor line.
// Has to be done after the IMM related code above which relies on the highlight.
@@ -1873,8 +1892,8 @@
private void drawCursor(Canvas canvas, int cursorOffsetVertical) {
final boolean translate = cursorOffsetVertical != 0;
if (translate) canvas.translate(0, cursorOffsetVertical);
- if (mCursorDrawable != null) {
- mCursorDrawable.draw(canvas);
+ if (mDrawableForCursor != null) {
+ mDrawableForCursor.draw(canvas);
}
if (translate) canvas.translate(0, -cursorOffsetVertical);
}
@@ -1933,7 +1952,7 @@
void updateCursorPosition() {
if (mTextView.mCursorDrawableRes == 0) {
- mCursorDrawable = null;
+ mDrawableForCursor = null;
return;
}
@@ -2314,17 +2333,17 @@
@VisibleForTesting
@Nullable
public Drawable getCursorDrawable() {
- return mCursorDrawable;
+ return mDrawableForCursor;
}
private void updateCursorPosition(int top, int bottom, float horizontal) {
- if (mCursorDrawable == null) {
- mCursorDrawable = mTextView.getContext().getDrawable(
+ if (mDrawableForCursor == null) {
+ mDrawableForCursor = mTextView.getContext().getDrawable(
mTextView.mCursorDrawableRes);
}
- final int left = clampHorizontalPosition(mCursorDrawable, horizontal);
- final int width = mCursorDrawable.getIntrinsicWidth();
- mCursorDrawable.setBounds(left, top - mTempRect.top, left + width,
+ final int left = clampHorizontalPosition(mDrawableForCursor, horizontal);
+ final int width = mDrawableForCursor.getIntrinsicWidth();
+ mDrawableForCursor.setBounds(left, top - mTempRect.top, left + width,
bottom + mTempRect.bottom);
}
@@ -4353,6 +4372,9 @@
protected abstract void updatePosition(float x, float y, boolean fromTouchScreen);
+ @MagnifierHandleTrigger
+ protected abstract int getMagnifierHandleTrigger();
+
protected boolean isAtRtlRun(@NonNull Layout layout, int offset) {
return layout.isRtlCharAt(offset);
}
@@ -4490,6 +4512,53 @@
return 0;
}
+ protected final void showMagnifier() {
+ if (mMagnifier == null) {
+ return;
+ }
+
+ final int trigger = getMagnifierHandleTrigger();
+ final int offset;
+ switch (trigger) {
+ case MagnifierHandleTrigger.INSERTION: // Fall through.
+ case MagnifierHandleTrigger.SELECTION_START:
+ offset = mTextView.getSelectionStart();
+ break;
+ case MagnifierHandleTrigger.SELECTION_END:
+ offset = mTextView.getSelectionEnd();
+ break;
+ default:
+ offset = -1;
+ break;
+ }
+
+ if (offset == -1) {
+ dismissMagnifier();
+ }
+
+ final Layout layout = mTextView.getLayout();
+ final int lineNumber = layout.getLineForOffset(offset);
+ // Horizontally snap to character offset.
+ final float xPosInView = getHorizontal(mTextView.getLayout(), offset);
+ // Vertically snap to middle of current line.
+ final float yPosInView = (mTextView.getLayout().getLineTop(lineNumber)
+ + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f;
+ final int[] coordinatesOnScreen = new int[2];
+ mTextView.getLocationOnScreen(coordinatesOnScreen);
+ final float centerXOnScreen = xPosInView + mTextView.getTotalPaddingLeft()
+ - mTextView.getScrollX() + coordinatesOnScreen[0];
+ final float centerYOnScreen = yPosInView + mTextView.getTotalPaddingTop()
+ - mTextView.getScrollY() + coordinatesOnScreen[1];
+
+ mMagnifier.show(centerXOnScreen, centerYOnScreen, MAGNIFIER_ZOOM);
+ }
+
+ protected final void dismissMagnifier() {
+ if (mMagnifier != null) {
+ mMagnifier.dismiss();
+ }
+ }
+
@Override
public boolean onTouchEvent(MotionEvent ev) {
updateFloatingToolbarVisibility(ev);
@@ -4542,10 +4611,7 @@
case MotionEvent.ACTION_UP:
filterOnTouchUp(ev.isFromSource(InputDevice.SOURCE_TOUCHSCREEN));
- mIsDragging = false;
- updateDrawable();
- break;
-
+ // Fall through.
case MotionEvent.ACTION_CANCEL:
mIsDragging = false;
updateDrawable();
@@ -4646,9 +4712,9 @@
@Override
protected int getCursorOffset() {
int offset = super.getCursorOffset();
- if (mCursorDrawable != null) {
- mCursorDrawable.getPadding(mTempRect);
- offset += (mCursorDrawable.getIntrinsicWidth()
+ if (mDrawableForCursor != null) {
+ mDrawableForCursor.getPadding(mTempRect);
+ offset += (mDrawableForCursor.getIntrinsicWidth()
- mTempRect.left - mTempRect.right) / 2;
}
return offset;
@@ -4656,9 +4722,9 @@
@Override
int getCursorHorizontalPosition(Layout layout, int offset) {
- if (mCursorDrawable != null) {
+ if (mDrawableForCursor != null) {
final float horizontal = getHorizontal(layout, offset);
- return clampHorizontalPosition(mCursorDrawable, horizontal) + mTempRect.left;
+ return clampHorizontalPosition(mDrawableForCursor, horizontal) + mTempRect.left;
}
return super.getCursorHorizontalPosition(layout, offset);
}
@@ -4671,6 +4737,11 @@
case MotionEvent.ACTION_DOWN:
mDownPositionX = ev.getRawX();
mDownPositionY = ev.getRawY();
+ showMagnifier();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ showMagnifier();
break;
case MotionEvent.ACTION_UP:
@@ -4696,11 +4767,10 @@
mTextActionMode.invalidateContentRect();
}
}
- hideAfterDelay();
- break;
-
+ // Fall through.
case MotionEvent.ACTION_CANCEL:
hideAfterDelay();
+ dismissMagnifier();
break;
default:
@@ -4751,6 +4821,12 @@
super.onDetached();
removeHiderCallback();
}
+
+ @Override
+ @MagnifierHandleTrigger
+ protected int getMagnifierHandleTrigger() {
+ return MagnifierHandleTrigger.INSERTION;
+ }
}
@Retention(RetentionPolicy.SOURCE)
@@ -5009,12 +5085,26 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean superResult = super.onTouchEvent(event);
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- // Reset the touch word offset and x value when the user
- // re-engages the handle.
- mTouchWordDelta = 0.0f;
- mPrevX = UNSET_X_VALUE;
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ // Reset the touch word offset and x value when the user
+ // re-engages the handle.
+ mTouchWordDelta = 0.0f;
+ mPrevX = UNSET_X_VALUE;
+ showMagnifier();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ showMagnifier();
+ break;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ dismissMagnifier();
+ break;
}
+
return superResult;
}
@@ -5110,6 +5200,13 @@
return isRtlChar == isRtlParagraph ? primaryOffset : secondaryOffset;
}
}
+
+ @MagnifierHandleTrigger
+ protected int getMagnifierHandleTrigger() {
+ return isStartHandle()
+ ? MagnifierHandleTrigger.SELECTION_START
+ : MagnifierHandleTrigger.SELECTION_END;
+ }
}
private int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index bf25915..23ebadb 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -2296,8 +2296,8 @@
}
/** @hide */
- protected final void detachFromAnchor() {
- final View anchor = mAnchor != null ? mAnchor.get() : null;
+ protected void detachFromAnchor() {
+ final View anchor = getAnchor();
if (anchor != null) {
final ViewTreeObserver vto = anchor.getViewTreeObserver();
vto.removeOnScrollChangedListener(mOnScrollChangedListener);
@@ -2316,7 +2316,7 @@
}
/** @hide */
- protected final void attachToAnchor(View anchor, int xoff, int yoff, int gravity) {
+ protected void attachToAnchor(View anchor, int xoff, int yoff, int gravity) {
detachFromAnchor();
final ViewTreeObserver vto = anchor.getViewTreeObserver();
@@ -2339,6 +2339,11 @@
mAnchoredGravity = gravity;
}
+ /** @hide */
+ protected @Nullable View getAnchor() {
+ return mAnchor != null ? mAnchor.get() : null;
+ }
+
private void alignToAnchor() {
final View anchor = mAnchor != null ? mAnchor.get() : null;
if (anchor != null && anchor.isAttachedToWindow() && hasDecorView()) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7903d6f..1b26f8e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -20,6 +20,7 @@
import android.annotation.ColorInt;
import android.annotation.DimenRes;
+import android.annotation.NonNull;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.Application;
@@ -108,9 +109,9 @@
// The unique identifiers for each custom {@link Action}.
private static final int SET_ON_CLICK_PENDING_INTENT_TAG = 1;
private static final int REFLECTION_ACTION_TAG = 2;
- private static final int SET_DRAWABLE_PARAMETERS_TAG = 3;
+ private static final int SET_DRAWABLE_TINT_TAG = 3;
private static final int VIEW_GROUP_ACTION_ADD_TAG = 4;
- private static final int SET_REFLECTION_ACTION_WITHOUT_PARAMS_TAG = 5;
+ private static final int VIEW_CONTENT_NAVIGATION_TAG = 5;
private static final int SET_EMPTY_VIEW_ACTION_TAG = 6;
private static final int VIEW_GROUP_ACTION_REMOVE_TAG = 7;
private static final int SET_PENDING_INTENT_TEMPLATE_TAG = 8;
@@ -121,7 +122,6 @@
private static final int TEXT_VIEW_SIZE_ACTION_TAG = 13;
private static final int VIEW_PADDING_ACTION_TAG = 14;
private static final int SET_REMOTE_VIEW_ADAPTER_LIST_TAG = 15;
- private static final int TEXT_VIEW_DRAWABLE_COLOR_FILTER_ACTION_TAG = 17;
private static final int SET_REMOTE_INPUTS_ACTION_TAG = 18;
private static final int LAYOUT_PARAM_ACTION_TAG = 19;
private static final int OVERRIDE_TEXT_COLORS_TAG = 20;
@@ -389,10 +389,10 @@
return MERGE_REPLACE;
}
- public abstract String getActionName();
+ public abstract int getActionTag();
public String getUniqueKey() {
- return (getActionName() + viewId);
+ return (getActionTag() + "_" + viewId);
}
/**
@@ -424,8 +424,8 @@
*/
private static abstract class RuntimeAction extends Action {
@Override
- public final String getActionName() {
- return "RuntimeAction";
+ public final int getActionTag() {
+ return 0;
}
@Override
@@ -514,7 +514,6 @@
}
private class SetEmptyView extends Action {
- int viewId;
int emptyViewId;
SetEmptyView(int viewId, int emptyViewId) {
@@ -528,7 +527,6 @@
}
public void writeToParcel(Parcel out, int flags) {
- out.writeInt(SET_EMPTY_VIEW_ACTION_TAG);
out.writeInt(this.viewId);
out.writeInt(this.emptyViewId);
}
@@ -546,8 +544,9 @@
adapterView.setEmptyView(emptyView);
}
- public String getActionName() {
- return "SetEmptyView";
+ @Override
+ public int getActionTag() {
+ return SET_EMPTY_VIEW_ACTION_TAG;
}
}
@@ -559,13 +558,12 @@
public SetOnClickFillInIntent(Parcel parcel) {
viewId = parcel.readInt();
- fillInIntent = Intent.CREATOR.createFromParcel(parcel);
+ fillInIntent = parcel.readTypedObject(Intent.CREATOR);
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(SET_ON_CLICK_FILL_IN_INTENT_TAG);
dest.writeInt(viewId);
- fillInIntent.writeToParcel(dest, 0 /* no flags */);
+ dest.writeTypedObject(fillInIntent, 0 /* no flags */);
}
@Override
@@ -624,8 +622,9 @@
}
}
- public String getActionName() {
- return "SetOnClickFillInIntent";
+ @Override
+ public int getActionTag() {
+ return SET_ON_CLICK_FILL_IN_INTENT_TAG;
}
Intent fillInIntent;
@@ -643,9 +642,8 @@
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(SET_PENDING_INTENT_TEMPLATE_TAG);
dest.writeInt(viewId);
- pendingIntentTemplate.writeToParcel(dest, 0 /* no flags */);
+ PendingIntent.writePendingIntentOrNullToParcel(pendingIntentTemplate, dest);
}
@Override
@@ -699,8 +697,9 @@
}
}
- public String getActionName() {
- return "SetPendingIntentTemplate";
+ @Override
+ public int getActionTag() {
+ return SET_PENDING_INTENT_TEMPLATE_TAG;
}
PendingIntent pendingIntentTemplate;
@@ -716,30 +715,13 @@
public SetRemoteViewsAdapterList(Parcel parcel) {
viewId = parcel.readInt();
viewTypeCount = parcel.readInt();
- int count = parcel.readInt();
- list = new ArrayList<RemoteViews>();
-
- for (int i = 0; i < count; i++) {
- RemoteViews rv = RemoteViews.CREATOR.createFromParcel(parcel);
- list.add(rv);
- }
+ list = parcel.createTypedArrayList(RemoteViews.CREATOR);
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(SET_REMOTE_VIEW_ADAPTER_LIST_TAG);
dest.writeInt(viewId);
dest.writeInt(viewTypeCount);
-
- if (list == null || list.size() == 0) {
- dest.writeInt(0);
- } else {
- int count = list.size();
- dest.writeInt(count);
- for (int i = 0; i < count; i++) {
- RemoteViews rv = list.get(i);
- rv.writeToParcel(dest, flags);
- }
- }
+ dest.writeTypedList(list, flags);
}
@Override
@@ -779,8 +761,9 @@
}
}
- public String getActionName() {
- return "SetRemoteViewsAdapterList";
+ @Override
+ public int getActionTag() {
+ return SET_REMOTE_VIEW_ADAPTER_LIST_TAG;
}
int viewTypeCount;
@@ -795,13 +778,12 @@
public SetRemoteViewsAdapterIntent(Parcel parcel) {
viewId = parcel.readInt();
- intent = Intent.CREATOR.createFromParcel(parcel);
+ intent = parcel.readTypedObject(Intent.CREATOR);
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(SET_REMOTE_VIEW_ADAPTER_INTENT_TAG);
dest.writeInt(viewId);
- intent.writeToParcel(dest, flags);
+ dest.writeTypedObject(intent, flags);
}
@Override
@@ -845,8 +827,9 @@
return copy;
}
- public String getActionName() {
- return "SetRemoteViewsAdapterIntent";
+ @Override
+ public int getActionTag() {
+ return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG;
}
Intent intent;
@@ -866,22 +849,12 @@
public SetOnClickPendingIntent(Parcel parcel) {
viewId = parcel.readInt();
-
- // We check a flag to determine if the parcel contains a PendingIntent.
- if (parcel.readInt() != 0) {
- pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
- }
+ pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(SET_ON_CLICK_PENDING_INTENT_TAG);
dest.writeInt(viewId);
-
- // We use a flag to indicate whether the parcel contains a valid object.
- dest.writeInt(pendingIntent != null ? 1 : 0);
- if (pendingIntent != null) {
- pendingIntent.writeToParcel(dest, 0 /* no flags */);
- }
+ PendingIntent.writePendingIntentOrNullToParcel(pendingIntent, dest);
}
@Override
@@ -922,8 +895,9 @@
target.setOnClickListener(listener);
}
- public String getActionName() {
- return "SetOnClickPendingIntent";
+ @Override
+ public int getActionTag() {
+ return SET_ON_CLICK_PENDING_INTENT_TAG;
}
PendingIntent pendingIntent;
@@ -1012,55 +986,37 @@
}
/**
- * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
+ * Equivalent to calling
* {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
- * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view.
+ * on the {@link Drawable} of a given view.
* <p>
- * These operations will be performed on the {@link Drawable} returned by the
+ * The operation will be performed on the {@link Drawable} returned by the
* target {@link View#getBackground()} by default. If targetBackground is false,
* we assume the target is an {@link ImageView} and try applying the operations
* to {@link ImageView#getDrawable()}.
* <p>
- * You can omit specific calls by marking their values with null or -1.
*/
- private class SetDrawableParameters extends Action {
- public SetDrawableParameters(int id, boolean targetBackground, int alpha,
- int colorFilter, PorterDuff.Mode mode, int level) {
+ private class SetDrawableTint extends Action {
+ SetDrawableTint(int id, boolean targetBackground,
+ int colorFilter, @NonNull PorterDuff.Mode mode) {
this.viewId = id;
this.targetBackground = targetBackground;
- this.alpha = alpha;
this.colorFilter = colorFilter;
this.filterMode = mode;
- this.level = level;
}
- public SetDrawableParameters(Parcel parcel) {
+ SetDrawableTint(Parcel parcel) {
viewId = parcel.readInt();
targetBackground = parcel.readInt() != 0;
- alpha = parcel.readInt();
colorFilter = parcel.readInt();
- boolean hasMode = parcel.readInt() != 0;
- if (hasMode) {
- filterMode = PorterDuff.Mode.valueOf(parcel.readString());
- } else {
- filterMode = null;
- }
- level = parcel.readInt();
+ filterMode = PorterDuff.intToMode(parcel.readInt());
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(SET_DRAWABLE_PARAMETERS_TAG);
dest.writeInt(viewId);
dest.writeInt(targetBackground ? 1 : 0);
- dest.writeInt(alpha);
dest.writeInt(colorFilter);
- if (filterMode != null) {
- dest.writeInt(1);
- dest.writeString(filterMode.toString());
- } else {
- dest.writeInt(0);
- }
- dest.writeInt(level);
+ dest.writeInt(PorterDuff.modeToInt(filterMode));
}
@Override
@@ -1078,47 +1034,36 @@
}
if (targetDrawable != null) {
- // Perform modifications only if values are set correctly
- if (alpha != -1) {
- targetDrawable.mutate().setAlpha(alpha);
- }
- if (filterMode != null) {
- targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
- }
- if (level != -1) {
- targetDrawable.mutate().setLevel(level);
- }
+ targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
}
}
- public String getActionName() {
- return "SetDrawableParameters";
+ @Override
+ public int getActionTag() {
+ return SET_DRAWABLE_TINT_TAG;
}
boolean targetBackground;
- int alpha;
int colorFilter;
PorterDuff.Mode filterMode;
- int level;
}
- private final class ReflectionActionWithoutParams extends Action {
- final String methodName;
+ private final class ViewContentNavigation extends Action {
+ final boolean mNext;
- ReflectionActionWithoutParams(int viewId, String methodName) {
+ ViewContentNavigation(int viewId, boolean next) {
this.viewId = viewId;
- this.methodName = methodName;
+ this.mNext = next;
}
- ReflectionActionWithoutParams(Parcel in) {
+ ViewContentNavigation(Parcel in) {
this.viewId = in.readInt();
- this.methodName = in.readString();
+ this.mNext = in.readBoolean();
}
public void writeToParcel(Parcel out, int flags) {
- out.writeInt(SET_REFLECTION_ACTION_WITHOUT_PARAMS_TAG);
out.writeInt(this.viewId);
- out.writeString(this.methodName);
+ out.writeBoolean(this.mNext);
}
@Override
@@ -1127,23 +1072,20 @@
if (view == null) return;
try {
- getMethod(view, this.methodName, null, false /* async */).invoke(view);
+ getMethod(view,
+ mNext ? "showNext" : "showPrevious", null, false /* async */).invoke(view);
} catch (Throwable ex) {
throw new ActionException(ex);
}
}
public int mergeBehavior() {
- // we don't need to build up showNext or showPrevious calls
- if (methodName.equals("showNext") || methodName.equals("showPrevious")) {
- return MERGE_IGNORE;
- } else {
- return MERGE_REPLACE;
- }
+ return MERGE_IGNORE;
}
- public String getActionName() {
- return "ReflectionActionWithoutParams";
+ @Override
+ public int getActionTag() {
+ return VIEW_CONTENT_NAVIGATION_TAG;
}
}
@@ -1157,12 +1099,7 @@
}
public BitmapCache(Parcel source) {
- int count = source.readInt();
- mBitmaps = new ArrayList<>(count);
- for (int i = 0; i < count; i++) {
- Bitmap b = Bitmap.CREATOR.createFromParcel(source);
- mBitmaps.add(b);
- }
+ mBitmaps = source.createTypedArrayList(Bitmap.CREATOR);
}
public int getBitmapId(Bitmap b) {
@@ -1188,11 +1125,7 @@
}
public void writeBitmapsToParcel(Parcel dest, int flags) {
- int count = mBitmaps.size();
- dest.writeInt(count);
- for (int i = 0; i < count; i++) {
- mBitmaps.get(i).writeToParcel(dest, flags);
- }
+ dest.writeTypedList(mBitmaps, flags);
}
public int getBitmapMemory() {
@@ -1228,7 +1161,6 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(BITMAP_REFLECTION_ACTION_TAG);
dest.writeInt(viewId);
dest.writeString(methodName);
dest.writeInt(bitmapId);
@@ -1247,8 +1179,9 @@
bitmapId = bitmapCache.getBitmapId(bitmap);
}
- public String getActionName() {
- return "BitmapReflectionAction";
+ @Override
+ public int getActionTag() {
+ return BITMAP_REFLECTION_ACTION_TAG;
}
}
@@ -1300,7 +1233,7 @@
// written to the parcel.
switch (this.type) {
case BOOLEAN:
- this.value = in.readInt() != 0;
+ this.value = in.readBoolean();
break;
case BYTE:
this.value = in.readByte();
@@ -1330,39 +1263,28 @@
this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
break;
case URI:
- if (in.readInt() != 0) {
- this.value = Uri.CREATOR.createFromParcel(in);
- }
+ this.value = in.readTypedObject(Uri.CREATOR);
break;
case BITMAP:
- if (in.readInt() != 0) {
- this.value = Bitmap.CREATOR.createFromParcel(in);
- }
+ this.value = in.readTypedObject(Bitmap.CREATOR);
break;
case BUNDLE:
this.value = in.readBundle();
break;
case INTENT:
- if (in.readInt() != 0) {
- this.value = Intent.CREATOR.createFromParcel(in);
- }
+ this.value = in.readTypedObject(Intent.CREATOR);
break;
case COLOR_STATE_LIST:
- if (in.readInt() != 0) {
- this.value = ColorStateList.CREATOR.createFromParcel(in);
- }
+ this.value = in.readTypedObject(ColorStateList.CREATOR);
break;
case ICON:
- if (in.readInt() != 0) {
- this.value = Icon.CREATOR.createFromParcel(in);
- }
+ this.value = in.readTypedObject(Icon.CREATOR);
default:
break;
}
}
public void writeToParcel(Parcel out, int flags) {
- out.writeInt(REFLECTION_ACTION_TAG);
out.writeInt(this.viewId);
out.writeString(this.methodName);
out.writeInt(this.type);
@@ -1376,7 +1298,7 @@
// we have written a valid value to the parcel.
switch (this.type) {
case BOOLEAN:
- out.writeInt((Boolean) this.value ? 1 : 0);
+ out.writeBoolean((Boolean) this.value);
break;
case BYTE:
out.writeByte((Byte) this.value);
@@ -1413,10 +1335,7 @@
case INTENT:
case COLOR_STATE_LIST:
case ICON:
- out.writeInt(this.value != null ? 1 : 0);
- if (this.value != null) {
- ((Parcelable) this.value).writeToParcel(out, flags);
- }
+ out.writeTypedObject((Parcelable) this.value, flags);
break;
default:
break;
@@ -1522,10 +1441,16 @@
}
}
- public String getActionName() {
+ @Override
+ public int getActionTag() {
+ return REFLECTION_ACTION_TAG;
+ }
+
+ @Override
+ public String getUniqueKey() {
// Each type of reflection action corresponds to a setter, so each should be seen as
// unique from the standpoint of merging.
- return "ReflectionAction" + this.methodName + this.type;
+ return super.getUniqueKey() + this.methodName + this.type;
}
@Override
@@ -1587,7 +1512,6 @@
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(VIEW_GROUP_ACTION_ADD_TAG);
dest.writeInt(viewId);
dest.writeInt(mIndex);
mNestedViews.writeToParcel(dest, flags);
@@ -1662,10 +1586,9 @@
return mNestedViews.prefersAsyncApply();
}
-
@Override
- public String getActionName() {
- return "ViewGroupActionAdd";
+ public int getActionTag() {
+ return VIEW_GROUP_ACTION_ADD_TAG;
}
}
@@ -1697,7 +1620,6 @@
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(VIEW_GROUP_ACTION_REMOVE_TAG);
dest.writeInt(viewId);
dest.writeInt(mViewIdToKeep);
}
@@ -1763,8 +1685,8 @@
}
@Override
- public String getActionName() {
- return "ViewGroupActionRemove";
+ public int getActionTag() {
+ return VIEW_GROUP_ACTION_REMOVE_TAG;
}
@Override
@@ -1804,18 +1726,10 @@
isRelative = (parcel.readInt() != 0);
useIcons = (parcel.readInt() != 0);
if (useIcons) {
- if (parcel.readInt() != 0) {
- i1 = Icon.CREATOR.createFromParcel(parcel);
- }
- if (parcel.readInt() != 0) {
- i2 = Icon.CREATOR.createFromParcel(parcel);
- }
- if (parcel.readInt() != 0) {
- i3 = Icon.CREATOR.createFromParcel(parcel);
- }
- if (parcel.readInt() != 0) {
- i4 = Icon.CREATOR.createFromParcel(parcel);
- }
+ i1 = parcel.readTypedObject(Icon.CREATOR);
+ i2 = parcel.readTypedObject(Icon.CREATOR);
+ i3 = parcel.readTypedObject(Icon.CREATOR);
+ i4 = parcel.readTypedObject(Icon.CREATOR);
} else {
d1 = parcel.readInt();
d2 = parcel.readInt();
@@ -1825,35 +1739,14 @@
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(TEXT_VIEW_DRAWABLE_ACTION_TAG);
dest.writeInt(viewId);
dest.writeInt(isRelative ? 1 : 0);
dest.writeInt(useIcons ? 1 : 0);
if (useIcons) {
- if (i1 != null) {
- dest.writeInt(1);
- i1.writeToParcel(dest, 0);
- } else {
- dest.writeInt(0);
- }
- if (i2 != null) {
- dest.writeInt(1);
- i2.writeToParcel(dest, 0);
- } else {
- dest.writeInt(0);
- }
- if (i3 != null) {
- dest.writeInt(1);
- i3.writeToParcel(dest, 0);
- } else {
- dest.writeInt(0);
- }
- if (i4 != null) {
- dest.writeInt(1);
- i4.writeToParcel(dest, 0);
- } else {
- dest.writeInt(0);
- }
+ dest.writeTypedObject(i1, 0);
+ dest.writeTypedObject(i2, 0);
+ dest.writeTypedObject(i3, 0);
+ dest.writeTypedObject(i4, 0);
} else {
dest.writeInt(d1);
dest.writeInt(d2);
@@ -1924,8 +1817,9 @@
return useIcons;
}
- public String getActionName() {
- return "TextViewDrawableAction";
+ @Override
+ public int getActionTag() {
+ return TEXT_VIEW_DRAWABLE_ACTION_TAG;
}
boolean isRelative = false;
@@ -1954,7 +1848,6 @@
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(TEXT_VIEW_SIZE_ACTION_TAG);
dest.writeInt(viewId);
dest.writeInt(units);
dest.writeFloat(size);
@@ -1967,8 +1860,9 @@
target.setTextSize(units, size);
}
- public String getActionName() {
- return "TextViewSizeAction";
+ @Override
+ public int getActionTag() {
+ return TEXT_VIEW_SIZE_ACTION_TAG;
}
int units;
@@ -1996,7 +1890,6 @@
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(VIEW_PADDING_ACTION_TAG);
dest.writeInt(viewId);
dest.writeInt(left);
dest.writeInt(top);
@@ -2011,8 +1904,9 @@
target.setPadding(left, top, right, bottom);
}
- public String getActionName() {
- return "ViewPaddingAction";
+ @Override
+ public int getActionTag() {
+ return VIEW_PADDING_ACTION_TAG;
}
int left, top, right, bottom;
@@ -2029,6 +1923,9 @@
public static final int LAYOUT_WIDTH = 2;
public static final int LAYOUT_MARGIN_BOTTOM_DIMEN = 3;
+ final int mProperty;
+ final int mValue;
+
/**
* @param viewId ID of the view alter
* @param property which layout parameter to alter
@@ -2036,21 +1933,20 @@
*/
public LayoutParamAction(int viewId, int property, int value) {
this.viewId = viewId;
- this.property = property;
- this.value = value;
+ this.mProperty = property;
+ this.mValue = value;
}
public LayoutParamAction(Parcel parcel) {
viewId = parcel.readInt();
- property = parcel.readInt();
- value = parcel.readInt();
+ mProperty = parcel.readInt();
+ mValue = parcel.readInt();
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(LAYOUT_PARAM_ACTION_TAG);
dest.writeInt(viewId);
- dest.writeInt(property);
- dest.writeInt(value);
+ dest.writeInt(mProperty);
+ dest.writeInt(mValue);
}
@Override
@@ -2063,27 +1959,27 @@
if (layoutParams == null) {
return;
}
- switch (property) {
+ switch (mProperty) {
case LAYOUT_MARGIN_END_DIMEN:
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
- int resolved = resolveDimenPixelOffset(target, value);
+ int resolved = resolveDimenPixelOffset(target, mValue);
((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(resolved);
target.setLayoutParams(layoutParams);
}
break;
case LAYOUT_MARGIN_BOTTOM_DIMEN:
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
- int resolved = resolveDimenPixelOffset(target, value);
+ int resolved = resolveDimenPixelOffset(target, mValue);
((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = resolved;
target.setLayoutParams(layoutParams);
}
break;
case LAYOUT_WIDTH:
- layoutParams.width = value;
+ layoutParams.width = mValue;
target.setLayoutParams(layoutParams);
break;
default:
- throw new IllegalArgumentException("Unknown property " + property);
+ throw new IllegalArgumentException("Unknown property " + mProperty);
}
}
@@ -2094,79 +1990,15 @@
return target.getContext().getResources().getDimensionPixelOffset(value);
}
- public String getActionName() {
- return "LayoutParamAction" + property + ".";
- }
-
- int property;
- int value;
- }
-
- /**
- * Helper action to set a color filter on a compound drawable on a TextView. Supports relative
- * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
- */
- private class TextViewDrawableColorFilterAction extends Action {
- public TextViewDrawableColorFilterAction(int viewId, boolean isRelative, int index,
- int color, PorterDuff.Mode mode) {
- this.viewId = viewId;
- this.isRelative = isRelative;
- this.index = index;
- this.color = color;
- this.mode = mode;
- }
-
- public TextViewDrawableColorFilterAction(Parcel parcel) {
- viewId = parcel.readInt();
- isRelative = (parcel.readInt() != 0);
- index = parcel.readInt();
- color = parcel.readInt();
- mode = readPorterDuffMode(parcel);
- }
-
- private PorterDuff.Mode readPorterDuffMode(Parcel parcel) {
- int mode = parcel.readInt();
- if (mode >= 0 && mode < PorterDuff.Mode.values().length) {
- return PorterDuff.Mode.values()[mode];
- } else {
- return PorterDuff.Mode.CLEAR;
- }
- }
-
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(TEXT_VIEW_DRAWABLE_COLOR_FILTER_ACTION_TAG);
- dest.writeInt(viewId);
- dest.writeInt(isRelative ? 1 : 0);
- dest.writeInt(index);
- dest.writeInt(color);
- dest.writeInt(mode.ordinal());
+ @Override
+ public int getActionTag() {
+ return LAYOUT_PARAM_ACTION_TAG;
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
- final TextView target = root.findViewById(viewId);
- if (target == null) return;
- Drawable[] drawables = isRelative
- ? target.getCompoundDrawablesRelative()
- : target.getCompoundDrawables();
- if (index < 0 || index >= 4) {
- throw new IllegalStateException("index must be in range [0, 3].");
- }
- Drawable d = drawables[index];
- if (d != null) {
- d.mutate();
- d.setColorFilter(color, mode);
- }
+ public String getUniqueKey() {
+ return super.getUniqueKey() + mProperty;
}
-
- public String getActionName() {
- return "TextViewDrawableColorFilterAction";
- }
-
- final boolean isRelative;
- final int index;
- final int color;
- final PorterDuff.Mode mode;
}
/**
@@ -2185,7 +2017,6 @@
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(SET_REMOTE_INPUTS_ACTION_TAG);
dest.writeInt(viewId);
dest.writeTypedArray(remoteInputs, flags);
}
@@ -2198,8 +2029,9 @@
target.setTagInternal(R.id.remote_input_tag, remoteInputs);
}
- public String getActionName() {
- return "SetRemoteInputsAction";
+ @Override
+ public int getActionTag() {
+ return SET_REMOTE_INPUTS_ACTION_TAG;
}
final Parcelable[] remoteInputs;
@@ -2221,7 +2053,6 @@
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(OVERRIDE_TEXT_COLORS_TAG);
dest.writeInt(textColor);
}
@@ -2246,8 +2077,9 @@
}
}
- public String getActionName() {
- return "OverrideTextColorsAction";
+ @Override
+ public int getActionTag() {
+ return OVERRIDE_TEXT_COLORS_TAG;
}
}
@@ -2339,20 +2171,12 @@
}
if (src.mActions != null) {
- mActions = new ArrayList<>();
-
Parcel p = Parcel.obtain();
- int count = src.mActions.size();
- for (int i = 0; i < count; i++) {
- p.setDataPosition(0);
- Action a = src.mActions.get(i);
- a.writeToParcel(
- p, a.hasSameAppInfo(mApplication) ? PARCELABLE_ELIDE_DUPLICATES : 0);
- p.setDataPosition(0);
- // Since src is already in memory, we do not care about stack overflow as it has
- // already been read once.
- mActions.add(getActionFromParcel(p, 0));
- }
+ src.writeActionsToParcel(p);
+ p.setDataPosition(0);
+ // Since src is already in memory, we do not care about stack overflow as it has
+ // already been read once.
+ readActionsFromParcel(p, 0);
p.recycle();
}
@@ -2393,13 +2217,7 @@
mLayoutId = parcel.readInt();
mIsWidgetCollectionChild = parcel.readInt() == 1;
- int count = parcel.readInt();
- if (count > 0) {
- mActions = new ArrayList<>(count);
- for (int i = 0; i < count; i++) {
- mActions.add(getActionFromParcel(parcel, depth));
- }
- }
+ readActionsFromParcel(parcel, depth);
} else {
// MODE_HAS_LANDSCAPE_AND_PORTRAIT
mLandscape = new RemoteViews(parcel, mBitmapCache, info, depth);
@@ -2410,21 +2228,31 @@
mReapplyDisallowed = parcel.readInt() == 0;
}
+ private void readActionsFromParcel(Parcel parcel, int depth) {
+ int count = parcel.readInt();
+ if (count > 0) {
+ mActions = new ArrayList<>(count);
+ for (int i = 0; i < count; i++) {
+ mActions.add(getActionFromParcel(parcel, depth));
+ }
+ }
+ }
+
private Action getActionFromParcel(Parcel parcel, int depth) {
int tag = parcel.readInt();
switch (tag) {
case SET_ON_CLICK_PENDING_INTENT_TAG:
return new SetOnClickPendingIntent(parcel);
- case SET_DRAWABLE_PARAMETERS_TAG:
- return new SetDrawableParameters(parcel);
+ case SET_DRAWABLE_TINT_TAG:
+ return new SetDrawableTint(parcel);
case REFLECTION_ACTION_TAG:
return new ReflectionAction(parcel);
case VIEW_GROUP_ACTION_ADD_TAG:
return new ViewGroupActionAdd(parcel, mBitmapCache, mApplication, depth);
case VIEW_GROUP_ACTION_REMOVE_TAG:
return new ViewGroupActionRemove(parcel);
- case SET_REFLECTION_ACTION_WITHOUT_PARAMS_TAG:
- return new ReflectionActionWithoutParams(parcel);
+ case VIEW_CONTENT_NAVIGATION_TAG:
+ return new ViewContentNavigation(parcel);
case SET_EMPTY_VIEW_ACTION_TAG:
return new SetEmptyView(parcel);
case SET_PENDING_INTENT_TEMPLATE_TAG:
@@ -2443,8 +2271,6 @@
return new BitmapReflectionAction(parcel);
case SET_REMOTE_VIEW_ADAPTER_LIST_TAG:
return new SetRemoteViewsAdapterList(parcel);
- case TEXT_VIEW_DRAWABLE_COLOR_FILTER_ACTION_TAG:
- return new TextViewDrawableColorFilterAction(parcel);
case SET_REMOTE_INPUTS_ACTION_TAG:
return new SetRemoteInputsAction(parcel);
case LAYOUT_PARAM_ACTION_TAG:
@@ -2601,7 +2427,7 @@
* @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
*/
public void showNext(int viewId) {
- addAction(new ReflectionActionWithoutParams(viewId, "showNext"));
+ addAction(new ViewContentNavigation(viewId, true /* next */));
}
/**
@@ -2610,7 +2436,7 @@
* @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
*/
public void showPrevious(int viewId) {
- addAction(new ReflectionActionWithoutParams(viewId, "showPrevious"));
+ addAction(new ViewContentNavigation(viewId, false /* next */));
}
/**
@@ -2684,28 +2510,6 @@
}
/**
- * Equivalent to applying a color filter on one of the drawables in
- * {@link android.widget.TextView#getCompoundDrawablesRelative()}.
- *
- * @param viewId The id of the view whose text should change.
- * @param index The index of the drawable in the array of
- * {@link android.widget.TextView#getCompoundDrawablesRelative()} to set the color
- * filter on. Must be in [0, 3].
- * @param color The color of the color filter. See
- * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
- * @param mode The mode of the color filter. See
- * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
- * @hide
- */
- public void setTextViewCompoundDrawablesRelativeColorFilter(int viewId,
- int index, int color, PorterDuff.Mode mode) {
- if (index < 0 || index >= 4) {
- throw new IllegalArgumentException("index must be in range [0, 3].");
- }
- addAction(new TextViewDrawableColorFilterAction(viewId, true, index, color, mode));
- }
-
- /**
* Equivalent to calling {@link
* TextView#setCompoundDrawablesWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
* using the drawables yielded by {@link Icon#loadDrawable(Context)}.
@@ -2902,12 +2706,10 @@
/**
* @hide
- * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
+ * Equivalent to calling
* {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
- * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given
- * view.
+ * on the {@link Drawable} of a given view.
* <p>
- * You can omit specific calls by marking their values with null or -1.
*
* @param viewId The id of the view that contains the target
* {@link Drawable}
@@ -2916,20 +2718,15 @@
* {@link android.view.View#getBackground()}. Otherwise, assume
* the target view is an {@link ImageView} and apply them to
* {@link ImageView#getDrawable()}.
- * @param alpha Specify an alpha value for the drawable, or -1 to leave
- * unchanged.
* @param colorFilter Specify a color for a
* {@link android.graphics.ColorFilter} for this drawable. This will be ignored if
* {@code mode} is {@code null}.
* @param mode Specify a PorterDuff mode for this drawable, or null to leave
* unchanged.
- * @param level Specify the level for the drawable, or -1 to leave
- * unchanged.
*/
- public void setDrawableParameters(int viewId, boolean targetBackground, int alpha,
- int colorFilter, PorterDuff.Mode mode, int level) {
- addAction(new SetDrawableParameters(viewId, targetBackground, alpha,
- colorFilter, mode, level));
+ public void setDrawableTint(int viewId, boolean targetBackground,
+ int colorFilter, @NonNull PorterDuff.Mode mode) {
+ addAction(new SetDrawableTint(viewId, targetBackground, colorFilter, mode));
}
/**
@@ -3696,18 +3493,7 @@
}
dest.writeInt(mLayoutId);
dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
- int count;
- if (mActions != null) {
- count = mActions.size();
- } else {
- count = 0;
- }
- dest.writeInt(count);
- for (int i=0; i<count; i++) {
- Action a = mActions.get(i);
- a.writeToParcel(dest, a.hasSameAppInfo(mApplication)
- ? PARCELABLE_ELIDE_DUPLICATES : 0);
- }
+ writeActionsToParcel(dest);
} else {
dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
// We only write the bitmap cache if we are the root RemoteViews, as this cache
@@ -3722,6 +3508,22 @@
dest.writeInt(mReapplyDisallowed ? 1 : 0);
}
+ private void writeActionsToParcel(Parcel parcel) {
+ int count;
+ if (mActions != null) {
+ count = mActions.size();
+ } else {
+ count = 0;
+ }
+ parcel.writeInt(count);
+ for (int i = 0; i < count; i++) {
+ Action a = mActions.get(i);
+ parcel.writeInt(a.getActionTag());
+ a.writeToParcel(parcel, a.hasSameAppInfo(mApplication)
+ ? PARCELABLE_ELIDE_DUPLICATES : 0);
+ }
+ }
+
private static ApplicationInfo getApplicationInfo(String packageName, int userId) {
if (packageName == null) {
return null;
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 36dc330..3be42a5 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -43,9 +43,11 @@
import java.text.BreakIterator;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Pattern;
@@ -58,11 +60,7 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public final class SelectionActionModeHelper {
- /**
- * Maximum time (in milliseconds) to wait for a result before timing out.
- */
- // TODO: Consider making this a ViewConfiguration.
- private static final int TIMEOUT_DURATION = 200;
+ private static final String LOG_TAG = "SelectActionModeHelper";
private static final boolean SMART_SELECT_ANIMATION_ENABLED = true;
@@ -83,7 +81,8 @@
mEditor = Preconditions.checkNotNull(editor);
mTextView = mEditor.getTextView();
mTextClassificationHelper = new TextClassificationHelper(
- mTextView.getTextClassifier(), mTextView.getText(),
+ mTextView.getTextClassifier(),
+ getText(mTextView),
0, 1, mTextView.getTextLocales());
mSelectionTracker = new SelectionTracker(mTextView);
@@ -97,7 +96,7 @@
public void startActionModeAsync(boolean adjustSelection) {
mSelectionTracker.onOriginalSelection(
- mTextView.getText(),
+ getText(mTextView),
mTextView.getSelectionStart(),
mTextView.getSelectionEnd(),
mTextView.isTextEditable());
@@ -108,7 +107,7 @@
resetTextClassificationHelper();
mTextClassificationAsyncTask = new TextClassificationAsyncTask(
mTextView,
- TIMEOUT_DURATION,
+ mTextClassificationHelper.getTimeoutDuration(),
adjustSelection
? mTextClassificationHelper::suggestSelection
: mTextClassificationHelper::classifyText,
@@ -127,7 +126,7 @@
resetTextClassificationHelper();
mTextClassificationAsyncTask = new TextClassificationAsyncTask(
mTextView,
- TIMEOUT_DURATION,
+ mTextClassificationHelper.getTimeoutDuration(),
mTextClassificationHelper::classifyText,
this::invalidateActionMode)
.execute();
@@ -195,7 +194,7 @@
}
private void startActionMode(@Nullable SelectionResult result) {
- final CharSequence text = mTextView.getText();
+ final CharSequence text = getText(mTextView);
if (result != null && text instanceof Spannable) {
Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
mTextClassification = result.mClassification;
@@ -229,7 +228,7 @@
return;
}
- final List<RectF> selectionRectangles =
+ final List<SmartSelectSprite.RectangleWithTextSelectionLayout> selectionRectangles =
convertSelectionToRectangles(layout, result.mStart, result.mEnd);
final PointF touchPoint = new PointF(
@@ -237,7 +236,8 @@
mEditor.getLastUpPositionY());
final PointF animationStartPoint =
- movePointInsideNearestRectangle(touchPoint, selectionRectangles);
+ movePointInsideNearestRectangle(touchPoint, selectionRectangles,
+ SmartSelectSprite.RectangleWithTextSelectionLayout::getRectangle);
mSmartSelectSprite.startAnimation(
animationStartPoint,
@@ -245,38 +245,58 @@
onAnimationEndCallback);
}
- private List<RectF> convertSelectionToRectangles(final Layout layout, final int start,
- final int end) {
- final List<RectF> result = new ArrayList<>();
- layout.getSelection(start, end, (left, top, right, bottom, textSelectionLayout) ->
- mergeRectangleIntoList(result, new RectF(left, top, right, bottom)));
+ private List<SmartSelectSprite.RectangleWithTextSelectionLayout> convertSelectionToRectangles(
+ final Layout layout, final int start, final int end) {
+ final List<SmartSelectSprite.RectangleWithTextSelectionLayout> result = new ArrayList<>();
- result.sort(SmartSelectSprite.RECTANGLE_COMPARATOR);
+ final Layout.SelectionRectangleConsumer consumer =
+ (left, top, right, bottom, textSelectionLayout) -> mergeRectangleIntoList(
+ result,
+ new RectF(left, top, right, bottom),
+ SmartSelectSprite.RectangleWithTextSelectionLayout::getRectangle,
+ r -> new SmartSelectSprite.RectangleWithTextSelectionLayout(r,
+ textSelectionLayout)
+ );
+
+ layout.getSelection(start, end, consumer);
+
+ result.sort(Comparator.comparing(
+ SmartSelectSprite.RectangleWithTextSelectionLayout::getRectangle,
+ SmartSelectSprite.RECTANGLE_COMPARATOR));
+
return result;
}
+ // TODO: Move public pure functions out of this class and make it package-private.
/**
- * Merges a {@link RectF} into an existing list of rectangles. While merging, this method
- * makes sure that:
+ * Merges a {@link RectF} into an existing list of any objects which contain a rectangle.
+ * While merging, this method makes sure that:
*
* <ol>
* <li>No rectangle is redundant (contained within a bigger rectangle)</li>
* <li>Rectangles of the same height and vertical position that intersect get merged</li>
* </ol>
*
- * @param list the list of rectangles to merge the new rectangle in
+ * @param list the list of rectangles (or other rectangle containers) to merge the new
+ * rectangle into
* @param candidate the {@link RectF} to merge into the list
+ * @param extractor a function that can extract a {@link RectF} from an element of the given
+ * list
+ * @param packer a function that can wrap the resulting {@link RectF} into an element that
+ * the list contains
* @hide
*/
@VisibleForTesting
- public static void mergeRectangleIntoList(List<RectF> list, RectF candidate) {
+ public static <T> void mergeRectangleIntoList(final List<T> list,
+ final RectF candidate, final Function<T, RectF> extractor,
+ final Function<RectF, T> packer) {
if (candidate.isEmpty()) {
return;
}
final int elementCount = list.size();
for (int index = 0; index < elementCount; ++index) {
- final RectF existingRectangle = list.get(index);
+ final RectF existingRectangle = extractor.apply(list.get(index));
if (existingRectangle.contains(candidate)) {
return;
}
@@ -299,26 +319,27 @@
}
for (int index = elementCount - 1; index >= 0; --index) {
- if (list.get(index).isEmpty()) {
+ final RectF rectangle = extractor.apply(list.get(index));
+ if (rectangle.isEmpty()) {
list.remove(index);
}
}
- list.add(candidate);
+ list.add(packer.apply(candidate));
}
/** @hide */
@VisibleForTesting
- public static PointF movePointInsideNearestRectangle(final PointF point,
- final List<RectF> rectangles) {
+ public static <T> PointF movePointInsideNearestRectangle(final PointF point,
+ final List<T> list, final Function<T, RectF> extractor) {
float bestX = -1;
float bestY = -1;
double bestDistance = Double.MAX_VALUE;
- final int elementCount = rectangles.size();
+ final int elementCount = list.size();
for (int index = 0; index < elementCount; ++index) {
- final RectF rectangle = rectangles.get(index);
+ final RectF rectangle = extractor.apply(list.get(index));
final float candidateY = rectangle.centerY();
final float candidateX;
@@ -356,7 +377,9 @@
}
private void resetTextClassificationHelper() {
- mTextClassificationHelper.reset(mTextView.getTextClassifier(), mTextView.getText(),
+ mTextClassificationHelper.reset(
+ mTextView.getTextClassifier(),
+ getText(mTextView),
mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
mTextView.getTextLocales());
}
@@ -382,6 +405,7 @@
private int mSelectionStart;
private int mSelectionEnd;
private boolean mAllowReset;
+ private final LogAbandonRunnable mDelayedLogAbandon = new LogAbandonRunnable();
SelectionTracker(TextView textView) {
mTextView = Preconditions.checkNotNull(textView);
@@ -393,6 +417,10 @@
*/
public void onOriginalSelection(
CharSequence text, int selectionStart, int selectionEnd, boolean editableText) {
+ // If we abandoned a selection and created a new one very shortly after, we may still
+ // have a pending request to log ABANDON, which we flush here.
+ mDelayedLogAbandon.flush();
+
mOriginalStart = mSelectionStart = selectionStart;
mOriginalEnd = mSelectionEnd = selectionEnd;
mAllowReset = false;
@@ -433,12 +461,7 @@
public void onSelectionDestroyed() {
mAllowReset = false;
// Wait a few ms to see if the selection was destroyed because of a text change event.
- mTextView.postDelayed(() -> {
- mLogger.logSelectionAction(
- mSelectionStart, mSelectionEnd,
- SelectionEvent.ActionType.ABANDON, null /* classification */);
- mSelectionStart = mSelectionEnd = -1;
- }, 100 /* ms */);
+ mDelayedLogAbandon.schedule(100 /* ms */);
}
/**
@@ -465,7 +488,7 @@
if (isSelectionStarted()
&& mAllowReset
&& textIndex >= mSelectionStart && textIndex <= mSelectionEnd
- && textView.getText() instanceof Spannable) {
+ && getText(textView) instanceof Spannable) {
mAllowReset = false;
boolean selected = editor.selectCurrentWord();
if (selected) {
@@ -495,6 +518,38 @@
private boolean isSelectionStarted() {
return mSelectionStart >= 0 && mSelectionEnd >= 0 && mSelectionStart != mSelectionEnd;
}
+
+ /** A helper for keeping track of pending abandon logging requests. */
+ private final class LogAbandonRunnable implements Runnable {
+ private boolean mIsPending;
+
+ /** Schedules an abandon to be logged with the given delay. Flush if necessary. */
+ void schedule(int delayMillis) {
+ if (mIsPending) {
+ Log.e(LOG_TAG, "Force flushing abandon due to new scheduling request");
+ flush();
+ }
+ mIsPending = true;
+ mTextView.postDelayed(this, delayMillis);
+ }
+
+ /** If there is a pending log request, execute it now. */
+ void flush() {
+ mTextView.removeCallbacks(this);
+ run();
+ }
+
+ @Override
+ public void run() {
+ if (mIsPending) {
+ mLogger.logSelectionAction(
+ mSelectionStart, mSelectionEnd,
+ SelectionEvent.ActionType.ABANDON, null /* classification */);
+ mSelectionStart = mSelectionEnd = -1;
+ mIsPending = false;
+ }
+ }
+ }
}
// TODO: Write tests
@@ -689,7 +744,7 @@
mSelectionResultSupplier = Preconditions.checkNotNull(selectionResultSupplier);
mSelectionResultCallback = Preconditions.checkNotNull(selectionResultCallback);
// Make a copy of the original text.
- mOriginalText = mTextView.getText().toString();
+ mOriginalText = getText(mTextView).toString();
}
@Override
@@ -705,7 +760,7 @@
@Override
@UiThread
protected void onPostExecute(SelectionResult result) {
- result = TextUtils.equals(mOriginalText, mTextView.getText()) ? result : null;
+ result = TextUtils.equals(mOriginalText, getText(mTextView)) ? result : null;
mSelectionResultCallback.accept(result);
}
@@ -752,6 +807,9 @@
private LocaleList mLastClassificationLocales;
private SelectionResult mLastClassificationResult;
+ /** Whether the TextClassifier has been initialized. */
+ private boolean mHot;
+
TextClassificationHelper(TextClassifier textClassifier,
CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
reset(textClassifier, text, selectionStart, selectionEnd, locales);
@@ -771,11 +829,13 @@
@WorkerThread
public SelectionResult classifyText() {
+ mHot = true;
return performClassification(null /* selection */);
}
@WorkerThread
public SelectionResult suggestSelection() {
+ mHot = true;
trimText();
final TextSelection selection = mTextClassifier.suggestSelection(
mTrimmedText, mRelativeStart, mRelativeEnd, mLocales);
@@ -784,6 +844,22 @@
return performClassification(selection);
}
+ /**
+ * Maximum time (in milliseconds) to wait for a textclassifier result before timing out.
+ */
+ // TODO: Consider making this a ViewConfiguration.
+ public int getTimeoutDuration() {
+ if (mHot) {
+ return 200;
+ } else {
+ // Return a slightly larger number than usual when the TextClassifier is first
+ // initialized. Initialization would usually take longer than subsequent calls to
+ // the TextClassifier. The impact of this on the UI is that we do not show the
+ // selection handles or toolbar until after this timeout.
+ return 500;
+ }
+ }
+
private SelectionResult performClassification(@Nullable TextSelection selection) {
if (!Objects.equals(mText, mLastClassificationText)
|| mSelectionStart != mLastClassificationSelectionStart
@@ -854,4 +930,14 @@
return SelectionEvent.ActionType.OTHER;
}
}
+
+ private static CharSequence getText(TextView textView) {
+ // Extracts the textView's text.
+ // TODO: Investigate why/when TextView.getText() is null.
+ final CharSequence text = textView.getText();
+ if (text != null) {
+ return text;
+ }
+ return "";
+ }
}
diff --git a/core/java/android/widget/SmartSelectSprite.java b/core/java/android/widget/SmartSelectSprite.java
index 27b93bc..7cbc494b 100644
--- a/core/java/android/widget/SmartSelectSprite.java
+++ b/core/java/android/widget/SmartSelectSprite.java
@@ -35,6 +35,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.Shape;
+import android.text.Layout;
import android.util.TypedValue;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -42,9 +43,9 @@
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.LinkedList;
import java.util.List;
/**
@@ -76,6 +77,26 @@
private Drawable mExistingDrawable = null;
private RectangleList mExistingRectangleList = null;
+ static final class RectangleWithTextSelectionLayout {
+ private final RectF mRectangle;
+ @Layout.TextSelectionLayout
+ private final int mTextSelectionLayout;
+
+ RectangleWithTextSelectionLayout(RectF rectangle, int textSelectionLayout) {
+ mRectangle = Preconditions.checkNotNull(rectangle);
+ mTextSelectionLayout = textSelectionLayout;
+ }
+
+ public RectF getRectangle() {
+ return mRectangle;
+ }
+
+ @Layout.TextSelectionLayout
+ public int getTextSelectionLayout() {
+ return mTextSelectionLayout;
+ }
+ }
+
/**
* A rounded rectangle with a configurable corner radius and the ability to expand outside of
* its bounding rectangle and clip against it.
@@ -84,12 +105,23 @@
private static final String PROPERTY_ROUND_RATIO = "roundRatio";
+ /**
+ * The direction in which the rectangle will perform its expansion. A rectangle can expand
+ * from its left edge, its right edge or from the center (or, more precisely, the user's
+ * touch point). For example, in left-to-right text, a selection spanning two lines with the
+ * user's action being on the first line will have the top rectangle and expansion direction
+ * of CENTER, while the bottom one will have an expansion direction of RIGHT.
+ */
@Retention(SOURCE)
@IntDef({ExpansionDirection.LEFT, ExpansionDirection.CENTER, ExpansionDirection.RIGHT})
private @interface ExpansionDirection {
- int LEFT = 0;
- int CENTER = 1;
- int RIGHT = 2;
+ int LEFT = -1;
+ int CENTER = 0;
+ int RIGHT = 1;
+ }
+
+ private static @ExpansionDirection int invert(@ExpansionDirection int expansionDirection) {
+ return expansionDirection * -1;
}
@Retention(SOURCE)
@@ -114,20 +146,33 @@
private final RectF mClipRect = new RectF();
private final Path mClipPath = new Path();
- /** How far offset the left edge of the rectangle is from the bounding box. */
+ /** How offset the left edge of the rectangle is from the left side of the bounding box. */
private float mLeftBoundary = 0;
- /** How far offset the right edge of the rectangle is from the bounding box. */
+ /** How offset the right edge of the rectangle is from the left side of the bounding box. */
private float mRightBoundary = 0;
+ /** Whether the horizontal bounds are inverted (for RTL scenarios). */
+ private final boolean mInverted;
+
+ private final float mBoundingWidth;
+
private RoundedRectangleShape(
final RectF boundingRectangle,
final @ExpansionDirection int expansionDirection,
final @RectangleBorderType int rectangleBorderType,
+ final boolean inverted,
final float strokeWidth) {
mBoundingRectangle = new RectF(boundingRectangle);
- mExpansionDirection = expansionDirection;
+ mBoundingWidth = boundingRectangle.width();
mRectangleBorderType = rectangleBorderType;
mStrokeWidth = strokeWidth;
+ mInverted = inverted && expansionDirection != ExpansionDirection.CENTER;
+
+ if (inverted) {
+ mExpansionDirection = invert(expansionDirection);
+ } else {
+ mExpansionDirection = expansionDirection;
+ }
if (boundingRectangle.height() > boundingRectangle.width()) {
setRoundRatio(0.0f);
@@ -148,6 +193,10 @@
*/
@Override
public void draw(Canvas canvas, Paint paint) {
+ if (mLeftBoundary == mRightBoundary) {
+ return;
+ }
+
final float cornerRadius = getCornerRadius();
final float adjustedCornerRadius = getAdjustedCornerRadius();
@@ -173,7 +222,7 @@
canvas.save();
mClipRect.set(mBoundingRectangle);
- mClipRect.inset(-mStrokeWidth, -mStrokeWidth);
+ mClipRect.inset(-mStrokeWidth / 2, -mStrokeWidth / 2);
canvas.clipRect(mClipRect);
canvas.drawRoundRect(mDrawRect, adjustedCornerRadius, adjustedCornerRadius, paint);
canvas.restore();
@@ -190,20 +239,28 @@
canvas.restore();
}
- public void setRoundRatio(@FloatRange(from = 0.0, to = 1.0) final float roundRatio) {
+ void setRoundRatio(@FloatRange(from = 0.0, to = 1.0) final float roundRatio) {
mRoundRatio = roundRatio;
}
- public float getRoundRatio() {
+ float getRoundRatio() {
return mRoundRatio;
}
- private void setLeftBoundary(final float leftBoundary) {
- mLeftBoundary = leftBoundary;
+ private void setStartBoundary(final float startBoundary) {
+ if (mInverted) {
+ mRightBoundary = mBoundingWidth - startBoundary;
+ } else {
+ mLeftBoundary = startBoundary;
+ }
}
- private void setRightBoundary(final float rightBoundary) {
- mRightBoundary = rightBoundary;
+ private void setEndBoundary(final float endBoundary) {
+ if (mInverted) {
+ mLeftBoundary = mBoundingWidth - endBoundary;
+ } else {
+ mRightBoundary = endBoundary;
+ }
}
private float getCornerRadius() {
@@ -247,8 +304,8 @@
private @DisplayType int mDisplayType = DisplayType.RECTANGLES;
private RectangleList(final List<RoundedRectangleShape> rectangles) {
- mRectangles = new LinkedList<>(rectangles);
- mReversedRectangles = new LinkedList<>(rectangles);
+ mRectangles = new ArrayList<>(rectangles);
+ mReversedRectangles = new ArrayList<>(rectangles);
Collections.reverse(mReversedRectangles);
mOutlinePolygonPath = generateOutlinePolygonPath(rectangles);
}
@@ -258,11 +315,11 @@
for (RoundedRectangleShape rectangle : mReversedRectangles) {
final float rectangleLeftBoundary = boundarySoFar - rectangle.getBoundingWidth();
if (leftBoundary < rectangleLeftBoundary) {
- rectangle.setLeftBoundary(0);
+ rectangle.setStartBoundary(0);
} else if (leftBoundary > boundarySoFar) {
- rectangle.setLeftBoundary(rectangle.getBoundingWidth());
+ rectangle.setStartBoundary(rectangle.getBoundingWidth());
} else {
- rectangle.setLeftBoundary(
+ rectangle.setStartBoundary(
rectangle.getBoundingWidth() - boundarySoFar + leftBoundary);
}
@@ -275,11 +332,11 @@
for (RoundedRectangleShape rectangle : mRectangles) {
final float rectangleRightBoundary = rectangle.getBoundingWidth() + boundarySoFar;
if (rectangleRightBoundary < rightBoundary) {
- rectangle.setRightBoundary(rectangle.getBoundingWidth());
+ rectangle.setEndBoundary(rectangle.getBoundingWidth());
} else if (boundarySoFar > rightBoundary) {
- rectangle.setRightBoundary(0);
+ rectangle.setEndBoundary(0);
} else {
- rectangle.setRightBoundary(rightBoundary - boundarySoFar);
+ rectangle.setEndBoundary(rightBoundary - boundarySoFar);
}
boundarySoFar = rectangleRightBoundary;
@@ -331,8 +388,8 @@
}
/**
- * @param context The {@link Context} in which the animation will run
- * @param invalidator A {@link Runnable} which will be called every time the animation updates,
+ * @param context the {@link Context} in which the animation will run
+ * @param invalidator a {@link Runnable} which will be called every time the animation updates,
* indicating that the view drawing the animation should invalidate itself
*/
SmartSelectSprite(final Context context, final Runnable invalidator) {
@@ -356,42 +413,48 @@
* "selection" and finally join them into a single polygon. In
* order to get the correct visual behavior, these rectangles
* should be sorted according to {@link #RECTANGLE_COMPARATOR}.
- * @param onAnimationEnd The callback which will be invoked once the whole animation
- * completes.
+ * @param onAnimationEnd the callback which will be invoked once the whole animation
+ * completes
* @throws IllegalArgumentException if the given start point is not in any of the
- * destinationRectangles.
+ * destinationRectangles
* @see #cancelAnimation()
*/
+ // TODO nullability checks on parameters
public void startAnimation(
final PointF start,
- final List<RectF> destinationRectangles,
- final Runnable onAnimationEnd) throws IllegalArgumentException {
+ final List<RectangleWithTextSelectionLayout> destinationRectangles,
+ final Runnable onAnimationEnd) {
cancelAnimation();
final ValueAnimator.AnimatorUpdateListener updateListener =
valueAnimator -> mInvalidator.run();
- final List<RoundedRectangleShape> shapes = new LinkedList<>();
- final List<Animator> cornerAnimators = new LinkedList<>();
+ final int rectangleCount = destinationRectangles.size();
- final RectF centerRectangle = destinationRectangles
- .stream()
- .filter((r) -> contains(r, start))
- .findFirst()
- .orElseThrow(() -> new IllegalArgumentException(
- "Center point is not inside any of the rectangles!"));
+ final List<RoundedRectangleShape> shapes = new ArrayList<>(rectangleCount);
+ final List<Animator> cornerAnimators = new ArrayList<>(rectangleCount);
+
+ RectangleWithTextSelectionLayout centerRectangle = null;
int startingOffset = 0;
- for (RectF rectangle : destinationRectangles) {
- if (rectangle.equals(centerRectangle)) {
+ for (int index = 0; index < rectangleCount; ++index) {
+ final RectangleWithTextSelectionLayout rectangleWithTextSelectionLayout =
+ destinationRectangles.get(index);
+ final RectF rectangle = rectangleWithTextSelectionLayout.getRectangle();
+ if (contains(rectangle, start)) {
+ centerRectangle = rectangleWithTextSelectionLayout;
break;
}
startingOffset += rectangle.width();
}
- startingOffset += start.x - centerRectangle.left;
+ if (centerRectangle == null) {
+ throw new IllegalArgumentException("Center point is not inside any of the rectangles!");
+ }
- final float centerRectangleHalfHeight = centerRectangle.height() / 2;
+ startingOffset += start.x - centerRectangle.getRectangle().left;
+
+ final float centerRectangleHalfHeight = centerRectangle.getRectangle().height() / 2;
final float startingOffsetLeft = startingOffset - centerRectangleHalfHeight;
final float startingOffsetRight = startingOffset + centerRectangleHalfHeight;
@@ -399,19 +462,21 @@
generateDirections(centerRectangle, destinationRectangles);
final @RoundedRectangleShape.RectangleBorderType int[] rectangleBorderTypes =
- generateBorderTypes(destinationRectangles);
+ generateBorderTypes(rectangleCount);
- int index = 0;
-
- for (RectF rectangle : destinationRectangles) {
+ for (int index = 0; index < rectangleCount; ++index) {
+ final RectangleWithTextSelectionLayout rectangleWithTextSelectionLayout =
+ destinationRectangles.get(index);
+ final RectF rectangle = rectangleWithTextSelectionLayout.getRectangle();
final RoundedRectangleShape shape = new RoundedRectangleShape(
rectangle,
expansionDirections[index],
rectangleBorderTypes[index],
+ rectangleWithTextSelectionLayout.getTextSelectionLayout()
+ == Layout.TEXT_SELECTION_LAYOUT_RIGHT_TO_LEFT,
mStrokeWidth);
cornerAnimators.add(createCornerAnimator(shape, updateListener));
shapes.add(shape);
- index++;
}
final RectangleList rectangleList = new RectangleList(shapes);
@@ -511,7 +576,8 @@
}
private static @RoundedRectangleShape.ExpansionDirection int[] generateDirections(
- final RectF centerRectangle, final List<RectF> rectangles) {
+ final RectangleWithTextSelectionLayout centerRectangle,
+ final List<RectangleWithTextSelectionLayout> rectangles) {
final @RoundedRectangleShape.ExpansionDirection int[] result = new int[rectangles.size()];
final int centerRectangleIndex = rectangles.indexOf(centerRectangle);
@@ -538,8 +604,8 @@
}
private static @RoundedRectangleShape.RectangleBorderType int[] generateBorderTypes(
- final List<RectF> rectangles) {
- final @RoundedRectangleShape.RectangleBorderType int[] result = new int[rectangles.size()];
+ final int numberOfRectangles) {
+ final @RoundedRectangleShape.RectangleBorderType int[] result = new int[numberOfRectangles];
for (int i = 1; i < result.length - 1; ++i) {
result[i] = RoundedRectangleShape.RectangleBorderType.OVERSHOOT;
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 2e1e963..604575f 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -248,10 +248,7 @@
com.android.internal.R.styleable.Switch_switchPadding, 0);
mSplitTrack = a.getBoolean(com.android.internal.R.styleable.Switch_splitTrack, false);
- // TODO: replace CUR_DEVELOPMENT with P once P is added to android.os.Build.VERSION_CODES.
- // STOPSHIP if the above TODO is not done.
- mUseFallbackLineSpacing =
- context.getApplicationInfo().targetSdkVersion >= VERSION_CODES.CUR_DEVELOPMENT;
+ mUseFallbackLineSpacing = context.getApplicationInfo().targetSdkVersion >= VERSION_CODES.P;
ColorStateList thumbTintList = a.getColorStateList(
com.android.internal.R.styleable.Switch_thumbTint);
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 05f7c0a..f8b6837 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -61,7 +61,10 @@
// This value will be set to 0 as soon as the first tab is added to TabHost.
private int mSelectedTab = -1;
+ @Nullable
private Drawable mLeftStrip;
+
+ @Nullable
private Drawable mRightStrip;
private boolean mDrawBottomStrips = true;
@@ -374,23 +377,36 @@
final Drawable leftStrip = mLeftStrip;
final Drawable rightStrip = mRightStrip;
- leftStrip.setState(selectedChild.getDrawableState());
- rightStrip.setState(selectedChild.getDrawableState());
+ if (leftStrip != null) {
+ leftStrip.setState(selectedChild.getDrawableState());
+ }
+ if (rightStrip != null) {
+ rightStrip.setState(selectedChild.getDrawableState());
+ }
if (mStripMoved) {
final Rect bounds = mBounds;
bounds.left = selectedChild.getLeft();
bounds.right = selectedChild.getRight();
final int myHeight = getHeight();
- leftStrip.setBounds(Math.min(0, bounds.left - leftStrip.getIntrinsicWidth()),
- myHeight - leftStrip.getIntrinsicHeight(), bounds.left, myHeight);
- rightStrip.setBounds(bounds.right, myHeight - rightStrip.getIntrinsicHeight(),
- Math.max(getWidth(), bounds.right + rightStrip.getIntrinsicWidth()), myHeight);
+ if (leftStrip != null) {
+ leftStrip.setBounds(Math.min(0, bounds.left - leftStrip.getIntrinsicWidth()),
+ myHeight - leftStrip.getIntrinsicHeight(), bounds.left, myHeight);
+ }
+ if (rightStrip != null) {
+ rightStrip.setBounds(bounds.right, myHeight - rightStrip.getIntrinsicHeight(),
+ Math.max(getWidth(), bounds.right + rightStrip.getIntrinsicWidth()),
+ myHeight);
+ }
mStripMoved = false;
}
- leftStrip.draw(canvas);
- rightStrip.draw(canvas);
+ if (leftStrip != null) {
+ leftStrip.draw(canvas);
+ }
+ if (rightStrip != null) {
+ rightStrip.draw(canvas);
+ }
}
/**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2de5527..24ae03c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1256,9 +1256,7 @@
final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
mUseInternationalizedInput = targetSdkVersion >= VERSION_CODES.O;
- // TODO: replace CUR_DEVELOPMENT with P once P is added to android.os.Build.VERSION_CODES.
- // STOPSHIP if the above TODO is not done.
- mUseFallbackLineSpacing = targetSdkVersion >= VERSION_CODES.CUR_DEVELOPMENT;
+ mUseFallbackLineSpacing = targetSdkVersion >= VERSION_CODES.P;
if (inputMethod != null) {
Class<?> c;
@@ -6283,7 +6281,7 @@
final int horizontalPadding = getCompoundPaddingLeft();
final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true);
- if (mEditor.mCursorDrawable == null) {
+ if (mEditor.mDrawableForCursor == null) {
synchronized (TEMP_RECTF) {
/*
* The reason for this concern about the thickness of the
@@ -6310,7 +6308,7 @@
(int) Math.ceil(verticalPadding + TEMP_RECTF.bottom + thick));
}
} else {
- final Rect bounds = mEditor.mCursorDrawable.getBounds();
+ final Rect bounds = mEditor.mDrawableForCursor.getBounds();
invalidate(bounds.left + horizontalPadding, bounds.top + verticalPadding,
bounds.right + horizontalPadding, bounds.bottom + verticalPadding);
}
@@ -6362,8 +6360,8 @@
int bottom = mLayout.getLineBottom(lineEnd);
// mEditor can be null in case selection is set programmatically.
- if (invalidateCursor && mEditor != null && mEditor.mCursorDrawable != null) {
- final Rect bounds = mEditor.mCursorDrawable.getBounds();
+ if (invalidateCursor && mEditor != null && mEditor.mDrawableForCursor != null) {
+ final Rect bounds = mEditor.mDrawableForCursor.getBounds();
top = Math.min(top, bounds.top);
bottom = Math.max(bottom, bounds.bottom);
}
diff --git a/core/java/com/android/internal/alsa/AlsaDevicesParser.java b/core/java/com/android/internal/alsa/AlsaDevicesParser.java
index 7cdd897..6e3d596 100644
--- a/core/java/com/android/internal/alsa/AlsaDevicesParser.java
+++ b/core/java/com/android/internal/alsa/AlsaDevicesParser.java
@@ -258,7 +258,7 @@
return line.charAt(kIndex_CardDeviceField) == '[';
}
- public void scan() {
+ public boolean scan() {
mDeviceRecords.clear();
File devicesFile = new File(kDevicesFilePath);
@@ -274,11 +274,13 @@
}
}
reader.close();
+ return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
+ return false;
}
//
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 2cab009..6e0ba341 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -100,7 +100,7 @@
private static final boolean DEBUG = false;
private static final int QUERY_TARGET_SERVICE_LIMIT = 5;
- private static final int WATCHDOG_TIMEOUT_MILLIS = 5000;
+ private static final int WATCHDOG_TIMEOUT_MILLIS = 2000;
private Bundle mReplacementExtras;
private IntentSender mChosenComponentSender;
@@ -1450,11 +1450,16 @@
getFirstRowPosition(rowPosition + 1));
int serviceSpacing = holder.row.getContext().getResources()
.getDimensionPixelSize(R.dimen.chooser_service_spacing);
- int top = rowPosition == 0 ? serviceSpacing : 0;
- if (nextStartType != ChooserListAdapter.TARGET_SERVICE) {
- setVertPadding(holder, top, serviceSpacing);
+ if (rowPosition == 0 && nextStartType != ChooserListAdapter.TARGET_SERVICE) {
+ // if the row is the only row for target service
+ setVertPadding(holder, 0, 0);
} else {
- setVertPadding(holder, top, 0);
+ int top = rowPosition == 0 ? serviceSpacing : 0;
+ if (nextStartType != ChooserListAdapter.TARGET_SERVICE) {
+ setVertPadding(holder, top, serviceSpacing);
+ } else {
+ setVertPadding(holder, top, 0);
+ }
}
} else {
holder.row.setBackgroundColor(Color.TRANSPARENT);
@@ -1580,8 +1585,8 @@
} catch (RemoteException e) {
Log.e(TAG, "Querying ChooserTargetService " + name + " failed.", e);
mChooserActivity.unbindService(this);
- destroy();
mChooserActivity.mServiceConnections.remove(this);
+ destroy();
}
}
}
@@ -1597,7 +1602,6 @@
}
mChooserActivity.unbindService(this);
- destroy();
mChooserActivity.mServiceConnections.remove(this);
if (mChooserActivity.mServiceConnections.isEmpty()) {
mChooserActivity.mChooserHandler.removeMessages(
@@ -1605,6 +1609,7 @@
mChooserActivity.sendVoiceChoicesIfNeeded();
}
mConnectedComponent = null;
+ destroy();
}
}
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index 36e4c1c6..ef30cd1 100644
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -21,12 +21,10 @@
import android.content.res.ObbInfo;
interface IMediaContainerService {
- String copyPackageToContainer(String packagePath, String containerId, String key,
- boolean isExternal, boolean isForwardLocked, String abiOverride);
int copyPackage(String packagePath, in IParcelFileDescriptorFactory target);
PackageInfoLite getMinimalPackageInfo(String packagePath, int flags, String abiOverride);
ObbInfo getObbInfo(String filename);
void clearDirectory(String directory);
- long calculateInstalledSize(String packagePath, boolean isForwardLocked, String abiOverride);
+ long calculateInstalledSize(String packagePath, String abiOverride);
}
diff --git a/core/java/com/android/internal/app/ShutdownActivity.java b/core/java/com/android/internal/app/ShutdownActivity.java
index 745d28f..f81e838 100644
--- a/core/java/com/android/internal/app/ShutdownActivity.java
+++ b/core/java/com/android/internal/app/ShutdownActivity.java
@@ -41,6 +41,9 @@
mReboot = Intent.ACTION_REBOOT.equals(intent.getAction());
mConfirm = intent.getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
mUserRequested = intent.getBooleanExtra(Intent.EXTRA_USER_REQUESTED_SHUTDOWN, false);
+ final String reason = mUserRequested
+ ? PowerManager.SHUTDOWN_USER_REQUESTED
+ : intent.getStringExtra(Intent.EXTRA_REASON);
Slog.i(TAG, "onCreate(): confirm=" + mConfirm);
Thread thr = new Thread("ShutdownActivity") {
@@ -52,9 +55,7 @@
if (mReboot) {
pm.reboot(mConfirm, null, false);
} else {
- pm.shutdown(mConfirm,
- mUserRequested ? PowerManager.SHUTDOWN_USER_REQUESTED : null,
- false);
+ pm.shutdown(mConfirm, reason, false);
}
} catch (RemoteException e) {
}
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
index ebedc89..0bc8c483 100644
--- a/core/java/com/android/internal/app/procstats/DumpUtils.java
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -29,6 +29,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import static com.android.internal.app.procstats.ProcessStats.*;
@@ -66,6 +67,8 @@
"cch-activity", "cch-aclient", "cch-empty"
};
+ // State enum is defined in frameworks/base/core/proto/android/service/procstats.proto
+ // Update states must sync enum definition as well, the ordering must not be changed.
static final String[] ADJ_SCREEN_TAGS = new String[] {
"0", "1"
};
@@ -177,6 +180,13 @@
printArrayEntry(pw, STATE_TAGS, state, 1);
}
+ public static void printProcStateTagProto(ProtoOutputStream proto, long screenId, long memId,
+ long stateId, int state) {
+ state = printProto(proto, screenId, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD * STATE_COUNT);
+ state = printProto(proto, memId, ADJ_MEM_TAGS, state, STATE_COUNT);
+ printProto(proto, stateId, STATE_TAGS, state, 1);
+ }
+
public static void printAdjTag(PrintWriter pw, int state) {
state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD);
printArrayEntry(pw, ADJ_MEM_TAGS, state, 1);
@@ -352,6 +362,15 @@
return value - index*mod;
}
+ public static int printProto(ProtoOutputStream proto, long fieldId, String[] array, int value, int mod) {
+ int index = value/mod;
+ if (index >= 0 && index < array.length) {
+ // Valid state enum number starts at 1, 0 stands for unknown.
+ proto.write(fieldId, index + 1);
+ } // else enum default is always zero in proto3
+ return value - index*mod;
+ }
+
public static String collapseString(String pkgName, String itemName) {
if (itemName.startsWith(pkgName)) {
final int ITEMLEN = itemName.length();
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index e0a4053..7519fce 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -21,6 +21,8 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.service.pm.PackageProto;
+import android.service.procstats.ProcessStatsProto;
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -29,6 +31,8 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.app.procstats.ProcessStats.PackageState;
@@ -69,6 +73,9 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
public final class ProcessState {
@@ -1157,6 +1164,7 @@
}
}
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("ProcessState{").append(Integer.toHexString(System.identityHashCode(this)))
@@ -1167,4 +1175,76 @@
sb.append("}");
return sb.toString();
}
+
+ public void toProto(ProtoOutputStream proto, String procName, int uid, long now) {
+ proto.write(ProcessStatsProto.PROCESS, procName);
+ proto.write(ProcessStatsProto.UID, uid);
+ if (mNumExcessiveCpu > 0 || mNumCachedKill > 0 ) {
+ final long killToken = proto.start(ProcessStatsProto.KILL);
+ proto.write(ProcessStatsProto.Kill.CPU, mNumExcessiveCpu);
+ proto.write(ProcessStatsProto.Kill.CACHED, mNumCachedKill);
+ ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.Kill.CACHED_PSS,
+ mMinCachedKillPss, mAvgCachedKillPss, mMaxCachedKillPss);
+ proto.end(killToken);
+ }
+
+ // Group proc stats by type (screen state + mem state + process state)
+ Map<Integer, Long> durationByState = new HashMap<>();
+ boolean didCurState = false;
+ for (int i=0; i<mDurations.getKeyCount(); i++) {
+ final int key = mDurations.getKeyAt(i);
+ final int type = SparseMappingTable.getIdFromKey(key);
+ long time = mDurations.getValue(key);
+ if (mCurState == type) {
+ didCurState = true;
+ time += now - mStartTime;
+ }
+ durationByState.put(type, time);
+ }
+ if (!didCurState && mCurState != STATE_NOTHING) {
+ durationByState.put(mCurState, now - mStartTime);
+ }
+
+ for (int i=0; i<mPssTable.getKeyCount(); i++) {
+ final int key = mPssTable.getKeyAt(i);
+ final int type = SparseMappingTable.getIdFromKey(key);
+ if (!durationByState.containsKey(type)) {
+ // state without duration should not have stats!
+ continue;
+ }
+ final long stateToken = proto.start(ProcessStatsProto.STATES);
+ DumpUtils.printProcStateTagProto(proto,
+ ProcessStatsProto.State.SCREEN_STATE,
+ ProcessStatsProto.State.MEMORY_STATE,
+ ProcessStatsProto.State.PROCESS_STATE,
+ type);
+
+ long duration = durationByState.get(type);
+ durationByState.remove(type); // remove the key since it is already being dumped.
+ proto.write(ProcessStatsProto.State.DURATION_MS, duration);
+
+ proto.write(ProcessStatsProto.State.SAMPLE_SIZE, mPssTable.getValue(key, PSS_SAMPLE_COUNT));
+ ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.PSS,
+ mPssTable.getValue(key, PSS_MINIMUM),
+ mPssTable.getValue(key, PSS_AVERAGE),
+ mPssTable.getValue(key, PSS_MAXIMUM));
+ ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.USS,
+ mPssTable.getValue(key, PSS_USS_MINIMUM),
+ mPssTable.getValue(key, PSS_USS_AVERAGE),
+ mPssTable.getValue(key, PSS_USS_MAXIMUM));
+
+ proto.end(stateToken);
+ }
+
+ for (Map.Entry<Integer, Long> entry : durationByState.entrySet()) {
+ final long stateToken = proto.start(ProcessStatsProto.STATES);
+ DumpUtils.printProcStateTagProto(proto,
+ ProcessStatsProto.State.SCREEN_STATE,
+ ProcessStatsProto.State.MEMORY_STATE,
+ ProcessStatsProto.State.PROCESS_STATE,
+ entry.getKey());
+ proto.write(ProcessStatsProto.State.DURATION_MS, entry.getValue());
+ proto.end(stateToken);
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 35b53c2..14f5e5b 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -22,6 +22,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.service.procstats.ProcessStatsSectionProto;
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -30,6 +31,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.procstats.DurationsTable;
@@ -1706,6 +1708,46 @@
}
}
+ public void toProto(ProtoOutputStream proto, long now) {
+ final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+
+ proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime);
+ proto.write(ProcessStatsSectionProto.END_REALTIME_MS,
+ mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
+ proto.write(ProcessStatsSectionProto.START_UPTIME_MS, mTimePeriodStartUptime);
+ proto.write(ProcessStatsSectionProto.END_UPTIME_MS, mTimePeriodEndUptime);
+ proto.write(ProcessStatsSectionProto.RUNTIME, mRuntime);
+ proto.write(ProcessStatsSectionProto.HAS_SWAPPED_PSS, mHasSwappedOutPss);
+ boolean partial = true;
+ if ((mFlags&FLAG_SHUTDOWN) != 0) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SHUTDOWN);
+ partial = false;
+ }
+ if ((mFlags&FLAG_SYSPROPS) != 0) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SYSPROPS);
+ partial = false;
+ }
+ if ((mFlags&FLAG_COMPLETE) != 0) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_COMPLETE);
+ partial = false;
+ }
+ if (partial) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_PARTIAL);
+ }
+
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ for (int ip=0; ip<procMap.size(); ip++) {
+ String procName = procMap.keyAt(ip);
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ final int uid = uids.keyAt(iu);
+ final ProcessState procState = uids.valueAt(iu);
+ final long processStateToken = proto.start(ProcessStatsSectionProto.PROCESS_STATS);
+ procState.toProto(proto, procName, uid, now);
+ proto.end(processStateToken);
+ }
+ }
+ }
final public static class ProcessStateHolder {
public final int appVersion;
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index e923223..59a7995 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -16,7 +16,6 @@
package com.android.internal.content;
-import static android.net.TrafficStats.MB_IN_BYTES;
import static android.os.storage.VolumeInfo.ID_PRIVATE_INTERNAL;
import android.content.Context;
@@ -27,13 +26,11 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageParser.PackageLite;
import android.os.Environment;
-import android.os.FileUtils;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
-import android.os.storage.StorageResultCode;
import android.os.storage.StorageVolume;
import android.os.storage.VolumeInfo;
import android.provider.Settings;
@@ -45,15 +42,9 @@
import libcore.io.IoUtils;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.util.Collections;
import java.util.Objects;
import java.util.UUID;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipOutputStream;
/**
* Constants used internally between the PackageManager
@@ -72,7 +63,6 @@
public static final int RECOMMEND_FAILED_INVALID_URI = -6;
public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7;
- private static final boolean localLOGV = false;
private static final String TAG = "PackageHelper";
// App installation location settings values
public static final int APP_INSTALL_AUTO = 0;
@@ -91,259 +81,6 @@
}
}
- public static String createSdDir(long sizeBytes, String cid, String sdEncKey, int uid,
- boolean isExternal) {
- // Round up to nearest MB, plus another MB for filesystem overhead
- final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1;
- try {
- IStorageManager storageManager = getStorageManager();
-
- if (localLOGV)
- Log.i(TAG, "Size of container " + sizeMb + " MB");
-
- int rc = storageManager.createSecureContainer(cid, sizeMb, "ext4", sdEncKey, uid,
- isExternal);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, "Failed to create secure container " + cid);
- return null;
- }
- String cachePath = storageManager.getSecureContainerPath(cid);
- if (localLOGV) Log.i(TAG, "Created secure container " + cid +
- " at " + cachePath);
- return cachePath;
- } catch (RemoteException e) {
- Log.e(TAG, "StorageManagerService running?");
- }
- return null;
- }
-
- public static boolean resizeSdDir(long sizeBytes, String cid, String sdEncKey) {
- // Round up to nearest MB, plus another MB for filesystem overhead
- final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1;
- try {
- IStorageManager storageManager = getStorageManager();
- int rc = storageManager.resizeSecureContainer(cid, sizeMb, sdEncKey);
- if (rc == StorageResultCode.OperationSucceeded) {
- return true;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "StorageManagerService running?");
- }
- Log.e(TAG, "Failed to create secure container " + cid);
- return false;
- }
-
- public static String mountSdDir(String cid, String key, int ownerUid) {
- return mountSdDir(cid, key, ownerUid, true);
- }
-
- public static String mountSdDir(String cid, String key, int ownerUid, boolean readOnly) {
- try {
- int rc = getStorageManager().mountSecureContainer(cid, key, ownerUid, readOnly);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.i(TAG, "Failed to mount container " + cid + " rc : " + rc);
- return null;
- }
- return getStorageManager().getSecureContainerPath(cid);
- } catch (RemoteException e) {
- Log.e(TAG, "StorageManagerService running?");
- }
- return null;
- }
-
- public static boolean unMountSdDir(String cid) {
- try {
- int rc = getStorageManager().unmountSecureContainer(cid, true);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, "Failed to unmount " + cid + " with rc " + rc);
- return false;
- }
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, "StorageManagerService running?");
- }
- return false;
- }
-
- public static boolean renameSdDir(String oldId, String newId) {
- try {
- int rc = getStorageManager().renameSecureContainer(oldId, newId);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, "Failed to rename " + oldId + " to " +
- newId + "with rc " + rc);
- return false;
- }
- return true;
- } catch (RemoteException e) {
- Log.i(TAG, "Failed ot rename " + oldId + " to " + newId +
- " with exception : " + e);
- }
- return false;
- }
-
- public static String getSdDir(String cid) {
- try {
- return getStorageManager().getSecureContainerPath(cid);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get container path for " + cid +
- " with exception " + e);
- }
- return null;
- }
-
- public static String getSdFilesystem(String cid) {
- try {
- return getStorageManager().getSecureContainerFilesystemPath(cid);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get container path for " + cid +
- " with exception " + e);
- }
- return null;
- }
-
- public static boolean finalizeSdDir(String cid) {
- try {
- int rc = getStorageManager().finalizeSecureContainer(cid);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.i(TAG, "Failed to finalize container " + cid);
- return false;
- }
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to finalize container " + cid +
- " with exception " + e);
- }
- return false;
- }
-
- public static boolean destroySdDir(String cid) {
- try {
- if (localLOGV) Log.i(TAG, "Forcibly destroying container " + cid);
- int rc = getStorageManager().destroySecureContainer(cid, true);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.i(TAG, "Failed to destroy container " + cid);
- return false;
- }
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to destroy container " + cid +
- " with exception " + e);
- }
- return false;
- }
-
- public static String[] getSecureContainerList() {
- try {
- return getStorageManager().getSecureContainerList();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get secure container list with exception" +
- e);
- }
- return null;
- }
-
- public static boolean isContainerMounted(String cid) {
- try {
- return getStorageManager().isSecureContainerMounted(cid);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to find out if container " + cid + " mounted");
- }
- return false;
- }
-
- /**
- * Extract public files for the single given APK.
- */
- public static long extractPublicFiles(File apkFile, File publicZipFile)
- throws IOException {
- final FileOutputStream fstr;
- final ZipOutputStream publicZipOutStream;
-
- if (publicZipFile == null) {
- fstr = null;
- publicZipOutStream = null;
- } else {
- fstr = new FileOutputStream(publicZipFile);
- publicZipOutStream = new ZipOutputStream(fstr);
- Log.d(TAG, "Extracting " + apkFile + " to " + publicZipFile);
- }
-
- long size = 0L;
-
- try {
- final ZipFile privateZip = new ZipFile(apkFile.getAbsolutePath());
- try {
- // Copy manifest, resources.arsc and res directory to public zip
- for (final ZipEntry zipEntry : Collections.list(privateZip.entries())) {
- final String zipEntryName = zipEntry.getName();
- if ("AndroidManifest.xml".equals(zipEntryName)
- || "resources.arsc".equals(zipEntryName)
- || zipEntryName.startsWith("res/")) {
- size += zipEntry.getSize();
- if (publicZipFile != null) {
- copyZipEntry(zipEntry, privateZip, publicZipOutStream);
- }
- }
- }
- } finally {
- try { privateZip.close(); } catch (IOException e) {}
- }
-
- if (publicZipFile != null) {
- publicZipOutStream.finish();
- publicZipOutStream.flush();
- FileUtils.sync(fstr);
- publicZipOutStream.close();
- FileUtils.setPermissions(publicZipFile.getAbsolutePath(), FileUtils.S_IRUSR
- | FileUtils.S_IWUSR | FileUtils.S_IRGRP | FileUtils.S_IROTH, -1, -1);
- }
- } finally {
- IoUtils.closeQuietly(publicZipOutStream);
- }
-
- return size;
- }
-
- private static void copyZipEntry(ZipEntry zipEntry, ZipFile inZipFile,
- ZipOutputStream outZipStream) throws IOException {
- byte[] buffer = new byte[4096];
- int num;
-
- ZipEntry newEntry;
- if (zipEntry.getMethod() == ZipEntry.STORED) {
- // Preserve the STORED method of the input entry.
- newEntry = new ZipEntry(zipEntry);
- } else {
- // Create a new entry so that the compressed len is recomputed.
- newEntry = new ZipEntry(zipEntry.getName());
- }
- outZipStream.putNextEntry(newEntry);
-
- final InputStream data = inZipFile.getInputStream(zipEntry);
- try {
- while ((num = data.read(buffer)) > 0) {
- outZipStream.write(buffer, 0, num);
- }
- outZipStream.flush();
- } finally {
- IoUtils.closeQuietly(data);
- }
- }
-
- public static boolean fixSdPermissions(String cid, int gid, String filename) {
- try {
- int rc = getStorageManager().fixPermissionsSecureContainer(cid, gid, filename);
- if (rc != StorageResultCode.OperationSucceeded) {
- Log.i(TAG, "Failed to fixperms container " + cid);
- return false;
- }
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to fixperms container " + cid + " with exception " + e);
- }
- return false;
- }
-
/**
* A group of external dependencies used in
* {@link #resolveInstallVolume(Context, String, int, long)}. It can be backed by real values
@@ -638,29 +375,37 @@
return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
+ @Deprecated
public static long calculateInstalledSize(PackageLite pkg, boolean isForwardLocked,
String abiOverride) throws IOException {
+ return calculateInstalledSize(pkg, abiOverride);
+ }
+
+ public static long calculateInstalledSize(PackageLite pkg, String abiOverride)
+ throws IOException {
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(pkg);
- return calculateInstalledSize(pkg, handle, isForwardLocked, abiOverride);
+ return calculateInstalledSize(pkg, handle, abiOverride);
} finally {
IoUtils.closeQuietly(handle);
}
}
+ @Deprecated
+ public static long calculateInstalledSize(PackageLite pkg, boolean isForwardLocked,
+ NativeLibraryHelper.Handle handle, String abiOverride) throws IOException {
+ return calculateInstalledSize(pkg, handle, abiOverride);
+ }
+
public static long calculateInstalledSize(PackageLite pkg, NativeLibraryHelper.Handle handle,
- boolean isForwardLocked, String abiOverride) throws IOException {
+ String abiOverride) throws IOException {
long sizeBytes = 0;
// Include raw APKs, and possibly unpacked resources
for (String codePath : pkg.getAllCodePaths()) {
final File codeFile = new File(codePath);
sizeBytes += codeFile.length();
-
- if (isForwardLocked) {
- sizeBytes += PackageHelper.extractPublicFiles(codeFile, null);
- }
}
// Include all relevant native code
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 0bd2981..36fd991 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -119,7 +119,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 166 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 167 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS;
@@ -341,8 +341,8 @@
protected final TimeBase mOnBatteryTimeBase = new TimeBase();
// These are the objects that will want to do something when the device
- // is unplugged from power *and* the screen is off.
- final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase();
+ // is unplugged from power *and* the screen is off or doze.
+ protected final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase();
// Set to true when we want to distribute CPU across wakelocks for the next
// CPU update, even if we aren't currently running wake locks.
@@ -436,8 +436,12 @@
public boolean mRecordAllHistory;
boolean mNoAutoReset;
- int mScreenState = Display.STATE_UNKNOWN;
- StopwatchTimer mScreenOnTimer;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected int mScreenState = Display.STATE_UNKNOWN;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected StopwatchTimer mScreenOnTimer;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected StopwatchTimer mScreenDozeTimer;
int mScreenBrightnessBin = -1;
final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -583,12 +587,16 @@
int mHighDischargeAmountSinceCharge;
int mDischargeScreenOnUnplugLevel;
int mDischargeScreenOffUnplugLevel;
+ int mDischargeScreenDozeUnplugLevel;
int mDischargeAmountScreenOn;
int mDischargeAmountScreenOnSinceCharge;
int mDischargeAmountScreenOff;
int mDischargeAmountScreenOffSinceCharge;
+ int mDischargeAmountScreenDoze;
+ int mDischargeAmountScreenDozeSinceCharge;
private LongSamplingCounter mDischargeScreenOffCounter;
+ private LongSamplingCounter mDischargeScreenDozeCounter;
private LongSamplingCounter mDischargeCounter;
static final int MAX_LEVEL_STEPS = 200;
@@ -673,13 +681,18 @@
}
@Override
- public LongCounter getDischargeScreenOffCoulombCounter() {
- return mDischargeScreenOffCounter;
+ public long getMahDischarge(int which) {
+ return mDischargeCounter.getCountLocked(which);
}
@Override
- public LongCounter getDischargeCoulombCounter() {
- return mDischargeCounter;
+ public long getMahDischargeScreenOff(int which) {
+ return mDischargeScreenOffCounter.getCountLocked(which);
+ }
+
+ @Override
+ public long getMahDischargeScreenDoze(int which) {
+ return mDischargeScreenDozeCounter.getCountLocked(which);
}
@Override
@@ -3573,8 +3586,9 @@
mActiveHistoryStates2 = 0xffffffff;
}
- public void updateTimeBasesLocked(boolean unplugged, boolean screenOff, long uptime,
+ public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptime,
long realtime) {
+ final boolean screenOff = isScreenOff(screenState) || isScreenDoze(screenState);
final boolean updateOnBatteryTimeBase = unplugged != mOnBatteryTimeBase.isRunning();
final boolean updateOnBatteryScreenOffTimeBase =
(unplugged && screenOff) != mOnBatteryScreenOffTimeBase.isRunning();
@@ -3591,20 +3605,22 @@
updateRpmStatsLocked(); // if either OnBattery or OnBatteryScreenOff timebase changes.
}
if (DEBUG_ENERGY_CPU) {
- Slog.d(TAG, "Updating cpu time because screen is now " + (screenOff ? "off" : "on")
+ Slog.d(TAG, "Updating cpu time because screen is now "
+ + Display.stateToString(screenState)
+ " and battery is " + (unplugged ? "on" : "off"));
}
updateCpuTimeLocked();
mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime);
- mOnBatteryScreenOffTimeBase.setRunning(unplugged && screenOff, uptime, realtime);
- for (int i = mUidStats.size() - 1; i >= 0; --i) {
- final Uid u = mUidStats.valueAt(i);
- if (updateOnBatteryTimeBase) {
- u.updateOnBatteryBgTimeBase(uptime, realtime);
+ if (updateOnBatteryTimeBase) {
+ for (int i = mUidStats.size() - 1; i >= 0; --i) {
+ mUidStats.valueAt(i).updateOnBatteryBgTimeBase(uptime, realtime);
}
- if (updateOnBatteryScreenOffTimeBase) {
- u.updateOnBatteryScreenOffBgTimeBase(uptime, realtime);
+ }
+ if (updateOnBatteryScreenOffTimeBase) {
+ mOnBatteryScreenOffTimeBase.setRunning(unplugged && screenOff, uptime, realtime);
+ for (int i = mUidStats.size() - 1; i >= 0; --i) {
+ mUidStats.valueAt(i).updateOnBatteryScreenOffBgTimeBase(uptime, realtime);
}
}
}
@@ -3864,8 +3880,10 @@
}
public void setPretendScreenOff(boolean pretendScreenOff) {
- mPretendScreenOff = pretendScreenOff;
- noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON);
+ if (mPretendScreenOff != pretendScreenOff) {
+ mPretendScreenOff = pretendScreenOff;
+ noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON);
+ }
}
private String mInitialAcquireWakeName;
@@ -4195,54 +4213,58 @@
}
}
- if (state == Display.STATE_ON) {
- // Screen turning on.
- final long elapsedRealtime = mClocks.elapsedRealtime();
- final long uptime = mClocks.uptimeMillis();
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ final long uptime = mClocks.uptimeMillis();
+
+ boolean updateHistory = false;
+ if (isScreenDoze(state)) {
+ mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
+ mScreenDozeTimer.startRunningLocked(elapsedRealtime);
+ updateHistory = true;
+ } else if (isScreenDoze(oldState)) {
+ mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG;
+ mScreenDozeTimer.stopRunningLocked(elapsedRealtime);
+ updateHistory = true;
+ }
+ if (isScreenOn(state)) {
mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime, uptime);
mScreenOnTimer.startRunningLocked(elapsedRealtime);
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(elapsedRealtime);
}
-
- updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), false,
- mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
-
- // Fake a wake lock, so we consider the device waked as long
- // as the screen is on.
- noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false,
- elapsedRealtime, uptime);
-
- // Update discharge amounts.
- if (mOnBatteryInternal) {
- updateDischargeScreenLevelsLocked(false, true);
- }
- } else if (oldState == Display.STATE_ON) {
- // Screen turning off or dozing.
- final long elapsedRealtime = mClocks.elapsedRealtime();
- final long uptime = mClocks.uptimeMillis();
+ updateHistory = true;
+ } else if (isScreenOn(oldState)) {
mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime, uptime);
mScreenOnTimer.stopRunningLocked(elapsedRealtime);
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
}
-
+ updateHistory = true;
+ }
+ if (updateHistory) {
+ if (DEBUG_HISTORY) Slog.v(TAG, "Screen state to: "
+ + Display.stateToString(state));
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ }
+ if (isScreenOn(state)) {
+ updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state,
+ mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
+ // Fake a wake lock, so we consider the device waked as long as the screen is on.
+ noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false,
+ elapsedRealtime, uptime);
+ } else if (isScreenOn(oldState)) {
noteStopWakeLocked(-1, -1, "screen", "screen", WAKE_TYPE_PARTIAL,
elapsedRealtime, uptime);
-
- updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), true,
+ updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state,
mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
-
- // Update discharge amounts.
- if (mOnBatteryInternal) {
- updateDischargeScreenLevelsLocked(true, false);
- }
+ }
+ // Update discharge amounts.
+ if (mOnBatteryInternal) {
+ updateDischargeScreenLevelsLocked(oldState, state);
}
}
}
@@ -5391,6 +5413,14 @@
return mScreenOnTimer.getCountLocked(which);
}
+ @Override public long getScreenDozeTime(long elapsedRealtimeUs, int which) {
+ return mScreenDozeTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+
+ @Override public int getScreenDozeCount(int which) {
+ return mScreenDozeTimer.getCountLocked(which);
+ }
+
@Override public long getScreenBrightnessTime(int brightnessBin,
long elapsedRealtimeUs, int which) {
return mScreenBrightnessTimer[brightnessBin].getTotalTimeLocked(
@@ -8829,6 +8859,7 @@
mHandler = new MyHandler(handler.getLooper());
mStartCount++;
mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
+ mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i] = new StopwatchTimer(mClocks, null, -100-i, null,
mOnBatteryTimeBase);
@@ -8887,6 +8918,7 @@
mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase);
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
+ mDischargeScreenDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mOnBattery = mOnBatteryInternal = false;
long uptime = mClocks.uptimeMillis() * 1000;
@@ -9430,8 +9462,16 @@
return mCharging;
}
- public boolean isScreenOn() {
- return mScreenState == Display.STATE_ON;
+ public boolean isScreenOn(int state) {
+ return state == Display.STATE_ON;
+ }
+
+ public boolean isScreenOff(int state) {
+ return state == Display.STATE_OFF;
+ }
+
+ public boolean isScreenDoze(int state) {
+ return state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND;
}
void initTimes(long uptime, long realtime) {
@@ -9451,9 +9491,12 @@
mDischargeAmountScreenOnSinceCharge = 0;
mDischargeAmountScreenOff = 0;
mDischargeAmountScreenOffSinceCharge = 0;
+ mDischargeAmountScreenDoze = 0;
+ mDischargeAmountScreenDozeSinceCharge = 0;
mDischargeStepTracker.init();
mChargeStepTracker.init();
mDischargeScreenOffCounter.reset(false);
+ mDischargeScreenDozeCounter.reset(false);
mDischargeCounter.reset(false);
}
@@ -9471,15 +9514,22 @@
mOnBatteryTimeBase.reset(uptime, realtime);
mOnBatteryScreenOffTimeBase.reset(uptime, realtime);
if ((mHistoryCur.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) == 0) {
- if (mScreenState == Display.STATE_ON) {
+ if (isScreenOn(mScreenState)) {
mDischargeScreenOnUnplugLevel = mHistoryCur.batteryLevel;
+ mDischargeScreenDozeUnplugLevel = 0;
+ mDischargeScreenOffUnplugLevel = 0;
+ } else if (isScreenDoze(mScreenState)) {
+ mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = mHistoryCur.batteryLevel;
mDischargeScreenOffUnplugLevel = 0;
} else {
mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = mHistoryCur.batteryLevel;
}
mDischargeAmountScreenOn = 0;
mDischargeAmountScreenOff = 0;
+ mDischargeAmountScreenDoze = 0;
}
initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
}
@@ -9490,6 +9540,7 @@
mStartCount = 0;
initTimes(uptimeMillis * 1000, elapsedRealtimeMillis * 1000);
mScreenOnTimer.reset(false);
+ mScreenDozeTimer.reset(false);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].reset(false);
}
@@ -9626,33 +9677,52 @@
}
}
- void updateDischargeScreenLevelsLocked(boolean oldScreenOn, boolean newScreenOn) {
- if (oldScreenOn) {
+ void updateDischargeScreenLevelsLocked(int oldState, int newState) {
+ updateOldDischargeScreenLevelLocked(oldState);
+ updateNewDischargeScreenLevelLocked(newState);
+ }
+
+ private void updateOldDischargeScreenLevelLocked(int state) {
+ if (isScreenOn(state)) {
int diff = mDischargeScreenOnUnplugLevel - mDischargeCurrentLevel;
if (diff > 0) {
mDischargeAmountScreenOn += diff;
mDischargeAmountScreenOnSinceCharge += diff;
}
- } else {
+ } else if (isScreenDoze(state)) {
+ int diff = mDischargeScreenDozeUnplugLevel - mDischargeCurrentLevel;
+ if (diff > 0) {
+ mDischargeAmountScreenDoze += diff;
+ mDischargeAmountScreenDozeSinceCharge += diff;
+ }
+ } else if (isScreenOff(state)){
int diff = mDischargeScreenOffUnplugLevel - mDischargeCurrentLevel;
if (diff > 0) {
mDischargeAmountScreenOff += diff;
mDischargeAmountScreenOffSinceCharge += diff;
}
}
- if (newScreenOn) {
+ }
+
+ private void updateNewDischargeScreenLevelLocked(int state) {
+ if (isScreenOn(state)) {
mDischargeScreenOnUnplugLevel = mDischargeCurrentLevel;
mDischargeScreenOffUnplugLevel = 0;
- } else {
+ mDischargeScreenDozeUnplugLevel = 0;
+ } else if (isScreenDoze(state)){
mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = mDischargeCurrentLevel;
+ mDischargeScreenOffUnplugLevel = 0;
+ } else if (isScreenOff(state)) {
+ mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = mDischargeCurrentLevel;
}
}
public void pullPendingStateUpdatesLocked() {
if (mOnBatteryInternal) {
- final boolean screenOn = mScreenState == Display.STATE_ON;
- updateDischargeScreenLevelsLocked(screenOn, screenOn);
+ updateDischargeScreenLevelsLocked(mScreenState, mScreenState);
}
}
@@ -10785,8 +10855,8 @@
return false;
}
- void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
- final int oldStatus, final int level, final int chargeUAh) {
+ protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
+ final boolean onBattery, final int oldStatus, final int level, final int chargeUAh) {
boolean doWrite = false;
Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
m.arg1 = onBattery ? 1 : 0;
@@ -10794,7 +10864,7 @@
final long uptime = mSecUptime * 1000;
final long realtime = mSecRealtime * 1000;
- final boolean screenOn = mScreenState == Display.STATE_ON;
+ final int screenState = mScreenState;
if (onBattery) {
// We will reset our status if we are unplugging after the
// battery was last full, or the level is at 100, or
@@ -10870,16 +10940,23 @@
}
addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargeUnplugLevel = level;
- if (screenOn) {
+ if (isScreenOn(screenState)) {
mDischargeScreenOnUnplugLevel = level;
+ mDischargeScreenDozeUnplugLevel = 0;
+ mDischargeScreenOffUnplugLevel = 0;
+ } else if (isScreenDoze(screenState)) {
+ mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = level;
mDischargeScreenOffUnplugLevel = 0;
} else {
mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = level;
}
mDischargeAmountScreenOn = 0;
+ mDischargeAmountScreenDoze = 0;
mDischargeAmountScreenOff = 0;
- updateTimeBasesLocked(true, !screenOn, uptime, realtime);
+ updateTimeBasesLocked(true, screenState, uptime, realtime);
} else {
mLastChargingStateLevel = level;
mOnBattery = mOnBatteryInternal = false;
@@ -10894,8 +10971,8 @@
mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;
}
- updateDischargeScreenLevelsLocked(screenOn, screenOn);
- updateTimeBasesLocked(false, !screenOn, uptime, realtime);
+ updateDischargeScreenLevelsLocked(screenState, screenState);
+ updateTimeBasesLocked(false, screenState, uptime, realtime);
mChargeStepTracker.init();
mLastChargeStepLevel = level;
mMaxChargeStepLevel = level;
@@ -11012,6 +11089,9 @@
final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh;
mDischargeCounter.addCountLocked(chargeDiff);
mDischargeScreenOffCounter.addCountLocked(chargeDiff);
+ if (isScreenDoze(mScreenState)) {
+ mDischargeScreenDozeCounter.addCountLocked(chargeDiff);
+ }
}
mHistoryCur.batteryChargeUAh = chargeUAh;
setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level, chargeUAh);
@@ -11054,6 +11134,9 @@
final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh;
mDischargeCounter.addCountLocked(chargeDiff);
mDischargeScreenOffCounter.addCountLocked(chargeDiff);
+ if (isScreenDoze(mScreenState)) {
+ mDischargeScreenDozeCounter.addCountLocked(chargeDiff);
+ }
}
mHistoryCur.batteryChargeUAh = chargeUAh;
changed = true;
@@ -11362,10 +11445,11 @@
return dischargeAmount;
}
+ @Override
public int getDischargeAmountScreenOn() {
synchronized(this) {
int val = mDischargeAmountScreenOn;
- if (mOnBattery && mScreenState == Display.STATE_ON
+ if (mOnBattery && isScreenOn(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) {
val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel;
}
@@ -11373,10 +11457,11 @@
}
}
+ @Override
public int getDischargeAmountScreenOnSinceCharge() {
synchronized(this) {
int val = mDischargeAmountScreenOnSinceCharge;
- if (mOnBattery && mScreenState == Display.STATE_ON
+ if (mOnBattery && isScreenOn(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) {
val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel;
}
@@ -11384,23 +11469,51 @@
}
}
+ @Override
public int getDischargeAmountScreenOff() {
synchronized(this) {
int val = mDischargeAmountScreenOff;
- if (mOnBattery && mScreenState != Display.STATE_ON
+ if (mOnBattery && isScreenOff(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) {
val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel;
}
+ // For backward compatibility, doze discharge is counted into screen off.
+ return val + getDischargeAmountScreenDoze();
+ }
+ }
+
+ @Override
+ public int getDischargeAmountScreenOffSinceCharge() {
+ synchronized(this) {
+ int val = mDischargeAmountScreenOffSinceCharge;
+ if (mOnBattery && isScreenOff(mScreenState)
+ && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) {
+ val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel;
+ }
+ // For backward compatibility, doze discharge is counted into screen off.
+ return val + getDischargeAmountScreenDozeSinceCharge();
+ }
+ }
+
+ @Override
+ public int getDischargeAmountScreenDoze() {
+ synchronized(this) {
+ int val = mDischargeAmountScreenDoze;
+ if (mOnBattery && isScreenDoze(mScreenState)
+ && mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) {
+ val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel;
+ }
return val;
}
}
- public int getDischargeAmountScreenOffSinceCharge() {
+ @Override
+ public int getDischargeAmountScreenDozeSinceCharge() {
synchronized(this) {
- int val = mDischargeAmountScreenOffSinceCharge;
- if (mOnBattery && mScreenState != Display.STATE_ON
- && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) {
- val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel;
+ int val = mDischargeAmountScreenDozeSinceCharge;
+ if (mOnBattery && isScreenDoze(mScreenState)
+ && mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) {
+ val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel;
}
return val;
}
@@ -11759,12 +11872,14 @@
mHighDischargeAmountSinceCharge = in.readInt();
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
+ mDischargeAmountScreenDozeSinceCharge = in.readInt();
mDischargeStepTracker.readFromParcel(in);
mChargeStepTracker.readFromParcel(in);
mDailyDischargeStepTracker.readFromParcel(in);
mDailyChargeStepTracker.readFromParcel(in);
mDischargeCounter.readSummaryFromParcelLocked(in);
mDischargeScreenOffCounter.readSummaryFromParcelLocked(in);
+ mDischargeScreenDozeCounter.readSummaryFromParcelLocked(in);
int NPKG = in.readInt();
if (NPKG > 0) {
mDailyPackageChanges = new ArrayList<>(NPKG);
@@ -11787,6 +11902,7 @@
mScreenState = Display.STATE_UNKNOWN;
mScreenOnTimer.readSummaryFromParcelLocked(in);
+ mScreenDozeTimer.readSummaryFromParcelLocked(in);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].readSummaryFromParcelLocked(in);
}
@@ -12180,12 +12296,14 @@
out.writeInt(getHighDischargeAmountSinceCharge());
out.writeInt(getDischargeAmountScreenOnSinceCharge());
out.writeInt(getDischargeAmountScreenOffSinceCharge());
+ out.writeInt(getDischargeAmountScreenDozeSinceCharge());
mDischargeStepTracker.writeToParcel(out);
mChargeStepTracker.writeToParcel(out);
mDailyDischargeStepTracker.writeToParcel(out);
mDailyChargeStepTracker.writeToParcel(out);
mDischargeCounter.writeSummaryFromParcelLocked(out);
mDischargeScreenOffCounter.writeSummaryFromParcelLocked(out);
+ mDischargeScreenDozeCounter.writeSummaryFromParcelLocked(out);
if (mDailyPackageChanges != null) {
final int NPKG = mDailyPackageChanges.size();
out.writeInt(NPKG);
@@ -12203,6 +12321,7 @@
out.writeLong(mNextMaxDailyDeadline);
mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
}
@@ -12635,6 +12754,7 @@
mScreenState = Display.STATE_UNKNOWN;
mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase, in);
+ mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase, in);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i] = new StopwatchTimer(mClocks, null, -100-i, null,
mOnBatteryTimeBase, in);
@@ -12728,10 +12848,13 @@
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOff = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
+ mDischargeAmountScreenDoze = in.readInt();
+ mDischargeAmountScreenDozeSinceCharge = in.readInt();
mDischargeStepTracker.readFromParcel(in);
mChargeStepTracker.readFromParcel(in);
mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
- mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase, in);
+ mDischargeScreenDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
mLastWriteTime = in.readLong();
mRpmStats.clear();
@@ -12848,6 +12971,7 @@
mOnBatteryScreenOffTimeBase.writeToParcel(out, uSecUptime, uSecRealtime);
mScreenOnTimer.writeToParcel(out, uSecRealtime);
+ mScreenDozeTimer.writeToParcel(out, uSecRealtime);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].writeToParcel(out, uSecRealtime);
}
@@ -12910,10 +13034,13 @@
out.writeInt(mDischargeAmountScreenOnSinceCharge);
out.writeInt(mDischargeAmountScreenOff);
out.writeInt(mDischargeAmountScreenOffSinceCharge);
+ out.writeInt(mDischargeAmountScreenDoze);
+ out.writeInt(mDischargeAmountScreenDozeSinceCharge);
mDischargeStepTracker.writeToParcel(out);
mChargeStepTracker.writeToParcel(out);
mDischargeCounter.writeToParcel(out);
mDischargeScreenOffCounter.writeToParcel(out);
+ mDischargeScreenDozeCounter.writeToParcel(out);
out.writeLong(mLastWriteTime);
out.writeInt(mRpmStats.size());
@@ -13020,8 +13147,10 @@
pw.println("mOnBatteryScreenOffTimeBase:");
mOnBatteryScreenOffTimeBase.dump(pw, " ");
Printer pr = new PrintWriterPrinter(pw);
- pr.println("*** Screen timer:");
+ pr.println("*** Screen on timer:");
mScreenOnTimer.logState(pr, " ");
+ pr.println("*** Screen doze timer:");
+ mScreenDozeTimer.logState(pr, " ");
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
pr.println("*** Screen brightness #" + i + ":");
mScreenBrightnessTimer[i].logState(pr, " ");
diff --git a/core/java/com/android/internal/util/BitUtils.java b/core/java/com/android/internal/util/BitUtils.java
index 28f12eb..ba80aea 100644
--- a/core/java/com/android/internal/util/BitUtils.java
+++ b/core/java/com/android/internal/util/BitUtils.java
@@ -93,6 +93,10 @@
return s & 0xffff;
}
+ public static int uint16(byte hi, byte lo) {
+ return ((hi & 0xff) << 8) | (lo & 0xff);
+ }
+
public static long uint32(int i) {
return i & 0xffffffffL;
}
diff --git a/core/java/com/android/internal/util/RingBuffer.java b/core/java/com/android/internal/util/RingBuffer.java
new file mode 100644
index 0000000..ad84353
--- /dev/null
+++ b/core/java/com/android/internal/util/RingBuffer.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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.util;
+
+import static com.android.internal.util.Preconditions.checkArgumentPositive;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+
+/**
+ * A simple ring buffer structure with bounded capacity backed by an array.
+ * Events can always be added at the logical end of the buffer. If the buffer is
+ * full, oldest events are dropped when new events are added.
+ * {@hide}
+ */
+public class RingBuffer<T> {
+
+ // Array for storing events.
+ private final T[] mBuffer;
+ // Cursor keeping track of the logical end of the array. This cursor never
+ // wraps and instead keeps track of the total number of append() operations.
+ private long mCursor = 0;
+
+ public RingBuffer(Class<T> c, int capacity) {
+ checkArgumentPositive(capacity, "A RingBuffer cannot have 0 capacity");
+ // Java cannot create generic arrays without a runtime hint.
+ mBuffer = (T[]) Array.newInstance(c, capacity);
+ }
+
+ public int size() {
+ return (int) Math.min(mBuffer.length, (long) mCursor);
+ }
+
+ public void append(T t) {
+ mBuffer[indexOf(mCursor++)] = t;
+ }
+
+ public T[] toArray() {
+ // Only generic way to create a T[] from another T[]
+ T[] out = Arrays.copyOf(mBuffer, size(), (Class<T[]>) mBuffer.getClass());
+ // Reverse iteration from youngest event to oldest event.
+ long inCursor = mCursor - 1;
+ int outIdx = out.length - 1;
+ while (outIdx >= 0) {
+ out[outIdx--] = (T) mBuffer[indexOf(inCursor--)];
+ }
+ return out;
+ }
+
+ private int indexOf(long cursor) {
+ return (int) Math.abs(cursor % mBuffer.length);
+ }
+}
diff --git a/core/java/com/android/internal/widget/LinearLayoutManager.java b/core/java/com/android/internal/widget/LinearLayoutManager.java
index d82c746..0000a74 100644
--- a/core/java/com/android/internal/widget/LinearLayoutManager.java
+++ b/core/java/com/android/internal/widget/LinearLayoutManager.java
@@ -168,10 +168,6 @@
/**
* Constructor used when layout manager is set in XML by RecyclerView attribute
* "layoutManager". Defaults to vertical orientation.
- *
- * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
- * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
- * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
*/
public LinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index b8ef82b..4be6b28 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -303,7 +303,7 @@
}
public void reportFailedPasswordAttempt(int userId) {
- if (userId == USER_FRP && frpCredentialEnabled()) {
+ if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
return;
}
getDevicePolicyManager().reportFailedPasswordAttempt(userId);
@@ -311,7 +311,7 @@
}
public void reportSuccessfulPasswordAttempt(int userId) {
- if (userId == USER_FRP && frpCredentialEnabled()) {
+ if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
return;
}
getDevicePolicyManager().reportSuccessfulPasswordAttempt(userId);
@@ -319,21 +319,21 @@
}
public void reportPasswordLockout(int timeoutMs, int userId) {
- if (userId == USER_FRP && frpCredentialEnabled()) {
+ if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
return;
}
getTrustManager().reportUnlockLockout(timeoutMs, userId);
}
public int getCurrentFailedPasswordAttempts(int userId) {
- if (userId == USER_FRP && frpCredentialEnabled()) {
+ if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
return 0;
}
return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId);
}
public int getMaximumFailedPasswordsForWipe(int userId) {
- if (userId == USER_FRP && frpCredentialEnabled()) {
+ if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
return 0;
}
return getDevicePolicyManager().getMaximumFailedPasswordsForWipe(
@@ -1774,11 +1774,12 @@
return getLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM) != 0;
}
- public static boolean userOwnsFrpCredential(UserInfo info) {
- return info != null && info.isPrimary() && info.isAdmin() && frpCredentialEnabled();
+ public static boolean userOwnsFrpCredential(Context context, UserInfo info) {
+ return info != null && info.isPrimary() && info.isAdmin() && frpCredentialEnabled(context);
}
- public static boolean frpCredentialEnabled() {
- return FRP_CREDENTIAL_ENABLED;
+ public static boolean frpCredentialEnabled(Context context) {
+ return FRP_CREDENTIAL_ENABLED && context.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableCredentialFactoryResetProtection);
}
}
diff --git a/core/java/com/android/internal/widget/Magnifier.java b/core/java/com/android/internal/widget/Magnifier.java
new file mode 100644
index 0000000..86e7b38
--- /dev/null
+++ b/core/java/com/android/internal/widget/Magnifier.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2017 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.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.UiThread;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.PixelCopy;
+import android.view.View;
+import android.view.ViewRootImpl;
+import android.widget.ImageView;
+import android.widget.PopupWindow;
+
+import com.android.internal.R;
+import com.android.internal.util.Preconditions;
+
+/**
+ * Android magnifier widget. Can be used by any view which is attached to window.
+ */
+public final class Magnifier {
+ private static final String LOG_TAG = "magnifier";
+ // The view for which this magnifier is attached.
+ private final View mView;
+ // The window containing the magnifier.
+ private final PopupWindow mWindow;
+ // The center coordinates of the window containing the magnifier.
+ private final Point mWindowCoords = new Point();
+ // The width of the window containing the magnifier.
+ private final int mWindowWidth;
+ // The height of the window containing the magnifier.
+ private final int mWindowHeight;
+ // The bitmap used to display the contents of the magnifier.
+ private final Bitmap mBitmap;
+ // The center coordinates of the content that is to be magnified.
+ private final Point mCenterZoomCoords = new Point();
+ // The callback of the pixel copy request will be invoked on this Handler when
+ // the copy is finished.
+ private final Handler mPixelCopyHandler = Handler.getMain();
+
+ /**
+ * Initializes a magnifier.
+ *
+ * @param view the view for which this magnifier is attached
+ */
+ @UiThread
+ public Magnifier(@NonNull View view) {
+ mView = Preconditions.checkNotNull(view);
+ final Context context = mView.getContext();
+ final View content = LayoutInflater.from(context).inflate(R.layout.magnifier, null);
+ mWindowWidth = context.getResources().getDimensionPixelSize(R.dimen.magnifier_width);
+ mWindowHeight = context.getResources().getDimensionPixelSize(R.dimen.magnifier_height);
+ final float elevation = context.getResources().getDimension(R.dimen.magnifier_elevation);
+
+ mWindow = new PopupWindow(context);
+ mWindow.setContentView(content);
+ mWindow.setWidth(mWindowWidth);
+ mWindow.setHeight(mWindowHeight);
+ mWindow.setElevation(elevation);
+ mWindow.setTouchable(false);
+ mWindow.setBackgroundDrawable(null);
+
+ mBitmap = Bitmap.createBitmap(mWindowWidth, mWindowHeight, Bitmap.Config.ARGB_8888);
+ getImageView().setImageBitmap(mBitmap);
+ }
+
+ /**
+ * Shows the magnifier on the screen.
+ *
+ * @param centerXOnScreen horizontal coordinate of the center point of the magnifier source
+ * @param centerYOnScreen vertical coordinate of the center point of the magnifier source
+ * @param scale the scale at which the magnifier zooms on the source content
+ */
+ public void show(@FloatRange(from=0) float centerXOnScreen,
+ @FloatRange(from=0) float centerYOnScreen,
+ @FloatRange(from=1, to=10) float scale) {
+ maybeResizeBitmap(scale);
+ configureCoordinates(centerXOnScreen, centerYOnScreen);
+ performPixelCopy();
+
+ if (mWindow.isShowing()) {
+ mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
+ mWindow.getHeight());
+ } else {
+ mWindow.showAtLocation(mView.getRootView(), Gravity.NO_GRAVITY,
+ mWindowCoords.x, mWindowCoords.y);
+ }
+ }
+
+ /**
+ * Dismisses the magnifier from the screen.
+ */
+ public void dismiss() {
+ mWindow.dismiss();
+ }
+
+ /**
+ * @return the height of the magnifier window.
+ */
+ public int getHeight() {
+ return mWindowHeight;
+ }
+
+ /**
+ * @return the width of the magnifier window.
+ */
+ public int getWidth() {
+ return mWindowWidth;
+ }
+
+ private void maybeResizeBitmap(float scale) {
+ final int bitmapWidth = (int) (mWindowWidth / scale);
+ final int bitmapHeight = (int) (mWindowHeight / scale);
+ if (mBitmap.getWidth() != bitmapWidth || mBitmap.getHeight() != bitmapHeight) {
+ mBitmap.reconfigure(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
+ getImageView().setImageBitmap(mBitmap);
+ }
+ }
+
+ private void configureCoordinates(float posXOnScreen, float posYOnScreen) {
+ mCenterZoomCoords.x = (int) posXOnScreen;
+ mCenterZoomCoords.y = (int) posYOnScreen;
+
+ final int verticalMagnifierOffset = mView.getContext().getResources().getDimensionPixelSize(
+ R.dimen.magnifier_offset);
+ final int availableTopSpace = (mCenterZoomCoords.y - mWindowHeight / 2)
+ - verticalMagnifierOffset - (mBitmap.getHeight() / 2);
+
+ mWindowCoords.x = mCenterZoomCoords.x - mWindowWidth / 2;
+ mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2
+ + verticalMagnifierOffset * (availableTopSpace > 0 ? -1 : 1);
+ }
+
+ private void performPixelCopy() {
+ int startX = mCenterZoomCoords.x - mBitmap.getWidth() / 2;
+ // Clamp startX value to avoid distorting the rendering of the magnifier content.
+ if (startX < 0) {
+ startX = 0;
+ } else if (startX + mBitmap.getWidth() > mView.getWidth()) {
+ startX = mView.getWidth() - mBitmap.getWidth();
+ }
+
+ final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
+ final ViewRootImpl viewRootImpl = mView.getViewRootImpl();
+
+ if (viewRootImpl != null && viewRootImpl.mSurface != null
+ && viewRootImpl.mSurface.isValid()) {
+ PixelCopy.request(
+ viewRootImpl.mSurface,
+ new Rect(startX, startY, startX + mBitmap.getWidth(),
+ startY + mBitmap.getHeight()),
+ mBitmap,
+ result -> getImageView().invalidate(),
+ mPixelCopyHandler);
+ } else {
+ Log.d(LOG_TAG, "Could not perform PixelCopy request");
+ }
+ }
+
+ private ImageView getImageView() {
+ return mWindow.getContentView().findViewById(R.id.magnifier_image);
+ }
+}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index d63e22c..256b920 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -78,6 +78,7 @@
"android_view_VelocityTracker.cpp",
"android_text_AndroidCharacter.cpp",
"android_text_AndroidBidi.cpp",
+ "android_text_Hyphenator.cpp",
"android_text_StaticLayout.cpp",
"android_os_Debug.cpp",
"android_os_GraphicsEnvironment.cpp",
@@ -239,7 +240,6 @@
"libinput",
"libcamera_client",
"libcamera_metadata",
- "libskia",
"libsqlite",
"libEGL",
"libGLESv1_CM",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 02c9848..deb4d5a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -173,6 +173,7 @@
extern int register_android_net_NetworkUtils(JNIEnv* env);
extern int register_android_net_TrafficStats(JNIEnv* env);
extern int register_android_text_AndroidCharacter(JNIEnv *env);
+extern int register_android_text_Hyphenator(JNIEnv *env);
extern int register_android_text_StaticLayout(JNIEnv *env);
extern int register_android_text_AndroidBidi(JNIEnv *env);
extern int register_android_opengl_classes(JNIEnv *env);
@@ -1321,6 +1322,7 @@
REG_JNI(register_android_content_StringBlock),
REG_JNI(register_android_content_XmlBlock),
REG_JNI(register_android_text_AndroidCharacter),
+ REG_JNI(register_android_text_Hyphenator),
REG_JNI(register_android_text_StaticLayout),
REG_JNI(register_android_text_AndroidBidi),
REG_JNI(register_android_view_InputDevice),
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 0ba27f6..c6801bf 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -97,21 +97,21 @@
static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex,
jint givenWeight, jint givenItalic) {
- uirenderer::FatVector<SkFontMgr::FontParameters::Axis, 2> skiaAxes;
+ uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes;
for (const auto& axis : builder->axes) {
- skiaAxes.emplace_back(SkFontMgr::FontParameters::Axis{axis.axisTag, axis.value});
+ skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
}
const size_t fontSize = data->size();
const void* fontPtr = data->data();
std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));
- SkFontMgr::FontParameters params;
+ SkFontArguments params;
params.setCollectionIndex(ttcIndex);
params.setAxes(skiaAxes.data(), skiaAxes.size());
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
- sk_sp<SkTypeface> face(fm->createFromStream(fontData.release(), params));
+ sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params));
if (face == NULL) {
ALOGE("addFont failed to create font, invalid request");
builder->axes.clear();
diff --git a/core/jni/android/graphics/FontUtils.cpp b/core/jni/android/graphics/FontUtils.cpp
index 3bcf0c7..0cf61b9 100644
--- a/core/jni/android/graphics/FontUtils.cpp
+++ b/core/jni/android/graphics/FontUtils.cpp
@@ -16,7 +16,7 @@
#include "FontUtils.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include <core_jni_helpers.h>
namespace android {
diff --git a/core/jni/android/graphics/GraphicBuffer.cpp b/core/jni/android/graphics/GraphicBuffer.cpp
index 1017cba..ae6fd38 100644
--- a/core/jni/android/graphics/GraphicBuffer.cpp
+++ b/core/jni/android/graphics/GraphicBuffer.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "GraphicBuffer"
#include "jni.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include <inttypes.h>
#include "android_os_Parcel.h"
diff --git a/core/jni/android/graphics/pdf/PdfUtils.cpp b/core/jni/android/graphics/pdf/PdfUtils.cpp
index dacca78..36355eb 100644
--- a/core/jni/android/graphics/pdf/PdfUtils.cpp
+++ b/core/jni/android/graphics/pdf/PdfUtils.cpp
@@ -17,7 +17,7 @@
#include "PdfUtils.h"
#include "jni.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include "fpdfview.h"
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 18f3177..09e37e1 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -40,6 +40,7 @@
#include "android_view_InputChannel.h"
#include "android_view_KeyEvent.h"
+#include "android-base/stringprintf.h"
#include "nativebridge/native_bridge.h"
#include "nativeloader/native_loader.h"
@@ -265,6 +266,8 @@
// ------------------------------------------------------------------------
+static thread_local std::string g_error_msg;
+
static jlong
loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
jobject messageQueue, jstring internalDataDir, jstring obbDir,
@@ -277,7 +280,6 @@
ScopedUtfChars pathStr(env, path);
std::unique_ptr<NativeCode> code;
bool needs_native_bridge = false;
- std::string error_msg;
void* handle = OpenNativeLibrary(env,
sdkVersion,
@@ -285,12 +287,12 @@
classLoader,
libraryPath,
&needs_native_bridge,
- &error_msg);
+ &g_error_msg);
if (handle == nullptr) {
ALOGW("NativeActivity LoadNativeLibrary(\"%s\") failed: %s",
pathStr.c_str(),
- error_msg.c_str());
+ g_error_msg.c_str());
return 0;
}
@@ -306,19 +308,22 @@
env->ReleaseStringUTFChars(funcName, funcStr);
if (code->createActivityFunc == NULL) {
- ALOGW("ANativeActivity_onCreate not found");
+ g_error_msg = needs_native_bridge ? NativeBridgeGetError() : dlerror();
+ ALOGW("ANativeActivity_onCreate not found: %s", g_error_msg.c_str());
return 0;
}
code->messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueue);
if (code->messageQueue == NULL) {
- ALOGW("Unable to retrieve native MessageQueue");
+ g_error_msg = "Unable to retrieve native MessageQueue";
+ ALOGW("%s", g_error_msg.c_str());
return 0;
}
int msgpipe[2];
if (pipe(msgpipe)) {
- ALOGW("could not create pipe: %s", strerror(errno));
+ g_error_msg = android::base::StringPrintf("could not create pipe: %s", strerror(errno));
+ ALOGW("%s", g_error_msg.c_str());
return 0;
}
code->mainWorkRead = msgpipe[0];
@@ -334,7 +339,8 @@
code->ANativeActivity::callbacks = &code->callbacks;
if (env->GetJavaVM(&code->vm) < 0) {
- ALOGW("NativeActivity GetJavaVM failed");
+ g_error_msg = "NativeActivity GetJavaVM failed";
+ ALOGW("%s", g_error_msg.c_str());
return 0;
}
code->env = env;
@@ -381,7 +387,9 @@
}
static jstring getDlError_native(JNIEnv* env, jobject clazz) {
- return env->NewStringUTF(dlerror());
+ jstring result = env->NewStringUTF(g_error_msg.c_str());
+ g_error_msg.clear();
+ return result;
}
static void
diff --git a/core/jni/android_hardware_HardwareBuffer.cpp b/core/jni/android_hardware_HardwareBuffer.cpp
index ba23450..8174a41 100644
--- a/core/jni/android_hardware_HardwareBuffer.cpp
+++ b/core/jni/android_hardware_HardwareBuffer.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "HardwareBuffer"
#include "jni.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include "android_os_Parcel.h"
#include "android/graphics/GraphicsJNI.h"
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index 863ca8b..b610b35 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -484,14 +484,14 @@
sp<ANativeWindow> anw;
if ((anw = getNativeWindow(env, surface)) == NULL) {
- jniThrowException(env, "Ljava/lang/UnsupportedOperationException;",
+ jniThrowException(env, "java/lang/UnsupportedOperationException;",
"Could not retrieve native window from surface.");
return BAD_VALUE;
}
int32_t usage = 0;
status_t err = anw->query(anw.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS, &usage);
if(err != NO_ERROR) {
- jniThrowException(env, "Ljava/lang/UnsupportedOperationException;",
+ jniThrowException(env, "java/lang/UnsupportedOperationException;",
"Error while querying surface usage bits");
return err;
}
@@ -511,7 +511,7 @@
status_t err = native_window_api_disconnect(anw.get(), NATIVE_WINDOW_API_CAMERA);
if(err != NO_ERROR) {
- jniThrowException(env, "Ljava/lang/UnsupportedOperationException;",
+ jniThrowException(env, "java/lang/UnsupportedOperationException;",
"Error while disconnecting surface");
return err;
}
diff --git a/core/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp
index 2d2837c..23c3877 100644
--- a/core/jni/android_hardware_display_DisplayViewport.cpp
+++ b/core/jni/android_hardware_display_DisplayViewport.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "DisplayViewport-JNI"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include "core_jni_helpers.h"
#include <android_hardware_display_DisplayViewport.h>
diff --git a/core/jni/android_os_SharedMemory.cpp b/core/jni/android_os_SharedMemory.cpp
index 1d29908..f6e5c7a 100644
--- a/core/jni/android_os_SharedMemory.cpp
+++ b/core/jni/android_os_SharedMemory.cpp
@@ -20,7 +20,7 @@
#include <cutils/ashmem.h>
#include <utils/Log.h>
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include <nativehelper/JniConstants.h>
#include <nativehelper/ScopedLocalRef.h>
diff --git a/core/jni/android_os_VintfRuntimeInfo.cpp b/core/jni/android_os_VintfRuntimeInfo.cpp
index 315eac1..9379ea6 100644
--- a/core/jni/android_os_VintfRuntimeInfo.cpp
+++ b/core/jni/android_os_VintfRuntimeInfo.cpp
@@ -29,28 +29,33 @@
using vintf::RuntimeInfo;
using vintf::VintfObject;
-#define MAP_STRING_METHOD(javaMethod, cppString) \
+#define MAP_STRING_METHOD(javaMethod, cppString, flags) \
static jstring android_os_VintfRuntimeInfo_##javaMethod(JNIEnv* env, jclass clazz) \
{ \
- std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(); \
+ std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo( \
+ false /* skipCache */, flags); \
if (info == nullptr) return nullptr; \
return env->NewStringUTF((cppString).c_str()); \
} \
-MAP_STRING_METHOD(getCpuInfo, info->cpuInfo());
-MAP_STRING_METHOD(getOsName, info->osName());
-MAP_STRING_METHOD(getNodeName, info->nodeName());
-MAP_STRING_METHOD(getOsRelease, info->osRelease());
-MAP_STRING_METHOD(getOsVersion, info->osVersion());
-MAP_STRING_METHOD(getHardwareId, info->hardwareId());
-MAP_STRING_METHOD(getKernelVersion, vintf::to_string(info->kernelVersion()));
-MAP_STRING_METHOD(getBootAvbVersion, vintf::to_string(info->bootAvbVersion()));
-MAP_STRING_METHOD(getBootVbmetaAvbVersion, vintf::to_string(info->bootVbmetaAvbVersion()));
+MAP_STRING_METHOD(getCpuInfo, info->cpuInfo(), RuntimeInfo::FetchFlag::CPU_INFO);
+MAP_STRING_METHOD(getOsName, info->osName(), RuntimeInfo::FetchFlag::CPU_VERSION);
+MAP_STRING_METHOD(getNodeName, info->nodeName(), RuntimeInfo::FetchFlag::CPU_VERSION);
+MAP_STRING_METHOD(getOsRelease, info->osRelease(), RuntimeInfo::FetchFlag::CPU_VERSION);
+MAP_STRING_METHOD(getOsVersion, info->osVersion(), RuntimeInfo::FetchFlag::CPU_VERSION);
+MAP_STRING_METHOD(getHardwareId, info->hardwareId(), RuntimeInfo::FetchFlag::CPU_VERSION);
+MAP_STRING_METHOD(getKernelVersion, vintf::to_string(info->kernelVersion()),
+ RuntimeInfo::FetchFlag::CPU_VERSION);
+MAP_STRING_METHOD(getBootAvbVersion, vintf::to_string(info->bootAvbVersion()),
+ RuntimeInfo::FetchFlag::AVB);
+MAP_STRING_METHOD(getBootVbmetaAvbVersion, vintf::to_string(info->bootVbmetaAvbVersion()),
+ RuntimeInfo::FetchFlag::AVB);
static jlong android_os_VintfRuntimeInfo_getKernelSepolicyVersion(JNIEnv *env, jclass clazz)
{
- std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo();
+ std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(
+ false /* skipCache */, RuntimeInfo::FetchFlag::POLICYVERS);
if (info == nullptr) return 0;
return static_cast<jlong>(info->kernelSepolicyVersion());
}
diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp
new file mode 100644
index 0000000..da025da
--- /dev/null
+++ b/core/jni/android_text_Hyphenator.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <cstdint>
+#include <core_jni_helpers.h>
+#include <minikin/Hyphenator.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+namespace android {
+
+static jlong nBuildHyphenator(JNIEnv* env, jclass, jlong dataAddress, jstring lang,
+ jint minPrefix, jint minSuffix) {
+ const uint8_t* bytebuf = reinterpret_cast<const uint8_t*>(dataAddress); // null allowed.
+ ScopedUtfChars language(env, lang);
+ minikin::Hyphenator* hyphenator = minikin::Hyphenator::loadBinary(
+ bytebuf, minPrefix, minSuffix, language.c_str(), language.size());
+ return reinterpret_cast<jlong>(hyphenator);
+}
+
+static const JNINativeMethod gMethods[] = {
+ {"nBuildHyphenator", "(JLjava/lang/String;II)J", (void*) nBuildHyphenator},
+};
+
+int register_android_text_Hyphenator(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/text/Hyphenator", gMethods, NELEM(gMethods));
+}
+
+} // namespace android
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 7442fa2..83ffeff 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -55,39 +55,64 @@
class JNILineBreakerLineWidth : public minikin::LineBreaker::LineWidthDelegate {
public:
JNILineBreakerLineWidth(float firstWidth, int32_t firstLineCount, float restWidth,
- std::vector<float>&& indents, int32_t indentsOffset)
+ std::vector<float>&& indents, std::vector<float>&& leftPaddings,
+ std::vector<float>&& rightPaddings, int32_t indentsAndPaddingsOffset)
: mFirstWidth(firstWidth), mFirstLineCount(firstLineCount), mRestWidth(restWidth),
- mIndents(std::move(indents)), mIndentsOffset(indentsOffset) {}
+ mIndents(std::move(indents)), mLeftPaddings(std::move(leftPaddings)),
+ mRightPaddings(std::move(rightPaddings)), mOffset(indentsAndPaddingsOffset) {}
float getLineWidth(size_t lineNo) override {
const float width = ((ssize_t)lineNo < (ssize_t)mFirstLineCount)
? mFirstWidth : mRestWidth;
- if (mIndents.empty()) {
- return width;
- }
+ return width - get(mIndents, lineNo);
+ }
- const size_t indentIndex = lineNo + mIndentsOffset;
- if (indentIndex < mIndents.size()) {
- return width - mIndents[indentIndex];
- } else {
- return width - mIndents.back();
- }
+ float getLeftPadding(size_t lineNo) override {
+ return get(mLeftPaddings, lineNo);
+ }
+
+ float getRightPadding(size_t lineNo) override {
+ return get(mRightPaddings, lineNo);
}
private:
+ float get(const std::vector<float>& vec, size_t lineNo) {
+ if (vec.empty()) {
+ return 0;
+ }
+ const size_t index = lineNo + mOffset;
+ if (index < vec.size()) {
+ return vec[index];
+ } else {
+ return vec.back();
+ }
+ }
+
const float mFirstWidth;
const int32_t mFirstLineCount;
const float mRestWidth;
const std::vector<float> mIndents;
- const int32_t mIndentsOffset;
+ const std::vector<float> mLeftPaddings;
+ const std::vector<float> mRightPaddings;
+ const int32_t mOffset;
};
+static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) {
+ if (javaArray == nullptr) {
+ return std::vector<float>();
+ } else {
+ ScopedIntArrayRO intArr(env, javaArray);
+ return std::vector<float>(intArr.get(), intArr.get() + intArr.size());
+ }
+}
+
// set text and set a number of parameters for creating a layout (width, tabstops, strategy,
// hyphenFrequency)
static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length,
jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth,
jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency,
- jboolean isJustified, jintArray indents, jint indentsOffset) {
+ jboolean isJustified, jintArray indents, jintArray leftPaddings, jintArray rightPaddings,
+ jint indentsAndPaddingsOffset) {
minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
b->resize(length);
env->GetCharArrayRegion(text, 0, length, b->buffer());
@@ -102,14 +127,11 @@
b->setHyphenationFrequency(static_cast<minikin::HyphenationFrequency>(hyphenFrequency));
b->setJustified(isJustified);
- std::vector<float> indentVec;
- // TODO: copy indents only once when LineBreaker is started to be used.
- if (indents != nullptr) {
- ScopedIntArrayRO indentArr(env, indents);
- indentVec.assign(indentArr.get(), indentArr.get() + indentArr.size());
- }
+ // TODO: copy indents and paddings only once when LineBreaker is started to be used.
b->setLineWidthDelegate(std::make_unique<JNILineBreakerLineWidth>(
- firstWidth, firstWidthLineLimit, restWidth, std::move(indentVec), indentsOffset));
+ firstWidth, firstWidthLineLimit, restWidth, jintArrayToFloatVector(env, indents),
+ jintArrayToFloatVector(env, leftPaddings), jintArrayToFloatVector(env, rightPaddings),
+ indentsAndPaddingsOffset));
}
static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
@@ -171,40 +193,49 @@
b->finish();
}
-static jlong nLoadHyphenator(JNIEnv* env, jclass, jobject buffer, jint offset,
- jint minPrefix, jint minSuffix) {
- const uint8_t* bytebuf = nullptr;
- if (buffer != nullptr) {
- void* rawbuf = env->GetDirectBufferAddress(buffer);
- if (rawbuf != nullptr) {
- bytebuf = reinterpret_cast<const uint8_t*>(rawbuf) + offset;
+class ScopedNullableUtfString {
+public:
+ ScopedNullableUtfString(JNIEnv* env, jstring s) : mEnv(env), mStr(s) {
+ if (s == nullptr) {
+ mUtf8Chars = nullptr;
} else {
- ALOGE("failed to get direct buffer address");
+ mUtf8Chars = mEnv->GetStringUTFChars(s, nullptr);
}
}
- minikin::Hyphenator* hyphenator = minikin::Hyphenator::loadBinary(
- bytebuf, minPrefix, minSuffix);
- return reinterpret_cast<jlong>(hyphenator);
-}
-static void nSetLocales(JNIEnv* env, jclass, jlong nativePtr, jstring javaLocaleNames,
- jlongArray nativeHyphenators) {
- minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
-
- ScopedUtfChars localeNames(env, javaLocaleNames);
- ScopedLongArrayRO hyphArr(env, nativeHyphenators);
- const size_t numLocales = hyphArr.size();
- std::vector<minikin::Hyphenator*> hyphVec;
- hyphVec.reserve(numLocales);
- for (size_t i = 0; i < numLocales; i++) {
- hyphVec.push_back(reinterpret_cast<minikin::Hyphenator*>(hyphArr[i]));
+ ~ScopedNullableUtfString() {
+ if (mUtf8Chars != nullptr) {
+ mEnv->ReleaseStringUTFChars(mStr, mUtf8Chars);
+ }
}
- b->setLocales(localeNames.c_str(), hyphVec);
+
+ const char* get() const {
+ return mUtf8Chars;
+ }
+
+private:
+ JNIEnv* mEnv;
+ jstring mStr;
+ const char* mUtf8Chars;
+};
+
+static std::vector<minikin::Hyphenator*> makeHyphenators(JNIEnv* env, jlongArray hyphenators) {
+ std::vector<minikin::Hyphenator*> out;
+ if (hyphenators == nullptr) {
+ return out;
+ }
+ ScopedLongArrayRO longArray(env, hyphenators);
+ size_t size = longArray.size();
+ out.reserve(size);
+ for (size_t i = 0; i < size; i++) {
+ out.push_back(reinterpret_cast<minikin::Hyphenator*>(longArray[i]));
+ }
+ return out;
}
// Basically similar to Paint.getTextRunAdvances but with C++ interface
-static jfloat nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr, jlong nativePaint, jint start,
- jint end, jboolean isRtl) {
+static void nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr, jlong nativePaint, jint start,
+ jint end, jboolean isRtl, jstring langTags, jlongArray hyphenators) {
minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
Paint* paint = reinterpret_cast<Paint*>(nativePaint);
const Typeface* typeface = paint->getAndroidTypeface();
@@ -212,22 +243,17 @@
const Typeface* resolvedTypeface = Typeface::resolveDefault(typeface);
minikin::FontStyle style = MinikinUtils::prepareMinikinPaint(&minikinPaint, paint,
typeface);
- return b->addStyleRun(&minikinPaint, resolvedTypeface->fFontCollection, style, start, end,
- isRtl);
-}
-// Accept width measurements for the run, passed in from Java
-static void nAddMeasuredRun(JNIEnv* env, jclass, jlong nativePtr,
- jint start, jint end, jfloatArray widths) {
- minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
- env->GetFloatArrayRegion(widths, start, end - start, b->charWidths() + start);
- b->addStyleRun(nullptr, nullptr, minikin::FontStyle{}, start, end, false);
+ ScopedNullableUtfString langTagsString(env, langTags);
+ b->addStyleRun(&minikinPaint, resolvedTypeface->fFontCollection, style, start,
+ end, isRtl, langTagsString.get(), makeHyphenators(env, hyphenators));
}
static void nAddReplacementRun(JNIEnv* env, jclass, jlong nativePtr,
- jint start, jint end, jfloat width) {
+ jint start, jint end, jfloat width, jstring langTags, jlongArray hyphenators) {
minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
- b->addReplacement(start, end, width);
+ ScopedNullableUtfString langTagsString(env, langTags);
+ b->addReplacement(start, end, width, langTagsString.get(), makeHyphenators(env, hyphenators));
}
static void nGetWidths(JNIEnv* env, jclass, jlong nativePtr, jfloatArray widths) {
@@ -240,12 +266,9 @@
{"nNewBuilder", "()J", (void*) nNewBuilder},
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
{"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
- {"nLoadHyphenator", "(Ljava/nio/ByteBuffer;III)J", (void*) nLoadHyphenator},
- {"nSetLocales", "(JLjava/lang/String;[J)V", (void*) nSetLocales},
- {"nSetupParagraph", "(J[CIFIF[IIIIZ[II)V", (void*) nSetupParagraph},
- {"nAddStyleRun", "(JJIIZ)F", (void*) nAddStyleRun},
- {"nAddMeasuredRun", "(JII[F)V", (void*) nAddMeasuredRun},
- {"nAddReplacementRun", "(JIIF)V", (void*) nAddReplacementRun},
+ {"nSetupParagraph", "(J[CIFIF[IIIIZ[I[I[II)V", (void*) nSetupParagraph},
+ {"nAddStyleRun", "(JJIIZLjava/lang/String;[J)V", (void*) nAddStyleRun},
+ {"nAddReplacementRun", "(JIIFLjava/lang/String;[J)V", (void*) nAddReplacementRun},
{"nGetWidths", "(J[F)V", (void*) nGetWidths},
{"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[F[F[II)I",
(void*) nComputeLineBreaks}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 883d4db..7908c9d 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -20,8 +20,6 @@
#include "android_os_Parcel.h"
#include "android_util_Binder.h"
-#include <nativehelper/JNIHelp.h>
-
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
@@ -44,8 +42,9 @@
#include <utils/SystemClock.h>
#include <utils/threads.h>
-#include <nativehelper/ScopedUtfChars.h>
+#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedUtfChars.h>
#include "core_jni_helpers.h"
@@ -174,8 +173,49 @@
return vm->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL;
}
-// Report a java.lang.Error (or subclass). This may terminate the runtime.
-static void report_java_lang_error(JNIEnv* env, jthrowable error)
+// Report a java.lang.Error (or subclass). This will terminate the runtime by
+// calling FatalError with a message derived from the given error.
+static void report_java_lang_error_fatal_error(JNIEnv* env, jthrowable error,
+ const char* msg)
+{
+ // Report an error: reraise the exception and ask the runtime to abort.
+
+ // Try to get the exception string. Sometimes logcat isn't available,
+ // so try to add it to the abort message.
+ std::string exc_msg = "(Unknown exception message)";
+ {
+ ScopedLocalRef<jclass> exc_class(env, env->GetObjectClass(error));
+ jmethodID method_id = env->GetMethodID(exc_class.get(), "toString",
+ "()Ljava/lang/String;");
+ ScopedLocalRef<jstring> jstr(
+ env,
+ reinterpret_cast<jstring>(
+ env->CallObjectMethod(error, method_id)));
+ env->ExceptionClear(); // Just for good measure.
+ if (jstr.get() != nullptr) {
+ ScopedUtfChars jstr_utf(env, jstr.get());
+ if (jstr_utf.c_str() != nullptr) {
+ exc_msg = jstr_utf.c_str();
+ } else {
+ env->ExceptionClear();
+ }
+ }
+ }
+
+ env->Throw(error);
+ ALOGE("java.lang.Error thrown during binder transaction (stack trace follows) : ");
+ env->ExceptionDescribe();
+
+ std::string error_msg = base::StringPrintf(
+ "java.lang.Error thrown during binder transaction: %s",
+ exc_msg.c_str());
+ env->FatalError(error_msg.c_str());
+}
+
+// Report a java.lang.Error (or subclass). This will terminate the runtime, either by
+// the uncaught exception handler, or explicitly by calling
+// report_java_lang_error_fatal_error.
+static void report_java_lang_error(JNIEnv* env, jthrowable error, const char* msg)
{
// Try to run the uncaught exception machinery.
jobject thread = env->CallStaticObjectMethod(gThreadDispatchOffsets.mClass,
@@ -187,77 +227,39 @@
}
// Some error occurred that meant that either dispatchUncaughtException could not be
// called or that it had an error itself (as this should be unreachable under normal
- // conditions). Clear the exception.
+ // conditions). As the binder code cannot handle Errors, attempt to log the error and
+ // abort.
env->ExceptionClear();
+ report_java_lang_error_fatal_error(env, error, msg);
}
static void report_exception(JNIEnv* env, jthrowable excep, const char* msg)
{
env->ExceptionClear();
- jstring tagstr = env->NewStringUTF(LOG_TAG);
- jstring msgstr = NULL;
- if (tagstr != NULL) {
- msgstr = env->NewStringUTF(msg);
+ ScopedLocalRef<jstring> tagstr(env, env->NewStringUTF(LOG_TAG));
+ ScopedLocalRef<jstring> msgstr(env);
+ if (tagstr != nullptr) {
+ msgstr.reset(env->NewStringUTF(msg));
}
- if ((tagstr == NULL) || (msgstr == NULL)) {
+ if ((tagstr != nullptr) && (msgstr != nullptr)) {
+ env->CallStaticIntMethod(gLogOffsets.mClass, gLogOffsets.mLogE,
+ tagstr.get(), msgstr.get(), excep);
+ if (env->ExceptionCheck()) {
+ // Attempting to log the failure has failed.
+ ALOGW("Failed trying to log exception, msg='%s'\n", msg);
+ env->ExceptionClear();
+ }
+ } else {
env->ExceptionClear(); /* assume exception (OOM?) was thrown */
ALOGE("Unable to call Log.e()\n");
ALOGE("%s", msg);
- goto bail;
- }
-
- env->CallStaticIntMethod(
- gLogOffsets.mClass, gLogOffsets.mLogE, tagstr, msgstr, excep);
- if (env->ExceptionCheck()) {
- /* attempting to log the failure has failed */
- ALOGW("Failed trying to log exception, msg='%s'\n", msg);
- env->ExceptionClear();
}
if (env->IsInstanceOf(excep, gErrorOffsets.mClass)) {
- // Try to report the error. This should not return under normal circumstances.
- report_java_lang_error(env, excep);
- // The traditional handling: re-raise and abort.
-
- /*
- * It's an Error: Reraise the exception and ask the runtime to abort.
- */
-
- // Try to get the exception string. Sometimes logcat isn't available,
- // so try to add it to the abort message.
- std::string exc_msg = "(Unknown exception message)";
- {
- ScopedLocalRef<jclass> exc_class(env, env->GetObjectClass(excep));
- jmethodID method_id = env->GetMethodID(exc_class.get(),
- "toString",
- "()Ljava/lang/String;");
- ScopedLocalRef<jstring> jstr(
- env,
- reinterpret_cast<jstring>(
- env->CallObjectMethod(excep, method_id)));
- env->ExceptionClear(); // Just for good measure.
- if (jstr.get() != nullptr) {
- ScopedUtfChars jstr_utf(env, jstr.get());
- exc_msg = jstr_utf.c_str();
- }
- }
-
- env->Throw(excep);
- ALOGE("java.lang.Error thrown during binder transaction (stack trace follows) : ");
- env->ExceptionDescribe();
-
- std::string error_msg = base::StringPrintf(
- "java.lang.Error thrown during binder transaction: %s",
- exc_msg.c_str());
- env->FatalError(error_msg.c_str());
+ report_java_lang_error(env, excep, msg);
}
-
-bail:
- /* discard local refs created for us by VM */
- env->DeleteLocalRef(tagstr);
- env->DeleteLocalRef(msgstr);
}
class JavaBBinderHolder;
@@ -309,14 +311,11 @@
code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);
if (env->ExceptionCheck()) {
- jthrowable excep = env->ExceptionOccurred();
- report_exception(env, excep,
+ ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
+ report_exception(env, excep.get(),
"*** Uncaught remote exception! "
"(Exceptions are not yet supported across processes.)");
res = JNI_FALSE;
-
- /* clean up JNI local ref -- we don't return to Java code */
- env->DeleteLocalRef(excep);
}
// Check if the strict mode state changed while processing the
@@ -328,11 +327,9 @@
}
if (env->ExceptionCheck()) {
- jthrowable excep = env->ExceptionOccurred();
- report_exception(env, excep,
+ ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
+ report_exception(env, excep.get(),
"*** Uncaught exception in onBinderStrictModePolicyChange");
- /* clean up JNI local ref -- we don't return to Java code */
- env->DeleteLocalRef(excep);
}
// Need to always call through the native implementation of
@@ -475,9 +472,8 @@
if (mObject != NULL) {
result = env->IsSameObject(obj, mObject);
} else {
- jobject me = env->NewLocalRef(mObjectWeak);
- result = env->IsSameObject(obj, me);
- env->DeleteLocalRef(me);
+ ScopedLocalRef<jobject> me(env, env->NewLocalRef(mObjectWeak));
+ result = env->IsSameObject(obj, me.get());
}
return result;
}
@@ -517,8 +513,8 @@
private:
JavaVM* const mVM;
- jobject mObject;
- jweak mObjectWeak; // will be a weak ref to the same VM-side DeathRecipient after binderDied()
+ jobject mObject; // Initial strong ref to Java-side DeathRecipient. Cleared on binderDied().
+ jweak mObjectWeak; // weak ref to the same Java-side DeathRecipient after binderDied().
wp<DeathRecipientList> mList;
};
@@ -590,7 +586,7 @@
env->DeleteGlobalRef((jobject)obj);
}
-static Mutex mProxyLock;
+static Mutex gProxyLock;
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
@@ -605,7 +601,7 @@
// For the rest of the function we will hold this lock, to serialize
// looking/creation/destruction of Java proxies for native Binder proxies.
- AutoMutex _l(mProxyLock);
+ AutoMutex _l(gProxyLock);
// Someone else's... do we know about it?
jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
@@ -1281,7 +1277,7 @@
static void android_os_BinderProxy_destroy(JNIEnv* env, jobject obj)
{
// Don't race with construction/initialization
- AutoMutex _l(mProxyLock);
+ AutoMutex _l(gProxyLock);
IBinder* b = (IBinder*)
env->GetLongField(obj, gBinderProxyOffsets.mObject);
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index bf8bbf5..a9b849e 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -373,6 +373,16 @@
}
}
+static void nativeSetColor(JNIEnv* env, jclass clazz, jlong nativeObject, jfloatArray fColor) {
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+ float* floatColors = env->GetFloatArrayElements(fColor, 0);
+ half3 color(floatColors[0], floatColors[1], floatColors[2]);
+ status_t err = ctrl->setColor(color);
+ if (err < 0 && err != NO_INIT) {
+ doThrowIAE(env);
+ }
+}
+
static void nativeSetMatrix(JNIEnv* env, jclass clazz, jlong nativeObject,
jfloat dsdx, jfloat dtdx, jfloat dtdy, jfloat dsdy) {
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
@@ -812,6 +822,8 @@
(void*)nativeSetTransparentRegionHint },
{"nativeSetAlpha", "(JF)V",
(void*)nativeSetAlpha },
+ {"nativeSetColor", "(J[F)V",
+ (void*)nativeSetColor },
{"nativeSetMatrix", "(JFFFF)V",
(void*)nativeSetMatrix },
{"nativeSetFlags", "(JII)V",
diff --git a/core/proto/android/app/notification_channel.proto b/core/proto/android/app/notification_channel.proto
new file mode 100644
index 0000000..bbc1956
--- /dev/null
+++ b/core/proto/android/app/notification_channel.proto
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+option java_package = "android.app";
+option java_multiple_files = true;
+
+package android.app;
+
+import "frameworks/base/core/proto/android/media/audioattributes.proto";
+
+/**
+ * An android.app.NotificationChannel object.
+ */
+message NotificationChannelProto {
+ string id = 1;
+ string name = 2;
+ string description = 3;
+ int32 importance = 4;
+ bool can_bypass_dnd = 5;
+ // Default is VISIBILITY_NO_OVERRIDE (-1000).
+ int32 lockscreen_visibility = 6;
+ string sound = 7;
+ bool use_lights = 8;
+ // Default is 0.
+ int32 light_color = 9;
+ repeated int64 vibration = 10;
+ // Bitwise representation of fields that have been changed by the user,
+ // preventing the app from making changes to these fields.
+ int32 user_locked_fields = 11;
+ bool is_vibration_enabled = 12;
+ // Default is true.
+ bool show_badge = 13;
+ // Default is false.
+ bool is_deleted = 14;
+ string group = 15;
+ android.media.AudioAttributesProto audio_attributes = 16;
+ // If this is a blockable system notification channel.
+ bool is_blockable_system = 17;
+}
diff --git a/core/proto/android/app/notification_channel_group.proto b/core/proto/android/app/notification_channel_group.proto
new file mode 100644
index 0000000..9cb456f
--- /dev/null
+++ b/core/proto/android/app/notification_channel_group.proto
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+option java_package = "android.app";
+option java_multiple_files = true;
+
+package android.app;
+
+import "frameworks/base/core/proto/android/app/notification_channel.proto";
+
+/**
+ * An android.app.NotificationChannelGroup object.
+ */
+message NotificationChannelGroupProto {
+ string id = 1;
+ string name = 2;
+ string description = 3;
+ bool is_blocked = 4;
+ repeated android.app.NotificationChannelProto channels = 5;
+}
diff --git a/core/proto/android/app/notificationmanager.proto b/core/proto/android/app/notificationmanager.proto
new file mode 100644
index 0000000..4dfd0cf
--- /dev/null
+++ b/core/proto/android/app/notificationmanager.proto
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+option java_package = "android.app";
+option java_multiple_files = true;
+
+package android.app;
+
+/**
+ * An android.app.NotificationMananger.Policy object.
+ */
+message PolicyProto {
+ enum Category {
+ CATEGORY_UNKNOWN = 0;
+ // Reminder notifications are prioritized.
+ REMINDERS = 1;
+ // Event notifications are prioritized.
+ EVENTS = 2;
+ // Message notifications are prioritized.
+ MESSAGES = 3;
+ // Calls are prioritized.
+ CALLS = 4;
+ // Calls from repeat callers are prioritized.
+ REPEAT_CALLERS = 5;
+ }
+ repeated Category priority_categories = 1;
+
+ enum Sender {
+ // Any sender is prioritized.
+ ANY = 0;
+ // Saved contacts are prioritized.
+ CONTACTS = 1;
+ // Only starred contacts are prioritized.
+ STARRED = 2;
+ }
+ Sender priority_call_sender = 2;
+ Sender priority_message_sender = 3;
+
+ enum SuppressedVisualEffect {
+ SVE_UNKNOWN = 0;
+ // Whether notifications suppressed by DND should not interrupt visually
+ // (e.g. with notification lights or by turning the screen on) when the
+ // screen is off.
+ SCREEN_OFF = 1;
+ // Whether notifications suppressed by DND should not interrupt visually
+ // when the screen is on (e.g. by peeking onto the screen).
+ SCREEN_ON = 2;
+ }
+ repeated SuppressedVisualEffect suppressed_visual_effects = 4;
+}
diff --git a/core/proto/android/media/audioattributes.proto b/core/proto/android/media/audioattributes.proto
new file mode 100644
index 0000000..3aa2792
--- /dev/null
+++ b/core/proto/android/media/audioattributes.proto
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+option java_package = "android.media";
+option java_multiple_files = true;
+
+package android.media;
+
+/**
+ * An android.media.AudioAttributes object.
+ */
+message AudioAttributesProto {
+ Usage usage = 1;
+ ContentType content_type = 2;
+ // Bit representation of set flags.
+ int32 flags = 3;
+ repeated string tags = 4;
+}
+
+enum ContentType {
+ // Content type value to use when the content type is unknown, or other than
+ // the ones defined.
+ CONTENT_TYPE_UNKNOWN = 0;
+ // Content type value to use when the content type is speech.
+ SPEECH = 1;
+ // Content type value to use when the content type is music.
+ MUSIC = 2;
+ // Content type value to use when the content type is a soundtrack,
+ // typically accompanying a movie or TV program.
+ MOVIE = 3;
+ // Content type value to use when the content type is a sound used to
+ // accompany a user action, such as a beep or sound effect expressing a key
+ // click, or event, such as the type of a sound for a bonus being received
+ // in a game. These sounds are mostly synthesized or short Foley sounds.
+ SONIFICATION = 4;
+}
+
+enum Usage {
+ // Usage value to use when the usage is unknown.
+ USAGE_UNKNOWN = 0;
+ // Usage value to use when the usage is media, such as music, or movie
+ // soundtracks.
+ MEDIA = 1;
+ // Usage value to use when the usage is voice communications, such as
+ // telephony or VoIP.
+ VOICE_COMMUNICATION = 2;
+ // Usage value to use when the usage is in-call signalling, such as with a
+ // "busy" beep, or DTMF tones.
+ VOICE_COMMUNICATION_SIGNALLING = 3;
+ // Usage value to use when the usage is an alarm (e.g. wake-up alarm).
+ ALARM = 4;
+ // Usage value to use when the usage is notification. Other notification
+ // usages are for more specialized uses.
+ NOTIFICATION = 5;
+ // Usage value to use when the usage is telephony ringtone.
+ NOTIFICATION_RINGTONE = 6;
+ // Usage value to use when the usage is a request to enter/end a
+ // communication, such as a VoIP communication or video-conference.
+ NOTIFICATION_COMMUNICATION_REQUEST = 7;
+ // Usage value to use when the usage is notification for an "instant"
+ // communication such as a chat, or SMS.
+ NOTIFICATION_COMMUNICATION_INSTANT = 8;
+ // Usage value to use when the usage is notification for a non-immediate
+ // type of communication such as e-mail.
+ NOTIFICATION_COMMUNICATION_DELAYED = 9;
+ // Usage value to use when the usage is to attract the user's attention,
+ // such as a reminder or low battery warning.
+ NOTIFICATION_EVENT = 10;
+ // Usage value to use when the usage is for accessibility, such as with a
+ // screen reader.
+ ASSISTANCE_ACCESSIBILITY = 11;
+ // Usage value to use when the usage is driving or navigation directions.
+ ASSISTANCE_NAVIGATION_GUIDANCE = 12;
+ // Usage value to use when the usage is sonification, such as with user
+ // interface sounds.
+ ASSISTANCE_SONIFICATION = 13;
+ // Usage value to use when the usage is for game audio.
+ GAME = 14;
+ // Usage value to use when feeding audio to the platform and replacing
+ // "traditional" audio source, such as audio capture devices.
+ VIRTUAL_SOURCE = 15;
+ // Usage value to use for audio responses to user queries, audio
+ // instructions or help utterances.
+ ASSISTANT = 16;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 7331a64..c3ceb1c 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -30,6 +30,7 @@
import "frameworks/base/core/proto/android/service/package.proto";
import "frameworks/base/core/proto/android/service/power.proto";
import "frameworks/base/core/proto/android/service/print.proto";
+import "frameworks/base/core/proto/android/service/procstats.proto";
import "frameworks/base/core/proto/android/providers/settings.proto";
import "frameworks/base/core/proto/android/os/incidentheader.proto";
import "frameworks/base/core/proto/android/os/kernelwake.proto";
@@ -71,13 +72,34 @@
(section).args = "fingerprint --proto --incident"
];
- android.service.NetworkStatsServiceDumpProto netstats = 3001;
+ android.service.NetworkStatsServiceDumpProto netstats = 3001 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "netstats --proto"
+ ];
+
android.providers.settings.SettingsServiceDumpProto settings = 3002;
android.service.appwidget.AppWidgetServiceDumpProto appwidget = 3003;
- android.service.battery.BatteryServiceDumpProto battery = 3006;
- android.service.diskstats.DiskStatsServiceDumpProto diskstats = 3007;
- android.service.notification.NotificationServiceDumpProto notification = 3004;
+ android.service.notification.NotificationServiceDumpProto notification = 3004 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "notification --proto"
+ ];
+
+ android.service.battery.BatteryServiceDumpProto battery = 3006 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "battery --proto"
+ ];
+
+ android.service.diskstats.DiskStatsServiceDumpProto diskstats = 3007 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "diskstats --proto"
+ ];
+
android.service.pm.PackageServiceDumpProto package = 3008;
android.service.power.PowerServiceDumpProto power = 3009;
android.service.print.PrintServiceDumpProto print = 3010;
+
+ android.service.procstats.ProcessStatsServiceDumpProto procstats = 3011 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "procstats --proto"
+ ];
}
diff --git a/core/proto/android/service/diskstats.proto b/core/proto/android/service/diskstats.proto
index 4d86526..4057e45 100644
--- a/core/proto/android/service/diskstats.proto
+++ b/core/proto/android/service/diskstats.proto
@@ -47,7 +47,7 @@
}
message DiskStatsCachedValuesProto {
- // Total app data size, in kilobytes
+ // Total app code size, in kilobytes
int64 agg_apps_size = 1;
// Total app cache size, in kilobytes
int64 agg_apps_cache_size = 2;
@@ -65,15 +65,19 @@
int64 other_size = 8;
// Sizes of individual packages
repeated DiskStatsAppSizesProto app_sizes = 9;
+ // Total app data size, in kilobytes
+ int64 agg_apps_data_size = 10;
}
message DiskStatsAppSizesProto {
// Name of the package
string package_name = 1;
- // App's data size in kilobytes
+ // App's code size in kilobytes
int64 app_size = 2;
// App's cache size in kilobytes
int64 cache_size = 3;
+ // App's data size in kilobytes
+ int64 app_data_size = 4;
}
message DiskStatsFreeSpaceProto {
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index 819460e..d8cb1a7 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -21,10 +21,27 @@
option java_multiple_files = true;
option java_outer_classname = "NotificationServiceProto";
+import "frameworks/base/core/proto/android/app/notification_channel.proto";
+import "frameworks/base/core/proto/android/app/notification_channel_group.proto";
+import "frameworks/base/core/proto/android/app/notificationmanager.proto";
+import "frameworks/base/core/proto/android/content/component_name.proto";
+
message NotificationServiceDumpProto {
repeated NotificationRecordProto records = 1;
ZenModeProto zen = 2;
+
+ ManagedServicesProto notification_listeners = 3;
+
+ int32 listener_hints = 4;
+
+ repeated ListenersDisablingEffectsProto listeners_disabling_effects = 5;
+
+ ManagedServicesProto notification_assistants = 6;
+
+ ManagedServicesProto condition_providers = 7;
+
+ RankingHelperProto ranking_config = 8;
}
message NotificationRecordProto {
@@ -40,6 +57,61 @@
int32 importance = 10;
}
+message ListenersDisablingEffectsProto {
+ int32 hint = 1;
+ repeated ManagedServiceInfoProto listeners = 2;
+}
+
+message ManagedServiceInfoProto {
+ android.content.ComponentNameProto component = 1;
+ int32 user_id = 2;
+ string service = 3;
+ bool is_system = 4;
+ bool is_guest = 5;
+}
+
+message ManagedServicesProto {
+ string caption = 1;
+
+ message ServiceProto {
+ repeated string name = 1;
+ int32 user_id = 2;
+ bool is_primary = 3;
+ }
+ repeated ServiceProto approved = 2;
+
+ // All of this type/caption enabled for current profiles.
+ repeated android.content.ComponentNameProto enabled = 3;
+
+
+ repeated ManagedServiceInfoProto live_services = 4;
+
+ // Snoozed for current profiles.
+ repeated android.content.ComponentNameProto snoozed = 5;
+}
+
+message RankingHelperProto {
+ repeated string notification_signal_extractors = 1;
+
+ message RecordProto {
+ string package = 1;
+ // Default value is UNKNOWN_UID = USER_NULL = -10000.
+ int32 uid = 2;
+ // Default is IMPORTANCE_UNSPECIFIED (-1000).
+ int32 importance = 3;
+ // Default is PRIORITY_DEFAULT (0).
+ int32 priority = 4;
+ // Default is VISIBILITY_NO_OVERRIDE (-1000).
+ int32 visibility = 5;
+ // Default is true.
+ bool show_badge = 6;
+ repeated android.app.NotificationChannelProto channels = 7;
+ repeated android.app.NotificationChannelGroupProto channel_groups = 8;
+ }
+ repeated RecordProto records = 2;
+ repeated RecordProto records_restored_without_uid = 3;
+}
+
enum State {
ENQUEUED = 0;
@@ -53,7 +125,7 @@
repeated string enabled_active_conditions = 2;
int32 suppressed_effects = 3;
repeated string suppressors = 4;
- string policy = 5;
+ android.app.PolicyProto policy = 5;
}
enum ZenMode {
@@ -61,4 +133,4 @@
ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1;
ZEN_MODE_NO_INTERRUPTIONS = 2;
ZEN_MODE_ALARMS = 3;
-}
\ No newline at end of file
+}
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
new file mode 100644
index 0000000..322b212
--- /dev/null
+++ b/core/proto/android/service/procstats.proto
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_outer_classname = "ProcessStatsServiceProto";
+
+import "frameworks/base/core/proto/android/util/common.proto";
+
+package android.service.procstats;
+
+/**
+ * Data from ProcStatsService Dumpsys
+ *
+ * Next Tag: 4
+ */
+message ProcessStatsServiceDumpProto {
+
+ ProcessStatsSectionProto procstats_now = 1;
+
+ ProcessStatsSectionProto procstats_over_3hrs = 2;
+
+ ProcessStatsSectionProto procstats_over_24hrs = 3;
+}
+
+/**
+ * Data model from /frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
+ * This proto is defined based on the writeToParcel method.
+ *
+ * Next Tag: 9
+ */
+message ProcessStatsSectionProto {
+
+ // Elapsed realtime at start of report.
+ int64 start_realtime_ms = 1;
+
+ // Elapsed realtime at end of report.
+ int64 end_realtime_ms = 2;
+
+ // CPU uptime at start of report.
+ int64 start_uptime_ms = 3;
+
+ // CPU uptime at end of report.
+ int64 end_uptime_ms = 4;
+
+ // System runtime library. e.g. "libdvm.so", "libart.so".
+ string runtime = 5;
+
+ // whether kernel reports swapped pss.
+ bool has_swapped_pss = 6;
+
+ // Data completeness. e.g. "complete", "partial", shutdown", or "sysprops".
+ enum Status {
+ STATUS_UNKNOWN = 0;
+ STATUS_COMPLETE = 1;
+ STATUS_PARTIAL = 2;
+ STATUS_SHUTDOWN = 3;
+ STATUS_SYSPROPS = 4;
+ }
+ repeated Status status = 7;
+
+ // Stats for each process.
+ repeated ProcessStatsProto process_stats = 8;
+}
+
+// Next Tag: 6
+message ProcessStatsProto {
+
+ // Name of process.
+ string process = 1;
+
+ // Uid of the process.
+ int32 uid = 2;
+
+ // Information about how often kills occurred
+ message Kill {
+ // Count of excessive CPU kills
+ int32 cpu = 1;
+
+ // Count of kills when cached
+ int32 cached = 2;
+
+ // PSS stats during cached kill
+ android.util.AggStats cached_pss = 3;
+ }
+ Kill kill = 3;
+
+ message State {
+ enum ScreenState {
+ SCREEN_UNKNOWN = 0;
+ OFF = 1;
+ ON = 2;
+ }
+ ScreenState screen_state = 1;
+
+ enum MemoryState {
+ MEMORY_UNKNOWN = 0;
+ NORMAL = 1; // normal.
+ MODERATE = 2; // moderate memory pressure.
+ LOW = 3; // low memory.
+ CRITICAL = 4; // critical memory.
+ }
+ MemoryState memory_state = 2;
+
+ enum ProcessState {
+ PROCESS_UNKNOWN = 0;
+ // Persistent system process.
+ PERSISTENT = 1;
+ // Top activity; actually any visible activity.
+ TOP = 2;
+ // Important foreground process (ime, wallpaper, etc).
+ IMPORTANT_FOREGROUND = 3;
+ // Important background process.
+ IMPORTANT_BACKGROUND = 4;
+ // Performing backup operation.
+ BACKUP = 5;
+ // Heavy-weight process (currently not used).
+ HEAVY_WEIGHT = 6;
+ // Background process running a service.
+ SERVICE = 7;
+ // Process not running, but would be if there was enough RAM.
+ SERVICE_RESTARTING = 8;
+ // Process running a receiver.
+ RECEIVER = 9;
+ // Process hosting home/launcher app when not on top.
+ HOME = 10;
+ // Process hosting the last app the user was in.
+ LAST_ACTIVITY = 11;
+ // Cached process hosting a previous activity.
+ CACHED_ACTIVITY = 12;
+ // Cached process hosting a client activity.
+ CACHED_ACTIVITY_CLIENT = 13;
+ // Cached process that is empty.
+ CACHED_EMPTY = 14;
+ }
+ ProcessState process_state = 3;
+
+ // Millisecond duration spent in this state
+ int64 duration_ms = 4;
+
+ // # of samples taken
+ int32 sample_size = 5;
+
+ // PSS is memory reserved for this process
+ android.util.AggStats pss = 6;
+
+ // USS is memory shared between processes, divided evenly for accounting
+ android.util.AggStats uss = 7;
+ }
+ repeated State states = 5;
+}
diff --git a/core/proto/android/util/common.proto b/core/proto/android/util/common.proto
new file mode 100644
index 0000000..6dd4c02
--- /dev/null
+++ b/core/proto/android/util/common.proto
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+package android.util;
+
+option java_multiple_files = true;
+
+/**
+ * Very basic data structure used by aggregated stats.
+ */
+message AggStats {
+
+ int64 min = 1;
+
+ int64 average = 2;
+
+ int64 max = 3;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 695fdac..6a9afbc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -204,8 +204,8 @@
android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" />
<protected-broadcast
android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
- <protected-broadcast android:name="android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.btopp.intent.action.INCOMING_FILE_NOTIFICATION" />
<protected-broadcast android:name="android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT" />
@@ -1873,11 +1873,11 @@
<!-- @SystemApi @hide Allows an application to create/manage/remove stacks -->
<permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi @hide Allows an application to embed other activities -->
<permission android:name="android.permission.ACTIVITY_EMBEDDING"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|development" />
<!-- Allows an application to start any activity, regardless of permission
protection or exported state.
@@ -3106,6 +3106,13 @@
<permission android:name="android.permission.BIND_APPWIDGET"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to bind app's slices and get their
+ content. This content will be surfaced to the
+ user and not to leave the device.
+ <p>Not for use by third-party applications. @hide -->
+ <permission android:name="android.permission.BIND_SLICE"
+ android:protectionLevel="signature|privileged|development" />
+
<!-- @SystemApi Private permission, to restrict who can bring up a dialog to add a new
keyguard widget
@hide -->
diff --git a/core/res/res/drawable/ic_ab_back_material_settings.xml b/core/res/res/drawable/ic_ab_back_material_settings.xml
new file mode 100644
index 0000000..7325a41
--- /dev/null
+++ b/core/res/res/drawable/ic_ab_back_material_settings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:autoMirrored="true"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@color/white"
+ android:pathData="M20,11H7.62l4.88,-4.88a0.996,0.996 0,1 0,-1.41 -1.41l-6.94,6.94c-0.2,0.2 -0.2,0.51 0,0.71l6.94,6.94a0.996,0.996 0,1 0,1.41 -1.41L7.62,13H20c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1z"/>
+</vector>
diff --git a/core/res/res/layout/magnifier.xml b/core/res/res/layout/magnifier.xml
new file mode 100644
index 0000000..181e5e5
--- /dev/null
+++ b/core/res/res/layout/magnifier.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/floatingToolbarPopupBackgroundDrawable">
+ <ImageView
+ android:id="@+id/magnifier_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
index 76b3528..ee5c758 100644
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -70,6 +70,8 @@
android:textSize="16sp"
android:textColor="#eeffffff"
android:layout_marginTop="4dp"
+ android:ellipsize="end"
+ android:maxLines="7"
/>
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index d3ba7ef..9e4184b 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Foon"</string>
<string name="map" msgid="6068210738233985748">"Kaarte"</string>
<string name="browse" msgid="6993590095938149861">"Blaaier"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Bergingspasie word min"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sommige stelselfunksies werk moontlik nie"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nie genoeg berging vir die stelsel nie. Maak seker jy het 250 MB spasie beskikbaar en herbegin."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Kanselleer"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Kanselleer"</string>
+ <string name="close" msgid="2318214661230355730">"MAAK TOE"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Aandag"</string>
<string name="loading" msgid="7933681260296021180">"Laai tans..."</string>
<string name="capital_on" msgid="1544682755514494298">"AAN"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Skaal"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Wys altyd"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Heraktiveer hierdie in Stelselinstellings > Programme > Afgelaai."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Program reageer nie"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> gebruik dalk te veel berging."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> steun nie die huidige skermgrootte-instelling nie en sal dalk onverwags reageer."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Wys altyd"</string>
<string name="smv_application" msgid="3307209192155442829">"Die program <xliff:g id="APPLICATION">%1$s</xliff:g> (proses <xliff:g id="PROCESS">%2$s</xliff:g>) het sy selfopgelegde StrictMode-beleid oortree."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Foon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Oorfone"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dokluidsprekers"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Oorfone"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Stelsel"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-oudio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Draadlose skerm"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index a3c2cab..00257ee 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"ስልክ"</string>
<string name="map" msgid="6068210738233985748">"ካርታዎች"</string>
<string name="browse" msgid="6993590095938149861">"አሳሽ"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"የማከማቻ ቦታ እያለቀ ነው"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"አንዳንድ የስርዓት ተግባራት ላይሰሩ ይችላሉ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ለስርዓቱ የሚሆን በቂ ቦታ የለም። 250 ሜባ ነጻ ቦታ እንዳለዎት ያረጋግጡና ዳግም ያስጀምሩ።"</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"ይቅር"</string>
<string name="yes" msgid="5362982303337969312">"እሺ"</string>
<string name="no" msgid="5141531044935541497">"ይቅር"</string>
+ <string name="close" msgid="2318214661230355730">"ዝጋ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"ትኩረት"</string>
<string name="loading" msgid="7933681260296021180">"በመጫን ላይ…"</string>
<string name="capital_on" msgid="1544682755514494298">"በ"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"የልኬት ለውጥ"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"ሁልጊዜ አሳይ"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"በስርዓት ቅንብሮች ውስጥ ይሄንን ዳግም አንቃ> Apps &gt፤ወርዷል፡፡"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"መተግበሪያው ምላሽ እየሰጠ አይደለም"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> በጣም ብዙ ማህደረ ትውስታ እየተጠቀመ ሊሆን ይችላል።"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> አሁን ያለውን የማሳያ መጠን ቅንብር አይደግፍም እና ያልተጠብቀ ባሕሪ ሊያሳይ ይችላል።"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"ሁልጊዜ አሳይ"</string>
<string name="smv_application" msgid="3307209192155442829">"መተግበሪያው <xliff:g id="APPLICATION">%1$s</xliff:g>( ሂደት<xliff:g id="PROCESS">%2$s</xliff:g>) በራስ ተነሳሺ StrictMode ደንብን ይተላለፋል።"</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ጡባዊ ተኮ"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ቴሌቪዥን"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"ስልክ"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"የጆሮ ማዳመጫዎች"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"የትከል ድምፅ ማጉያዎች"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"ኤችዲኤምአይ"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"የጆሮ ማዳመጫዎች"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"ዩ ኤስ ቢ"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ስርዓት"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"የብሉቱዝ ድምጽ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ገመድ አልባ ማሳያ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 5ff51f2..8f78cfa 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1060,6 +1060,10 @@
<string name="dial" msgid="4204975095406423102">"الهاتف"</string>
<string name="map" msgid="6068210738233985748">"الخرائط"</string>
<string name="browse" msgid="6993590095938149861">"المتصفح"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"مساحة التخزين منخفضة"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"قد لا تعمل بعض وظائف النظام"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ليست هناك سعة تخزينية كافية للنظام. تأكد من أنه لديك مساحة خالية تبلغ ٢٥٠ ميغابايت وأعد التشغيل."</string>
@@ -1069,6 +1073,7 @@
<string name="cancel" msgid="6442560571259935130">"إلغاء"</string>
<string name="yes" msgid="5362982303337969312">"حسنًا"</string>
<string name="no" msgid="5141531044935541497">"إلغاء"</string>
+ <string name="close" msgid="2318214661230355730">"إغلاق"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"تنبيه"</string>
<string name="loading" msgid="7933681260296021180">"جارٍ التحميل…"</string>
<string name="capital_on" msgid="1544682755514494298">"تشغيل"</string>
@@ -1125,6 +1130,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"تدرج"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"الإظهار دائمًا"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"يمكنك إعادة تمكين هذا في إعدادات النظام > التطبيقات > ما تم تنزيله."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"التطبيق لا يستجيب"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"ربما يشغل <xliff:g id="APP_NAME">%1$s</xliff:g> مساحة كبيرة جدًا من الذاكرة."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> غير متوافق مع الإعداد الحالي لحجم شاشة العرض وربما يعمل بطريقة غير متوقعة."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"العرض دائمًا"</string>
<string name="smv_application" msgid="3307209192155442829">"انتهك التطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> (العملية <xliff:g id="PROCESS">%2$s</xliff:g>) سياسة StrictMode المفروضة ذاتيًا."</string>
@@ -1512,9 +1519,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"الجهاز اللوحي"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"التلفزيون"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"الهاتف"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"سماعات رأس"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"مكبرات صوت للإرساء"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"سماعات رأس"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"النظام"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"صوت بلوتوث"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"عرض شاشة لاسلكي"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index bd63c27..97a67e0 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Xəritə"</string>
<string name="browse" msgid="6993590095938149861">"Brauzer"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Yaddaş yeri bitir"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bəzi sistem funksiyaları işləməyə bilər"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistem üçün yetərincə yaddaş ehtiyatı yoxdur. 250 MB yaddaş ehtiyatının olmasına əmin olun və yenidən başladın."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Ləğv et"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Ləğv et"</string>
+ <string name="close" msgid="2318214661230355730">"BAĞLAYIN"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Diqqət"</string>
<string name="loading" msgid="7933681260296021180">"Yüklənir…"</string>
<string name="capital_on" msgid="1544682755514494298">"AÇIQ"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Miqyas"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Həmişə göstər"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Bunları Sistem ayarlarında yenidən aktivləşdir Yüklənmiş > Tətbiqlər >."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Tətbiq cavab vermir"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> daha çox yaddaş istifadə edə bilər."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> cari Ekran ölçüsü ayarını dəstəkləmir və gözlənilməz şəkildə davrana bilər."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Həmişə göstərin"</string>
<string name="smv_application" msgid="3307209192155442829">"Tətbiq <xliff:g id="APPLICATION">%1$s</xliff:g> (proses <xliff:g id="PROCESS">%2$s</xliff:g>) StrictMode siyasətini pozdu."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Planşet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Qulaqlıq"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dok spikerlər"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Qulaqlıq"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Simsiz ekran"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index e362a87..2daf942 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1000,6 +1000,10 @@
<string name="dial" msgid="4204975095406423102">"Pozovi"</string>
<string name="map" msgid="6068210738233985748">"Mape"</string>
<string name="browse" msgid="6993590095938149861">"Pregledač"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Memorijski prostor je na izmaku"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke sistemske funkcije možda ne funkcionišu"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno memorijskog prostora za sistem. Uverite se da imate 250 MB slobodnog prostora i ponovo pokrenite."</string>
@@ -1009,6 +1013,7 @@
<string name="cancel" msgid="6442560571259935130">"Otkaži"</string>
<string name="yes" msgid="5362982303337969312">"Potvrdi"</string>
<string name="no" msgid="5141531044935541497">"Otkaži"</string>
+ <string name="close" msgid="2318214661230355730">"ZATVORI"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Pažnja"</string>
<string name="loading" msgid="7933681260296021180">"Učitava se…"</string>
<string name="capital_on" msgid="1544682755514494298">"DA"</string>
@@ -1065,6 +1070,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Razmera"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Uvek prikazuj"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Ponovo omogućite u meniju Sistemska podešavanja > Aplikacije > Preuzeto."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Aplikacija ne reaguje"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> možda koristi previše memorije."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> ne podržava trenutno podešavanje veličine prikaza i može da se ponaša neočekivano."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Uvek prikazuj"</string>
<string name="smv_application" msgid="3307209192155442829">"Aplikacija <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) je prekršila samonametnute StrictMode smernice."</string>
@@ -1443,9 +1450,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Slušalice"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Zvučnici bazne stanice"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Slušalice"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Bežični ekran"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index fdbe702..c89987b 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1020,6 +1020,10 @@
<string name="dial" msgid="4204975095406423102">"Тэлефон"</string>
<string name="map" msgid="6068210738233985748">"Карты"</string>
<string name="browse" msgid="6993590095938149861">"Браўзер"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Месца для захавання на зыходзе"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некаторыя сістэмныя функцыі могуць не працаваць"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Не хапае сховішча для сістэмы. Пераканайцеся, што ў вас ёсць 250 МБ свабоднага месца, і перазапусціце."</string>
@@ -1029,6 +1033,7 @@
<string name="cancel" msgid="6442560571259935130">"Скасаваць"</string>
<string name="yes" msgid="5362982303337969312">"ОК"</string>
<string name="no" msgid="5141531044935541497">"Скасаваць"</string>
+ <string name="close" msgid="2318214661230355730">"ЗАКРЫЦЬ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Увага"</string>
<string name="loading" msgid="7933681260296021180">"Загрузка..."</string>
<string name="capital_on" msgid="1544682755514494298">"Уключыць"</string>
@@ -1085,6 +1090,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Шкала"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Заўсёды паказваць"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Зноў уключыце гэта ў раздзеле \"Сістэмныя налады > Прыкладанні > Спампаваныя\"."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Праграма не адказвае"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> можа выкарыстоўваць занадта шмат памяці."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> не падтрымлівае бягучую наладу Памеру дысплэя і можа паводзіць сябе непрадказальным чынам."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Заўсёды паказваць"</string>
<string name="smv_application" msgid="3307209192155442829">"Прыкладанне <xliff:g id="APPLICATION">%1$s</xliff:g> (працэс <xliff:g id="PROCESS">%2$s</xliff:g>) парушыла ўласную палітыку StrictMode."</string>
@@ -1466,9 +1473,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Планшэт"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ТБ"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Тэлефон"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Навушнікі"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Дынамікі станцыi"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Навушнікі"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Сістэма"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-аўдыё"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Бесправадны дысплей"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index ae4cc61..07bdc3d 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Телефон"</string>
<string name="map" msgid="6068210738233985748">"Карти"</string>
<string name="browse" msgid="6993590095938149861">"Браузър"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Мястото в хранилището е на изчерпване"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Възможно е някои функции на системата да не работят"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"За системата няма достатъчно място в хранилището. Уверете се, че имате свободни 250 МБ, и рестартирайте."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Отказ"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Отказ"</string>
+ <string name="close" msgid="2318214661230355730">"ЗАТВАРЯНЕ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Внимание"</string>
<string name="loading" msgid="7933681260296021180">"Зарежда се..."</string>
<string name="capital_on" msgid="1544682755514494298">"ВКЛ"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Мащаб"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Винаги да се показва"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Активирайте отново това в „Системни настройки“ > „Приложения“ > „Изтеглени“."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Приложението не реагира"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Възможно е <xliff:g id="APP_NAME">%1$s</xliff:g> да използва твърде много памет."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> не поддържа текущата настройка за размер на дисплея и може да се държи по неочакван начин."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Винаги да се показва"</string>
<string name="smv_application" msgid="3307209192155442829">"Приложението „<xliff:g id="APPLICATION">%1$s</xliff:g>“ (процес „<xliff:g id="PROCESS">%2$s</xliff:g>“) наруши правилото за стриктен режим, наложено от самото него."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Таблет"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Телевизор"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Телефон"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Слушалки"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Докинг станц.: Високогов."</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Слушалки"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Система"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Звук през Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Безжичен дисплей"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index e3ecfda..5d19486 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"ফোন করুন"</string>
<string name="map" msgid="6068210738233985748">"মানচিত্র"</string>
<string name="browse" msgid="6993590095938149861">"ব্রাউজার"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"স্টোরেজ পূর্ণ হতে চলেছে"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"কিছু কিছু সিস্টেম ক্রিয়াকলাপ কাজ নাও করতে পারে"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"সিস্টেমের জন্য যথেষ্ট স্টোরেজ নেই৷ আপনার কাছে ২৫০এমবি ফাঁকা স্থান রয়েছে কিনা সে বিষয়ে নিশ্চিত হন এবং সিস্টেম চালু করুন৷"</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"বাতিল করুন"</string>
<string name="yes" msgid="5362982303337969312">"ঠিক আছে"</string>
<string name="no" msgid="5141531044935541497">"বাতিল করুন"</string>
+ <string name="close" msgid="2318214661230355730">"বন্ধ করুন"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"খেয়াল করুন"</string>
<string name="loading" msgid="7933681260296021180">"লোড হচ্ছে..."</string>
<string name="capital_on" msgid="1544682755514494298">"চালু করুন"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"স্কেল"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"সবসময় দেখান"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"সিস্টেম সেটিংস> অ্যাপ্স> ডাউনলোড করাগুলি এ এটি পুনঃসক্ষম করুন৷"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"অ্যাপটি সাড়া দিচ্ছে না"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"মনে হচ্ছে <xliff:g id="APP_NAME">%1$s</xliff:g> খুব বেশি মেমরি ব্যবহার করছে।"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g>, বর্তমান প্রদর্শনের আকারের সেটিংস সমর্থন করে না এবং অপ্রত্যাশিত আচরণ করতে পারে৷"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"সর্বদা দেখান"</string>
<string name="smv_application" msgid="3307209192155442829">"অ্যাপ্লিকেশানটি <xliff:g id="APPLICATION">%1$s</xliff:g> (প্রক্রিয়া <xliff:g id="PROCESS">%2$s</xliff:g>) তার স্ব-প্রয়োগ করা কঠোর মোড নীতি লঙ্ঘন করেছে৷"</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ট্যাবলেট"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"টিভি"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"ফোন"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"হেডফোন"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"ডক স্পিকার"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"হেডফোন"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"সিস্টেম"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ব্লুটুথ অডিও"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ওয়্যারলেস প্রদর্শন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 232a4e1..31ecfc5 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -980,7 +980,7 @@
<string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
<string name="selectAll" msgid="6876518925844129331">"Odaberi sve"</string>
<string name="cut" msgid="3092569408438626261">"Izreži"</string>
- <string name="copy" msgid="2681946229533511987">"Kopirajte"</string>
+ <string name="copy" msgid="2681946229533511987">"Kopiraj"</string>
<string name="failed_to_copy_to_clipboard" msgid="1833662432489814471">"Kopiranje u spremnik nije uspjelo"</string>
<string name="paste" msgid="5629880836805036433">"Zalijepi"</string>
<string name="paste_as_plain_text" msgid="5427792741908010675">"Zalijepi kao neformatiran tekst"</string>
@@ -1000,6 +1000,10 @@
<string name="dial" msgid="4204975095406423102">"Pozovi"</string>
<string name="map" msgid="6068210738233985748">"Mape"</string>
<string name="browse" msgid="6993590095938149861">"Preglednik"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke funkcije sistema možda neće raditi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno prostora za sistem. Obezbijedite 250MB slobodnog prostora i ponovo pokrenite uređaj."</string>
@@ -1009,6 +1013,7 @@
<string name="cancel" msgid="6442560571259935130">"Otkaži"</string>
<string name="yes" msgid="5362982303337969312">"Uredu"</string>
<string name="no" msgid="5141531044935541497">"Otkaži"</string>
+ <string name="close" msgid="2318214661230355730">"ZATVORI"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Pažnja"</string>
<string name="loading" msgid="7933681260296021180">"Učitavanje..."</string>
<string name="capital_on" msgid="1544682755514494298">"Uključeno"</string>
@@ -1067,6 +1072,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Razmjer"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Uvijek prikaži"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Ponovo omogućite ovu opciju u meniju Postavke sistema > Aplikacije > Preuzete aplikacije."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Aplikacija ne reagira"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Moguće je da aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> koristi previše memorije."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> ne podržava trenutnu postavku veličine ekrana i može se ponašati neočekivano."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Uvijek prikaži"</string>
<string name="smv_application" msgid="3307209192155442829">"Aplikacija <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) prekršila je vlastita StrictMode pravila."</string>
@@ -1078,7 +1085,7 @@
<string name="android_upgrading_notification_body" msgid="5761201379457064286">"Neke aplikacije možda neće raditi ispravno dok traje nadogradnja"</string>
<string name="app_upgrading_toast" msgid="3008139776215597053">"Aplikacija <xliff:g id="APPLICATION">%1$s</xliff:g> se nadograđuje…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"Optimiziranje aplikacije <xliff:g id="NUMBER_0">%1$d</xliff:g> od <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
- <string name="android_preparing_apk" msgid="8162599310274079154">"Priprema se <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
+ <string name="android_preparing_apk" msgid="8162599310274079154">"Pripremanje aplikacije <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Pokretanje aplikacija."</string>
<string name="android_upgrading_complete" msgid="1405954754112999229">"Pokretanje pri kraju."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"Pokrenuta je aplikacija <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -1445,9 +1452,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Slušalice"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Zvučnici priključne stanice"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Slušalice"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Bežični prikaz"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index d713c1b0..082513d 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Truca"</string>
<string name="map" msgid="6068210738233985748">"Mapes"</string>
<string name="browse" msgid="6993590095938149861">"Navegador"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"L\'espai d\'emmagatzematge s\'està esgotant"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"És possible que algunes funcions del sistema no funcionin"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"No hi ha prou espai d\'emmagatzematge per al sistema. Comprova que tinguis 250 MB d\'espai lliure i reinicia."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Cancel·la"</string>
<string name="yes" msgid="5362982303337969312">"D\'acord"</string>
<string name="no" msgid="5141531044935541497">"Cancel·la"</string>
+ <string name="close" msgid="2318214661230355730">"TANCA"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Atenció"</string>
<string name="loading" msgid="7933681260296021180">"S\'està carregant…"</string>
<string name="capital_on" msgid="1544682755514494298">"SÍ"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Escala"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Mostra sempre"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Torna a activar-ho a Configuració del sistema > Aplicacions > Baixades."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"L\'aplicació no respon"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"És possible que <xliff:g id="APP_NAME">%1$s</xliff:g> faci servir massa memòria."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> no admet la mida de pantalla actual i és possible que funcioni de manera inesperada."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Mostra sempre"</string>
<string name="smv_application" msgid="3307209192155442829">"L\'aplicació <xliff:g id="APPLICATION">%1$s</xliff:g>(procés <xliff:g id="PROCESS">%2$s</xliff:g>) ha incomplert la seva política autoimposada de mode estricte."</string>
@@ -1285,7 +1292,7 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fons de pantalla"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Canvia el fons de pantalla"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Oient de notificacions"</string>
- <string name="vr_listener_binding_label" msgid="4316591939343607306">"Processador de RV"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Processador d\'RV"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Proveïdor de condicions"</string>
<string name="notification_ranker_binding_label" msgid="774540592299064747">"Servei de classificació de notificacions"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activada"</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tauleta"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televisor"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telèfon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Auriculars"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Altaveus de la base"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Auriculars"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Àudio per Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Pantalla sense fil"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 31e7487..23f3cb9 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1020,6 +1020,10 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Mapy"</string>
<string name="browse" msgid="6993590095938149861">"Prohlížeč"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"V úložišti je málo místa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Některé systémové funkce nemusí fungovat"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Pro systém není dostatek místa v úložišti. Uvolněte alespoň 250 MB místa a restartujte zařízení."</string>
@@ -1029,6 +1033,7 @@
<string name="cancel" msgid="6442560571259935130">"Zrušit"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Zrušit"</string>
+ <string name="close" msgid="2318214661230355730">"ZAVŘÍT"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Upozornění"</string>
<string name="loading" msgid="7933681260296021180">"Načítání..."</string>
<string name="capital_on" msgid="1544682755514494298">"I"</string>
@@ -1085,6 +1090,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Měřítko"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Vždy zobrazovat"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Tento režim znovu povolíte v sekci Nastavení systému > Aplikace > Stažené."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Aplikace nereaguje"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> pravděpodobně využívá příliš mnoho paměti."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> aktuální nastavení velikosti zobrazení nepodporuje a může se chovat neočekávaně."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Vždy zobrazovat"</string>
<string name="smv_application" msgid="3307209192155442829">"Aplikace <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) porušila své vlastní vynucené zásady StrictMode."</string>
@@ -1466,9 +1473,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televize"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Sluchátka"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Reproduktory doku"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Sluchátka"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Systém"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth Audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Bezdrátový displej"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index f1ba174..9360cbe 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Opkald"</string>
<string name="map" msgid="6068210738233985748">"Kort"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der er snart ikke mere lagerplads"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nogle systemfunktioner virker måske ikke"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Der er ikke nok ledig lagerplads til systemet. Sørg for, at du har 250 MB ledig plads, og genstart."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Annuller"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Annuller"</string>
+ <string name="close" msgid="2318214661230355730">"LUK"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Bemærk"</string>
<string name="loading" msgid="7933681260296021180">"Indlæser…"</string>
<string name="capital_on" msgid="1544682755514494298">"TIL"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Skaler"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Vis altid"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Aktivér dette igen i Systemindstillinger > Apps > Downloadet."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Appen svarer ikke"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> bruger muligvis for meget hukommelse."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> understøtter ikke den aktuelle indstilling for visningsstørrelse og vil muligvis ikke fungere som forventet."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Vis altid"</string>
<string name="smv_application" msgid="3307209192155442829">"Appen <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) har overtrådt sin egen StrictMode-politik."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Tv"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Hovedtelefoner"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dockstationens højttalere"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Hovedtelefoner"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-lyd"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Trådløs skærm"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index cc510d7..8b31d96 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Karten"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der Speicherplatz wird knapp"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Einige Systemfunktionen funktionieren möglicherweise nicht."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Der Speicherplatz reicht nicht für das System aus. Stelle sicher, dass 250 MB freier Speicherplatz vorhanden sind, und starte das Gerät dann neu."</string>
@@ -989,6 +993,8 @@
<string name="cancel" msgid="6442560571259935130">"Abbrechen"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Abbrechen"</string>
+ <!-- no translation found for close (2318214661230355730) -->
+ <skip />
<string name="dialog_alert_title" msgid="2049658708609043103">"Achtung"</string>
<string name="loading" msgid="7933681260296021180">"Wird geladen…"</string>
<string name="capital_on" msgid="1544682755514494298">"AN"</string>
@@ -1045,6 +1051,10 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Skalieren"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Immer anzeigen"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Eine erneute Aktivierung ist in den Systemeinstellungen unter \"Apps > Heruntergeladen\" möglich."</string>
+ <!-- no translation found for top_app_killed_title (6814231368167994497) -->
+ <skip />
+ <!-- no translation found for top_app_killed_message (3487519022191609844) -->
+ <skip />
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> unterstützt nicht die aktuelle Einstellung für die Anzeigegröße, sodass ein unerwartetes Verhalten auftreten kann."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Immer anzeigen"</string>
<string name="smv_application" msgid="3307209192155442829">"Die App <xliff:g id="APPLICATION">%1$s</xliff:g> (Prozess <xliff:g id="PROCESS">%2$s</xliff:g>) hat gegen deine selbsterzwungene StrictMode-Richtlinie verstoßen."</string>
@@ -1420,9 +1430,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Kopfhörer"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dock-Lautsprecher"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Kopfhörer"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-Audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Kabellose Übertragung (WiDi)"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index ede305c..19aafc8 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Τηλέφωνο"</string>
<string name="map" msgid="6068210738233985748">"Χάρτες"</string>
<string name="browse" msgid="6993590095938149861">"Πρόγραμμα περιήγησης"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ο αποθηκευτικός χώρος εξαντλείται"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Ορισμένες λειτουργίες συστήματος ενδέχεται να μην λειτουργούν"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Δεν υπάρχει αρκετός αποθηκευτικός χώρος για το σύστημα. Βεβαιωθείτε ότι διαθέτετε 250 MB ελεύθερου χώρου και κάντε επανεκκίνηση."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Ακύρωση"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Ακύρωση"</string>
+ <string name="close" msgid="2318214661230355730">"ΚΛΕΙΣΙΜΟ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Προσοχή"</string>
<string name="loading" msgid="7933681260296021180">"Φόρτωση…"</string>
<string name="capital_on" msgid="1544682755514494298">"Ενεργό"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Κλίμακα"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Να εμφανίζονται πάντα"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Ενεργοποιήστε το ξανά στις Ρυθμίσεις συστημάτων > Εφαρμογές > Ληφθείσες."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Η εφαρμογή δεν αποκρίνεται"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> μπορεί να χρησιμοποιεί υπερβολική μνήμη."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> δεν υποστηρίζει την τρέχουσα ρύθμιση Μεγέθους οθόνης και ενδέχεται να παρουσιάζει μη αναμενόμενη συμπεριφορά."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Να εμφανίζεται πάντα"</string>
<string name="smv_application" msgid="3307209192155442829">"Η εφαρμογή <xliff:g id="APPLICATION">%1$s</xliff:g> (διεργασία <xliff:g id="PROCESS">%2$s</xliff:g>) παραβίασε την αυτοεπιβαλλόμενη πολιτική StrictMode."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Τηλεόραση"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Τηλέφωνο"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Ακουστικά"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Ηχεία βάσης σύνδεσης"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Ακουστικά"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Σύστημα"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Ήχος Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Ασύρματη οθόνη"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index fa7f180..7fff878 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Phone"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Cancel"</string>
+ <string name="close" msgid="2318214661230355730">"CLOSE"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Attention"</string>
<string name="loading" msgid="7933681260296021180">"Loading…"</string>
<string name="capital_on" msgid="1544682755514494298">"ON"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Scale"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Always show"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Re-enable this in System settings > Apps > Downloaded."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"App isn\'t responding"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> may be using too much memory."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> does not support the current Display size setting and may behave unexpectedly."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Always show"</string>
<string name="smv_application" msgid="3307209192155442829">"The app <xliff:g id="APPLICATION">%1$s</xliff:g> (process <xliff:g id="PROCESS">%2$s</xliff:g>) has violated its self-enforced Strict Mode policy."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Phone"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Headphones"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dock speakers"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Headphones"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wireless display"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index fa7f180..7fff878 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Phone"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Cancel"</string>
+ <string name="close" msgid="2318214661230355730">"CLOSE"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Attention"</string>
<string name="loading" msgid="7933681260296021180">"Loading…"</string>
<string name="capital_on" msgid="1544682755514494298">"ON"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Scale"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Always show"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Re-enable this in System settings > Apps > Downloaded."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"App isn\'t responding"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> may be using too much memory."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> does not support the current Display size setting and may behave unexpectedly."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Always show"</string>
<string name="smv_application" msgid="3307209192155442829">"The app <xliff:g id="APPLICATION">%1$s</xliff:g> (process <xliff:g id="PROCESS">%2$s</xliff:g>) has violated its self-enforced Strict Mode policy."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Phone"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Headphones"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dock speakers"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Headphones"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wireless display"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index fa7f180..7fff878 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Phone"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Cancel"</string>
+ <string name="close" msgid="2318214661230355730">"CLOSE"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Attention"</string>
<string name="loading" msgid="7933681260296021180">"Loading…"</string>
<string name="capital_on" msgid="1544682755514494298">"ON"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Scale"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Always show"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Re-enable this in System settings > Apps > Downloaded."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"App isn\'t responding"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> may be using too much memory."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> does not support the current Display size setting and may behave unexpectedly."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Always show"</string>
<string name="smv_application" msgid="3307209192155442829">"The app <xliff:g id="APPLICATION">%1$s</xliff:g> (process <xliff:g id="PROCESS">%2$s</xliff:g>) has violated its self-enforced Strict Mode policy."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Phone"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Headphones"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dock speakers"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Headphones"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wireless display"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index fa7f180..7fff878 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Phone"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Cancel"</string>
+ <string name="close" msgid="2318214661230355730">"CLOSE"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Attention"</string>
<string name="loading" msgid="7933681260296021180">"Loading…"</string>
<string name="capital_on" msgid="1544682755514494298">"ON"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Scale"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Always show"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Re-enable this in System settings > Apps > Downloaded."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"App isn\'t responding"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> may be using too much memory."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> does not support the current Display size setting and may behave unexpectedly."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Always show"</string>
<string name="smv_application" msgid="3307209192155442829">"The app <xliff:g id="APPLICATION">%1$s</xliff:g> (process <xliff:g id="PROCESS">%2$s</xliff:g>) has violated its self-enforced Strict Mode policy."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Phone"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Headphones"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dock speakers"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Headphones"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wireless display"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 353b437..b93db1c 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Phone"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure you have 250MB of free space and restart."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Cancel"</string>
+ <string name="close" msgid="2318214661230355730">"CLOSE"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Attention"</string>
<string name="loading" msgid="7933681260296021180">"Loading…"</string>
<string name="capital_on" msgid="1544682755514494298">"ON"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Scale"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Always show"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Re-enable this in System settings > Apps > Downloaded."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"App isn\'t responding"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> may be using too much memory."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> does not support the current Display size setting and may behave unexpectedly."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Always show"</string>
<string name="smv_application" msgid="3307209192155442829">"The app <xliff:g id="APPLICATION">%1$s</xliff:g> (process <xliff:g id="PROCESS">%2$s</xliff:g>) has violated its self-enforced StrictMode policy."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Phone"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Headphones"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dock speakers"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Headphones"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wireless display"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 4a7a191..3946e7d 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Teléfono"</string>
<string name="map" msgid="6068210738233985748">"Mapas"</string>
<string name="browse" msgid="6993590095938149861">"Navegador"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio de almacenamiento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no estén disponibles."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"No hay espacio suficiente para el sistema. Asegúrate de que haya 250 MB libres y reinicia el dispositivo."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="yes" msgid="5362982303337969312">"Aceptar"</string>
<string name="no" msgid="5141531044935541497">"Cancelar"</string>
+ <string name="close" msgid="2318214661230355730">"CERRAR"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Atención"</string>
<string name="loading" msgid="7933681260296021180">"Cargando…"</string>
<string name="capital_on" msgid="1544682755514494298">"Sí"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Escala"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Mostrar siempre"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Volver a activar Configuración del sistema > Aplicaciones > Descargas"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"La app no responde"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Es posible que <xliff:g id="APP_NAME">%1$s</xliff:g> esté consumiendo demasiada memoria."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> no es compatible con la configuración del tamaño de pantalla actual. Es posible que no se comporte de manera correcta."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Mostrar siempre"</string>
<string name="smv_application" msgid="3307209192155442829">"La aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> (proceso <xliff:g id="PROCESS">%2$s</xliff:g>) ha infringido su política StrictMode de aplicación automática."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Dispositivo"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Auriculares"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Altavoces del conector"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Auriculares"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Pantalla inalámbrica"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 425e988..7085fda 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -524,12 +524,12 @@
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite que la aplicación modifique cómo se registra el uso de red en relación con las aplicaciones. Las aplicaciones normales no deben usar este permiso."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"acceder a las notificaciones"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que la aplicación recupere, examine y borre notificaciones, incluidas las que han publicado otras aplicaciones."</string>
- <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"enlazar con un servicio de detector de notificaciones"</string>
- <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite enlazar con la interfaz de nivel superior de un servicio de detector de notificaciones. No debe ser necesario para las aplicaciones normales."</string>
- <string name="permlab_bindConditionProviderService" msgid="1180107672332704641">"enlazar con un servicio de proveedor de condiciones"</string>
- <string name="permdesc_bindConditionProviderService" msgid="1680513931165058425">"Permite enlazar con la interfaz de nivel superior de un servicio de proveedor de condiciones. Las aplicaciones normales no deberían necesitar este permiso."</string>
- <string name="permlab_bindDreamService" msgid="4153646965978563462">"enlazar con un servicio de salvapantallas"</string>
- <string name="permdesc_bindDreamService" msgid="7325825272223347863">"Permite enlazar con la interfaz de nivel superior de un servicio de salvapantallas. Las aplicaciones normales no deberían necesitar este permiso."</string>
+ <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vincular con un servicio de detector de notificaciones"</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite vincular con la interfaz de nivel superior de un servicio de detector de notificaciones. No debe ser necesario para las aplicaciones normales."</string>
+ <string name="permlab_bindConditionProviderService" msgid="1180107672332704641">"vincular con un servicio de proveedor de condiciones"</string>
+ <string name="permdesc_bindConditionProviderService" msgid="1680513931165058425">"Permite vincular con la interfaz de nivel superior de un servicio de proveedor de condiciones. Las aplicaciones normales no deberían necesitar este permiso."</string>
+ <string name="permlab_bindDreamService" msgid="4153646965978563462">"vincular con un servicio de salvapantallas"</string>
+ <string name="permdesc_bindDreamService" msgid="7325825272223347863">"Permite vincular con la interfaz de nivel superior de un servicio de salvapantallas. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"ejecutar la aplicación de configuración proporcionada por el operador"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Permite ejecutar la aplicación de configuración proporcionada por el operador. No debe ser necesario para aplicaciones normales."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"detectar cambios en el estado de la red"</string>
@@ -542,10 +542,10 @@
<string name="permdesc_handoverStatus" msgid="4788144087245714948">"Permite que esta aplicación reciba información sobre las transferencias actuales de Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"quitar certificados DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"Permite a una aplicación eliminar los certificados DRM. Las aplicaciones normales no deberían necesitar este permiso."</string>
- <string name="permlab_bindCarrierMessagingService" msgid="1490229371796969158">"enlazar con el servicio de mensajería de un operador"</string>
- <string name="permdesc_bindCarrierMessagingService" msgid="2762882888502113944">"Permite enlazar con la interfaz de nivel superior del servicio de mensajería de un operador. Las aplicaciones normales no deberían necesitar este permiso."</string>
- <string name="permlab_bindCarrierServices" msgid="3233108656245526783">"enlazar con servicios de operador"</string>
- <string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"Permite enlazar con servicios de operador. Las aplicaciones normales no deberían necesitar este permiso."</string>
+ <string name="permlab_bindCarrierMessagingService" msgid="1490229371796969158">"vincular con el servicio de mensajería de un operador"</string>
+ <string name="permdesc_bindCarrierMessagingService" msgid="2762882888502113944">"Permite vincular con la interfaz de nivel superior del servicio de mensajería de un operador. Las aplicaciones normales no deberían necesitar este permiso."</string>
+ <string name="permlab_bindCarrierServices" msgid="3233108656245526783">"vincular con servicios de operador"</string>
+ <string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"Permite vincular con servicios de operador. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_access_notification_policy" msgid="4247510821662059671">"acceso a No molestar"</string>
<string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Permite que la aplicación lea y modifique la configuración de No molestar."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Establecimiento de reglas de contraseña"</string>
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Teléfono"</string>
<string name="map" msgid="6068210738233985748">"Mapas"</string>
<string name="browse" msgid="6993590095938149861">"Navegador"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no funcionen."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"No hay espacio suficiente para el sistema. Comprueba que haya 250 MB libres y reinicia el dispositivo."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="yes" msgid="5362982303337969312">"Aceptar"</string>
<string name="no" msgid="5141531044935541497">"Cancelar"</string>
+ <string name="close" msgid="2318214661230355730">"CERRAR"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Atención"</string>
<string name="loading" msgid="7933681260296021180">"Cargando..."</string>
<string name="capital_on" msgid="1544682755514494298">"ACTIVADO"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Escala"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Mostrar siempre"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Para volver a habilitar esta opción, accede a Ajustes > Aplicaciones > Descargadas."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"La aplicación no responde"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Es posible que <xliff:g id="APP_NAME">%1$s</xliff:g> esté usando demasiada memoria."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> no admite el tamaño de pantalla actual y es posible que funcione de forma inesperada."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Mostrar siempre"</string>
<string name="smv_application" msgid="3307209192155442829">"La aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> (proceso <xliff:g id="PROCESS">%2$s</xliff:g>) ha infringido su política StrictMode autoaplicable."</string>
@@ -1221,7 +1228,7 @@
<string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"El dispositivo no admite este medio externo (<xliff:g id="NAME">%s</xliff:g>). Toca para configurarlo con un formato admitido."</string>
<string name="ext_media_unsupported_notification_message" product="tv" msgid="3725436899820390906">"El dispositivo no admite esta <xliff:g id="NAME">%s</xliff:g>. Selecciónala para configurarla en un formato admitido."</string>
<string name="ext_media_badremoval_notification_title" msgid="3206248947375505416">"Extracción inesperada de <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="ext_media_badremoval_notification_message" msgid="380176703346946313">"Desactiva tu <xliff:g id="NAME">%s</xliff:g> antes de extraer la unidad para evitar pérdidas de datos"</string>
+ <string name="ext_media_badremoval_notification_message" msgid="380176703346946313">"Desconecta tu <xliff:g id="NAME">%s</xliff:g> antes de extraer la unidad para evitar pérdidas de datos"</string>
<string name="ext_media_nomedia_notification_title" msgid="1704840188641749091">"Tu <xliff:g id="NAME">%s</xliff:g> se ha extraído"</string>
<string name="ext_media_nomedia_notification_message" msgid="6471542972147056586">"Tu <xliff:g id="NAME">%s</xliff:g> se ha extraído: inserta otra unidad"</string>
<string name="ext_media_unmounting_notification_title" msgid="640674168454809372">"Expulsando <xliff:g id="NAME">%s</xliff:g>…"</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Teléfono"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Auriculares"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Altavoces de la base"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Auriculares"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Pantalla inalámbrica"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 7c57daf..2565e62 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"Brauser"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Talletusruum saab täis"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Mõned süsteemifunktsioonid ei pruugi töötada"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Süsteemis pole piisavalt talletusruumi. Veenduge, et seadmes oleks 250 MB vaba ruumi, ja käivitage seade uuesti."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Tühista"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Tühista"</string>
+ <string name="close" msgid="2318214661230355730">"SULGE"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Tähelepanu"</string>
<string name="loading" msgid="7933681260296021180">"Laadimine ..."</string>
<string name="capital_on" msgid="1544682755514494298">"SEES"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Mõõtkava"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Kuva alati"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Lubage see uuesti valikutes Süsteemiseaded > Rakendused > Allalaaditud."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Rakendus ei reageeri"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Võimalik, et rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> kasutab liiga palju mälu."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> ei toeta praegust ekraani suuruse seadet ja võib ootamatult käituda."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Kuva alati"</string>
<string name="smv_application" msgid="3307209192155442829">"Rakendus <xliff:g id="APPLICATION">%1$s</xliff:g> (protsess <xliff:g id="PROCESS">%2$s</xliff:g>) on rikkunud isekehtestatud StrictMode\'i eeskirju."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tahvelarvuti"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Teler"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Kõrvaklapid"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Doki kõlarid"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Kõrvaklapid"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Süsteem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-heli"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Juhtmeta ekraan"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 5556a5d..f88d4c4 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefonoa"</string>
<string name="map" msgid="6068210738233985748">"Mapak"</string>
<string name="browse" msgid="6993590095938149861">"Arakatzailea"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Memoria betetzen ari da"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sistemaren funtzio batzuek ez dute agian funtzionatuko"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sisteman ez dago behar adina memoria. Ziurtatu gutxienez 250 MB erabilgarri dituzula eta, ondoren, berrabiarazi gailua."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Utzi"</string>
<string name="yes" msgid="5362982303337969312">"Ados"</string>
<string name="no" msgid="5141531044935541497">"Utzi"</string>
+ <string name="close" msgid="2318214661230355730">"ITXI"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Abisua"</string>
<string name="loading" msgid="7933681260296021180">"Kargatzen…"</string>
<string name="capital_on" msgid="1544682755514494298">"AKTIBATUTA"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Eskala"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Erakutsi beti"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Gaitu hori berriro Sistemaren ezarpenak > Aplikazioak > Deskargatutakoak."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Aplikazioak ez du erantzuten"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> memoria gehiegi erabiltzen ari liteke."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak ez du onartzen uneko pantailaren tamaina eta espero ez bezala joka lezake."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Erakutsi beti"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> aplikazioak (<xliff:g id="PROCESS">%2$s</xliff:g> prozesua) berak aplikatutako StrictMode gidalerroa urratu du."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tableta"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Telebista"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefonoa"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Aurikularrak"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Konektatu bozgorailuak oinarrira"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Aurikularrak"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetootharen audioa"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Hari gabeko pantaila"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index bb9635a..2bd7696 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"تلفن"</string>
<string name="map" msgid="6068210738233985748">"نقشهها"</string>
<string name="browse" msgid="6993590095938149861">"مرورگر"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"حافظه درحال پر شدن است"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"فضای ذخیرهسازی سیستم کافی نیست. اطمینان حاصل کنید که دارای ۲۵۰ مگابایت فضای خالی هستید و سیستم را راهاندازی مجدد کنید."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"لغو"</string>
<string name="yes" msgid="5362982303337969312">"تأیید"</string>
<string name="no" msgid="5141531044935541497">"لغو"</string>
+ <string name="close" msgid="2318214661230355730">"بستن"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"توجه"</string>
<string name="loading" msgid="7933681260296021180">"در حال بارکردن…"</string>
<string name="capital_on" msgid="1544682755514494298">"روشن"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"مقیاس"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"همیشه نشان داده شود"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"در تنظیمات سیستم >برنامهها > مورد بارگیری شده آن را دوباره فعال کنید."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"برنامه پاسخ نمیدهد"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> ممکن است حافظه خیلی زیادی مصرف کند."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> از تنظیم فعلی اندازه نمایشگر پشتیبانی نمیکند و ممکن است رفتار غیرمنتظرهای داشته باشد."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"همیشه نشان داده شود"</string>
<string name="smv_application" msgid="3307209192155442829">"برنامه <xliff:g id="APPLICATION">%1$s</xliff:g> (پردازش <xliff:g id="PROCESS">%2$s</xliff:g>) خطمشی StrictMode اجرایی خود را نقض کرده است."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"رایانهٔ لوحی"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"تلویزیون"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"تلفن"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"هدفونها"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"بلندگوهای جایگاه"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"هدفونها"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"سیستم"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"بلوتوثهای صوتی"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"صفحه نمایش بیسیم"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index a32db2c..c2a2ccb63 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Puhelin"</string>
<string name="map" msgid="6068210738233985748">"Kartat"</string>
<string name="browse" msgid="6993590095938149861">"Selain"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Tallennustila loppumassa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kaikki järjestelmätoiminnot eivät välttämättä toimi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Tallennustila ei riitä. Varmista, että vapaata tilaa on 250 Mt, ja käynnistä uudelleen."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Peruuta"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Peruuta"</string>
+ <string name="close" msgid="2318214661230355730">"SULJE"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Huomio"</string>
<string name="loading" msgid="7933681260296021180">"Ladataan…"</string>
<string name="capital_on" msgid="1544682755514494298">"PÄÄLLÄ"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Asteikko"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Näytä aina"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Ota tämä uudelleen käyttöön kohdassa Järjestelmäasetukset > Sovellukset > Ladattu."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Sovellus ei vastaa"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> saattaa käyttää liikaa muistia."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei tue nykyistä näytön kokoasetusta ja saattaa toimia odottamattomalla tavalla."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Näytä aina"</string>
<string name="smv_application" msgid="3307209192155442829">"Sovellus <xliff:g id="APPLICATION">%1$s</xliff:g> (prosessi <xliff:g id="PROCESS">%2$s</xliff:g>) on rikkonut itse käyttöön ottamaansa StrictMode-käytäntöä."</string>
@@ -1130,7 +1137,7 @@
<string name="wifi_connect_alert_title" msgid="8455846016001810172">"Sallitaanko yhteys?"</string>
<string name="wifi_connect_alert_message" msgid="6451273376815958922">"Sovellus %1$s yrittää yhdistää Wi-Fi-verkkoon %2$s."</string>
<string name="wifi_connect_default_application" msgid="7143109390475484319">"Sovellus"</string>
- <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Suora Wi-Fi-yhteys"</string>
+ <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
<string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Käynnistä suora Wi-Fi-yhteys. Wi-Fi-asiakas/-hotspot poistetaan käytöstä."</string>
<string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Suoran Wi-Fi-yhteyden käynnistäminen epäonnistui."</string>
<string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct on käytössä"</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tabletti"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televisio"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Puhelin"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Kuulokkeet"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Telineen kaiuttimet"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Kuulokkeet"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Järjestelmä"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-ääni"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Langaton näyttö"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 8fe60e3..fbed666 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -253,7 +253,7 @@
<string name="foreground_service_apps_in_background" msgid="7175032677643332242">"<xliff:g id="NUMBER">%1$d</xliff:g> applications sollicitent la pile"</string>
<string name="foreground_service_tap_for_details" msgid="372046743534354644">"Touchez pour afficher des détails sur l\'utilisation de la pile et des données"</string>
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
- <string name="safeMode" msgid="2788228061547930246">"Mode sécurisé"</string>
+ <string name="safeMode" msgid="2788228061547930246">"Mode sans échec"</string>
<string name="android_system_label" msgid="6577375335728551336">"Système Android"</string>
<string name="user_owner_label" msgid="1119010402169916617">"Passer au profil personnel"</string>
<string name="managed_profile_label" msgid="5289992269827577857">"Passer au profil professionnel"</string>
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Téléphone"</string>
<string name="map" msgid="6068210738233985748">"Cartes"</string>
<string name="browse" msgid="6993590095938149861">"Navigateur"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Espace de stockage bientôt saturé"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Annuler"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Annuler"</string>
+ <string name="close" msgid="2318214661230355730">"FERMER"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Attention"</string>
<string name="loading" msgid="7933681260296021180">"Chargement en cours..."</string>
<string name="capital_on" msgid="1544682755514494298">"OUI"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Redimensionner"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Toujours afficher"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Réactivez ce mode en accédant à Paramètres système > Applications > Téléchargements"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"L\'application ne répond pas"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> utilise peut-être trop de mémoire."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas compatible avec le paramètre de taille d\'affichage actuel et peut se comporter de manière inattendue."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Toujours afficher"</string>
<string name="smv_application" msgid="3307209192155442829">"L\'application <xliff:g id="APPLICATION">%1$s</xliff:g> (du processus <xliff:g id="PROCESS">%2$s</xliff:g>) a enfreint ses propres règles du mode strict."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablette"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Télévision"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Téléphone"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Oreillettes"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Haut-parleurs de la station d\'accueil"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Oreillettes"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Système"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Affichage sans fil"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 52d4824..fcdccad 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Téléphone"</string>
<string name="map" msgid="6068210738233985748">"Cartes"</string>
<string name="browse" msgid="6993590095938149861">"Navigateur"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Espace de stockage bientôt saturé"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Annuler"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Annuler"</string>
+ <string name="close" msgid="2318214661230355730">"FERMER"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Attention"</string>
<string name="loading" msgid="7933681260296021180">"Chargement…"</string>
<string name="capital_on" msgid="1544682755514494298">"OUI"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Mise à l\'échelle"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Toujours afficher"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Réactivez ce mode en accédant à Paramètres système > Applications > Téléchargements"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"L\'application ne répond pas"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> utilise peut-être trop de mémoire."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas compatible avec le paramètre de taille d\'affichage actuel et peut présenter un comportement inattendu."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Toujours afficher"</string>
<string name="smv_application" msgid="3307209192155442829">"L\'application <xliff:g id="APPLICATION">%1$s</xliff:g> (du processus <xliff:g id="PROCESS">%2$s</xliff:g>) a enfreint ses propres règles du mode strict."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablette"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Téléviseur"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Téléphone"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Écouteurs"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Haut-parleurs de la station d\'accueil"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Écouteurs"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Système"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Affichage sans fil"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 9a3a83f..0c0e12a 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Teléfono"</string>
<string name="map" msgid="6068210738233985748">"Mapas"</string>
<string name="browse" msgid="6993590095938149861">"Navegador"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Estase esgotando o espazo de almacenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"É posible que algunhas funcións do sistema non funcionen"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Non hai almacenamento suficiente para o sistema. Asegúrate de ter un espazo libre de 250 MB e reinicia o dispositivo."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="yes" msgid="5362982303337969312">"Aceptar"</string>
<string name="no" msgid="5141531044935541497">"Cancelar"</string>
+ <string name="close" msgid="2318214661230355730">"PECHAR"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Atención"</string>
<string name="loading" msgid="7933681260296021180">"Cargando..."</string>
<string name="capital_on" msgid="1544682755514494298">"SI"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Escala"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Mostrar sempre"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Volve activar esta función en Configuración do sistema > Aplicacións > Descargadas."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"A aplicación non responde"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"É posible que <xliff:g id="APP_NAME">%1$s</xliff:g> estea utilizando demasiada memoria."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> non admite a configuración do tamaño de pantalla actual e quizais presente un comportamento inesperado."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Mostrar sempre"</string>
<string name="smv_application" msgid="3307209192155442829">"A aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> (proceso <xliff:g id="PROCESS">%2$s</xliff:g>) infrinxiu a súa política StrictMode autoaplicada."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tableta"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televisión"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Teléfono"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Auriculares"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Conectar altofalantes á base"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Auriculares"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio por Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Visualización sen fíos"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index ec80236..d985e28 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -270,8 +270,8 @@
<string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS સંદેશા મોકલવાની અને જોવાની"</string>
<string name="permgrouprequest_sms" msgid="605618939583628306">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને SMS સંદેશા મોકલવા અને જોવાની મંજૂરી આપો"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"સ્ટોરેજ"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"તમારા ઉપકરણ પર ફોટા, મીડિયા અને ફાઇલો ઍક્સેસ કરવાની"</string>
- <string name="permgrouprequest_storage" msgid="7429669910547860218">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને તમારા ઉપકરણ પર ફોટા, મીડિયા, અને ફાઇલોને ઍક્સેસ કરવાની મંજૂરી આપો"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"તમારા ઉપકરણ પર ફોટો, મીડિયા અને ફાઇલો ઍક્સેસ કરવાની"</string>
+ <string name="permgrouprequest_storage" msgid="7429669910547860218">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને તમારા ઉપકરણ પર ફોટો, મીડિયા, અને ફાઇલોને ઍક્સેસ કરવાની મંજૂરી આપો"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"માઇક્રોફોન"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"ઑડિઓ રેકોર્ડ કરવાની"</string>
<string name="permgrouprequest_microphone" msgid="8065941268709600606">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને ઑડિઓ રોકૉર્ડ કરવાની મંજૂરી આપો"</string>
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"ફોન"</string>
<string name="map" msgid="6068210738233985748">"નકશા"</string>
<string name="browse" msgid="6993590095938149861">"બ્રાઉઝર"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"સ્ટોરેજ સ્થાન સમાપ્ત થયું"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"કેટલાક સિસ્ટમ કાર્યો કામ કરી શકશે નહીં"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"સિસ્ટમ માટે પર્યાપ્ત સ્ટોરેજ નથી. ખાતરી કરો કે તમારી પાસે 250MB ખાલી સ્થાન છે અને ફરીથી પ્રારંભ કરો."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"રદ કરો"</string>
<string name="yes" msgid="5362982303337969312">"ઓકે"</string>
<string name="no" msgid="5141531044935541497">"રદ કરો"</string>
+ <string name="close" msgid="2318214661230355730">"બંધ કરો"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"ધ્યાન આપો"</string>
<string name="loading" msgid="7933681260296021180">"લોડ કરી રહ્યું છે…"</string>
<string name="capital_on" msgid="1544682755514494298">"ચાલુ"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"સ્કેલ"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"હંમેશા બતાવો"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"આને સિસ્ટમ સેટિંગ્સ > ઍપ્લિકેશનો > ડાઉનલોડ કરેલમાં ફરીથી સક્ષમ કરો."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"ઍપ પ્રતિસાદ આપી રહી નથી"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> ખૂબ વધારે મેમરીનો ઉપયોગ કરતી હોઈ શકે."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> વર્તમાન પ્રદર્શન કદની સેટિંગનું સમર્થન કરતું નથી અને અનપેક્ષિત રીતે વર્તી શકે છે."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"હંમેશાં બતાવો"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> ઍપ્લિકેશન (<xliff:g id="PROCESS">%2$s</xliff:g> પ્રક્રિયા)એ તેની સ્વ-લાગુ કરેલ StrictMode નીતિનું ઉલ્લંઘન કર્યું છે."</string>
@@ -1183,7 +1190,7 @@
<string name="usb_charging_notification_title" msgid="6895185153353640787">"આ ઉપકરણને USB થી ચાર્જ કરે છે"</string>
<string name="usb_supplying_notification_title" msgid="5310642257296510271">"જોડાયેલ ઉપકરણ માટે USB પાવર પૂરો પાડે છે"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"ફાઇલ ટ્રાન્સફર માટે USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"ફોટા ટ્રાન્સફર માટે USB"</string>
+ <string name="usb_ptp_notification_title" msgid="1347328437083192112">"ફોટો ટ્રાન્સફર માટે USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI માટે USB"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB ઍક્સેસરીથી કનેક્ટ થયાં"</string>
<string name="usb_notification_message" msgid="3370903770828407960">"વધુ વિકલ્પો માટે ટૅપ કરો."</string>
@@ -1213,7 +1220,7 @@
<string name="ext_media_checking_notification_title" msgid="5734005953288045806">"<xliff:g id="NAME">%s</xliff:g> ને તૈયાર કરી રહ્યું છે"</string>
<string name="ext_media_checking_notification_message" msgid="4747432538578886744">"ભૂલો માટે તપાસી રહ્યું છે"</string>
<string name="ext_media_new_notification_message" msgid="7589986898808506239">"નવું <xliff:g id="NAME">%s</xliff:g> મળ્યું"</string>
- <string name="ext_media_ready_notification_message" msgid="4083398150380114462">"ફોટા અને મીડિયા ટ્રાન્સફર કરવા માટે"</string>
+ <string name="ext_media_ready_notification_message" msgid="4083398150380114462">"ફોટો અને મીડિયા ટ્રાન્સફર કરવા માટે"</string>
<string name="ext_media_unmountable_notification_title" msgid="8295123366236989588">"દૂષિત <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_unmountable_notification_message" msgid="2343202057122495773">"<xliff:g id="NAME">%s</xliff:g> દૂષિત છે. ઠીક કરવા માટે ટૅપ કરો."</string>
<string name="ext_media_unmountable_notification_message" product="tv" msgid="3941179940297874950">"<xliff:g id="NAME">%s</xliff:g> દૂષિત છે. સુધારવા માટે પસંદ કરો."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ટેબ્લેટ"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"ફોન"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"હેડફોન"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"સ્પીકર્સ ડૉક કરો"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"હેડફોન"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"સિસ્ટમ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"બ્લૂટૂથ ઑડિઓ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"વાયરલેસ ડિસ્પ્લે"</string>
@@ -1738,7 +1746,7 @@
<string name="app_category_game" msgid="5431836943981492993">"રમતો"</string>
<string name="app_category_audio" msgid="1659853108734301647">"સંગીત અને ઑડિઓ"</string>
<string name="app_category_video" msgid="2728726078629384196">"મૂવી અને વીડિઓ"</string>
- <string name="app_category_image" msgid="4867854544519846048">"ફોટા અને છબીઓ"</string>
+ <string name="app_category_image" msgid="4867854544519846048">"ફોટો અને છબીઓ"</string>
<string name="app_category_social" msgid="5842783057834965912">"સામાજિક અને સંચાર"</string>
<string name="app_category_news" msgid="7496506240743986873">"સમાચાર અને સામાયિકો"</string>
<string name="app_category_maps" msgid="5878491404538024367">"નકશા અને નેવિગેશન"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 217d106..e29deab 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"फ़ोन"</string>
<string name="map" msgid="6068210738233985748">"मानचित्र"</string>
<string name="browse" msgid="6993590095938149861">"ब्राउज़र"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"मेमोरी में जगह नहीं बची है"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"हो सकता है कुछ सिस्टम फ़ंक्शन कार्य न करें"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"सिस्टम के लिए ज़रूरी मेमोरी नहीं है. पक्का करें कि आपके पास 250एमबी की खाली जगह है और फिर से शुरू करें."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"रद्द करें"</string>
<string name="yes" msgid="5362982303337969312">"ठीक है"</string>
<string name="no" msgid="5141531044935541497">"रद्द करें"</string>
+ <string name="close" msgid="2318214661230355730">"बंद करें"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"ध्यान दें"</string>
<string name="loading" msgid="7933681260296021180">"लोड हो रहे हैं..."</string>
<string name="capital_on" msgid="1544682755514494298">"ऑन"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"स्केल"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"हमेशा दिखाएं"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"इसे सिस्टम सेटिंग > ऐप > डाउनलोड किए गए में फिर से चालू करें."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"ऐप काम नहीं कर रहा है"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> शायद बहुत ज़्यादा मेमोरी इस्तेमाल कर रहा है."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> वर्तमान स्क्रीन के आकार की सेटिंग का समर्थन नहीं करता है और अनपेक्षित रूप से व्यवहार कर सकता है."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"हमेशा दिखाएं"</string>
<string name="smv_application" msgid="3307209192155442829">"ऐप्स <xliff:g id="APPLICATION">%1$s</xliff:g> (प्रक्रिया <xliff:g id="PROCESS">%2$s</xliff:g>) ने उसकी स्वयं लागू होने वाली StrictMode नीति का उल्लंघन किया है."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"टैबलेट"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"टीवी"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"फ़ोन"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"हेडफ़ोन"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"डॉक स्पीकर"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"हेडफ़ोन"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"सिस्टम"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ब्लूटूथ ऑडियो"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"वायरलेस डिसप्ले"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 60a0cc2..bcf789b 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1000,6 +1000,10 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Karte"</string>
<string name="browse" msgid="6993590095938149861">"Preglednik"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke sistemske funkcije možda neće raditi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno pohrane za sustav. Oslobodite 250 MB prostora i pokrenite uređaj ponovo."</string>
@@ -1009,6 +1013,7 @@
<string name="cancel" msgid="6442560571259935130">"Odustani"</string>
<string name="yes" msgid="5362982303337969312">"U redu"</string>
<string name="no" msgid="5141531044935541497">"Odustani"</string>
+ <string name="close" msgid="2318214661230355730">"ZATVORI"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Pažnja"</string>
<string name="loading" msgid="7933681260296021180">"Učitavanje…"</string>
<string name="capital_on" msgid="1544682755514494298">"Uklj."</string>
@@ -1065,6 +1070,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Mjerilo"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Uvijek prikaži"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Omogućiti to ponovo u Postavkama sustava > Aplikacije > Preuzimanja."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Aplikacija ne reagira"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> možda upotrebljava previše memorije."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> ne podržava trenutačnu postavku veličine zaslona i može se ponašati neočekivano."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Uvijek prikaži"</string>
<string name="smv_application" msgid="3307209192155442829">"Aplikacija <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) prekršila je vlastito pravilo StrictMode."</string>
@@ -1443,9 +1450,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tabletno računalo"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televizor"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Slušalice"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Zvučnici postolja"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Slušalice"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sustav"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth zvuk"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Bežični prikaz"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 3c40d56..93eb9ac 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Térkép"</string>
<string name="browse" msgid="6993590095938149861">"Böngésző"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kevés a szabad terület"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Előfordulhat, hogy néhány rendszerfunkció nem működik."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nincs elegendő tárhely a rendszerhez. Győződjön meg arról, hogy rendelkezik 250 MB szabad területtel, majd kezdje elölről."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Mégse"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Mégse"</string>
+ <string name="close" msgid="2318214661230355730">"BEZÁRÁS"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Figyelem"</string>
<string name="loading" msgid="7933681260296021180">"Betöltés..."</string>
<string name="capital_on" msgid="1544682755514494298">"Be"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Skála"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Mindig megjelenik"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Újbóli engedélyezés itt: Rendszerbeállítások > Alkalmazások > Letöltve."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Az alkalmazás nem válaszol"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Lehetséges, hogy a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> túl sok memóriát használ."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazás nem támogatja a képernyőméret jelenlegi beállításait, ezért nem várt módon viselkedhet."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Mindig megjelenik"</string>
<string name="smv_application" msgid="3307209192155442829">"A(z) <xliff:g id="APPLICATION">%1$s</xliff:g> alkalmazás (<xliff:g id="PROCESS">%2$s</xliff:g> folyamat) megsértette az általa kényszerített Szigorú üzemmód irányelvet."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Táblagép"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Fejhallgató"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dokkolóegység hangszórója"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Fejhallgató"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Rendszer"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth hang"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Vezeték nélküli kijelző"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 190b2d5..e5d92c6 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Հեռախոս"</string>
<string name="map" msgid="6068210738233985748">"Քարտեզներ"</string>
<string name="browse" msgid="6993590095938149861">"Դիտարկիչ"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Հիշողությունը սպառվում է"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Որոշ գործառույթներ կարող են չաշխատել"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Համակարգի համար բավարար հիշողություն չկա: Համոզվեք, որ ունեք 250ՄԲ ազատ տարածություն և վերագործարկեք:"</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Չեղարկել"</string>
<string name="yes" msgid="5362982303337969312">"Լավ"</string>
<string name="no" msgid="5141531044935541497">"Չեղարկել"</string>
+ <string name="close" msgid="2318214661230355730">"ՓԱԿԵԼ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Ուշադրություն"</string>
<string name="loading" msgid="7933681260296021180">"Բեռնում..."</string>
<string name="capital_on" msgid="1544682755514494298">"I"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Աստիճանակարգել"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Միշտ ցույց տալ"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Կրկին ակտիվացնել սա Համակարգի կարգավորումներում &gt Ծրագրեր > Ներբեռնումներ:"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Հավելվածը չի արձագանքում"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Հնարավոր է, որ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը չափազանց շատ հիշողություն է օգտագործում:"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը չի աջակցում Էկրանի չափի ընթացիկ կարգավորումները, ինչի պատճառով կարող են խնդիրներ առաջանալ:"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Միշտ ցուցադրել"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> ծրագիրը (գործընթաց <xliff:g id="PROCESS">%2$s</xliff:g>) խախտել է իր ինքնահարկադրված Խիստ ռեժիմ քաղաքականությունը:"</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Գրասալիկ"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Հեռուստացույց"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Հեռախոս"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Ականջակալներ"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Համակցված բարձրախոսներ"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Ականջակալներ"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Համակարգ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-ի աուդիո ֆայլ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Անլար էկրան"</string>
@@ -1760,10 +1768,10 @@
<item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> ինքնալցման առաջարկ</item>
<item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> ինքնալցման առաջարկ</item>
</plurals>
- <string name="autofill_save_title" msgid="3345527308992082601">"Պահե՞լ <b><xliff:g id="LABEL">%1$s</xliff:g></b>-ում:"</string>
- <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Պահե՞լ <xliff:g id="TYPE">%1$s</xliff:g>-ը <b><xliff:g id="LABEL">%2$s</xliff:g></b>-ում:"</string>
- <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Պահե՞լ <xliff:g id="TYPE_0">%1$s</xliff:g>-ը և <xliff:g id="TYPE_1">%2$s</xliff:g>-ը <b><xliff:g id="LABEL">%3$s</xliff:g></b>-ում:"</string>
- <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Պահե՞լ <xliff:g id="TYPE_0">%1$s</xliff:g>-ը, <xliff:g id="TYPE_1">%2$s</xliff:g>-ը և <xliff:g id="TYPE_2">%3$s</xliff:g>-ը <b><xliff:g id="LABEL">%4$s</xliff:g></b>-ում:"</string>
+ <string name="autofill_save_title" msgid="3345527308992082601">"Պահե՞լ <b><xliff:g id="LABEL">%1$s</xliff:g></b> ծառայությունում"</string>
+ <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Պահե՞լ <xliff:g id="TYPE">%1$s</xliff:g>ը <b><xliff:g id="LABEL">%2$s</xliff:g></b> ծառայությունում"</string>
+ <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Պահե՞լ <xliff:g id="TYPE_0">%1$s</xliff:g>ն ու <xliff:g id="TYPE_1">%2$s</xliff:g>ը <b><xliff:g id="LABEL">%3$s</xliff:g></b> ծառայությունում"</string>
+ <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Պահե՞լ <xliff:g id="TYPE_0">%1$s</xliff:g>ը, <xliff:g id="TYPE_1">%2$s</xliff:g>ն ու <xliff:g id="TYPE_2">%3$s</xliff:g>ը <b><xliff:g id="LABEL">%4$s</xliff:g></b> ծառայությունում"</string>
<string name="autofill_save_yes" msgid="6398026094049005921">"Պահել"</string>
<string name="autofill_save_no" msgid="2625132258725581787">"Ոչ"</string>
<string name="autofill_save_type_password" msgid="5288448918465971568">"գաղտնաբառ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index b6a8aa28..89e96f9 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telepon"</string>
<string name="map" msgid="6068210738233985748">"Peta"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang penyimpanan hampir habis"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak dapat bekerja"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Penyimpanan tidak cukup untuk sistem. Pastikan Anda memiliki 250 MB ruang kosong, lalu mulai ulang."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Batal"</string>
<string name="yes" msgid="5362982303337969312">"Oke"</string>
<string name="no" msgid="5141531044935541497">"Batal"</string>
+ <string name="close" msgid="2318214661230355730">"TUTUP"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Perhatian"</string>
<string name="loading" msgid="7933681260296021180">"Memuat..."</string>
<string name="capital_on" msgid="1544682755514494298">"AKTIF"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Skala"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Selalu tampilkan"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Aktifkan kembali dialog ini di Setelan sistem > Apl > Terdownload."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Aplikasi tidak merespons"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> mungkin menggunakan terlalu banyak memori."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak mendukung setelan Ukuran layar saat ini dan dapat menunjukkan perilaku yang tak diharapkan."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Selalu tampilkan"</string>
<string name="smv_application" msgid="3307209192155442829">"Apl <xliff:g id="APPLICATION">%1$s</xliff:g> (proses <xliff:g id="PROCESS">%2$s</xliff:g>) telah melanggar kebijakan StrictMode yang diberlakukannya sendiri."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Ponsel"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Headphone"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Pengeras suara dok"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Headphone"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Layar nirkabel"</string>
@@ -1437,7 +1445,7 @@
<string name="media_route_status_available" msgid="6983258067194649391">"Tersedia"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"Tidak tersedia"</string>
<string name="media_route_status_in_use" msgid="4533786031090198063">"Sedang digunakan"</string>
- <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Layar Bawaan"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Layar Built-In"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"Layar HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Hamparan #<xliff:g id="ID">%1$d</xliff:g>"</string>
<string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
@@ -1607,7 +1615,7 @@
</plurals>
<string name="restr_pin_try_later" msgid="973144472490532377">"Coba lagi nanti"</string>
<string name="immersive_cling_title" msgid="8394201622932303336">"Melihat layar penuh"</string>
- <string name="immersive_cling_description" msgid="3482371193207536040">"Untuk keluar, gesek ke bawah dari atas."</string>
+ <string name="immersive_cling_description" msgid="3482371193207536040">"Untuk keluar, geser layar ke bawah dari atas."</string>
<string name="immersive_cling_positive" msgid="5016839404568297683">"Mengerti"</string>
<string name="done_label" msgid="2093726099505892398">"Selesai"</string>
<string name="hour_picker_description" msgid="6698199186859736512">"Penggeser putar jam"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 8cc5f97..2850fec 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Sími"</string>
<string name="map" msgid="6068210738233985748">"Kort"</string>
<string name="browse" msgid="6993590095938149861">"Vafri"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Geymslurýmið er senn á þrotum"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sumir kerfiseiginleikar kunna að vera óvirkir"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Ekki nægt geymslurými fyrir kerfið. Gakktu úr skugga um að 250 MB séu laus og endurræstu."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Hætta við"</string>
<string name="yes" msgid="5362982303337969312">"Í lagi"</string>
<string name="no" msgid="5141531044935541497">"Hætta við"</string>
+ <string name="close" msgid="2318214661230355730">"LOKA"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Athugaðu"</string>
<string name="loading" msgid="7933681260296021180">"Hleður…"</string>
<string name="capital_on" msgid="1544682755514494298">"KVEIKT"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Breyta stærð"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Sýna alltaf"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Þú getur kveikt aftur á þessu undir Kerfisstillingar > Forrit > Sótt."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Forritið svarar ekki"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> notar hugsanlega of mikið minni."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> styður ekki núverandi skjástærðarstillingu og gæti því ekki virkað sem skyldi."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Sýna alltaf"</string>
<string name="smv_application" msgid="3307209192155442829">"Forritið <xliff:g id="APPLICATION">%1$s</xliff:g> (ferli <xliff:g id="PROCESS">%2$s</xliff:g>) hefur brotið gegn eigin StrictMode-stefnu."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Spjaldtölva"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Sjónvarp"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Sími"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Heyrnartól"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dokkuhátalarar"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Heyrnartól"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Kerfi"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-hljóð"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Þráðlaus skjábirting"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 611a9a1..0274602 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefono"</string>
<string name="map" msgid="6068210738233985748">"Mappe"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spazio di archiviazione in esaurimento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Alcune funzioni di sistema potrebbero non funzionare"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Memoria insufficiente per il sistema. Assicurati di avere 250 MB di spazio libero e riavvia."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Annulla"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Annulla"</string>
+ <string name="close" msgid="2318214661230355730">"CHIUDI"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Attenzione"</string>
<string name="loading" msgid="7933681260296021180">"Caricamento..."</string>
<string name="capital_on" msgid="1544682755514494298">"ON"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Scala"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Mostra sempre"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Riattivala in Impostazioni di sistema > Applicazioni > Scaricate."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"L\'app non risponde"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Probabilmente <xliff:g id="APP_NAME">%1$s</xliff:g> utilizza troppa memoria."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> non supporta le dimensioni di visualizzazione attualmente impostate e potrebbe comportarsi in modo imprevisto."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Mostra sempre"</string>
<string name="smv_application" msgid="3307209192155442829">"L\'applicazione <xliff:g id="APPLICATION">%1$s</xliff:g> (processo <xliff:g id="PROCESS">%2$s</xliff:g>) ha violato la norma StrictMode autoimposta."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefono"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Cuffie audio"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Altoparlanti dock"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Cuffie audio"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Visualizzazione wireless"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 40aeba4..5fa6c52 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1020,6 +1020,10 @@
<string name="dial" msgid="4204975095406423102">"טלפון"</string>
<string name="map" msgid="6068210738233985748">"מפות"</string>
<string name="browse" msgid="6993590095938149861">"דפדפן"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"שטח האחסון אוזל"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"אין מספיק שטח אחסון עבור המערכת. ודא שיש לך שטח פנוי בגודל 250MB התחל שוב."</string>
@@ -1029,6 +1033,7 @@
<string name="cancel" msgid="6442560571259935130">"ביטול"</string>
<string name="yes" msgid="5362982303337969312">"אישור"</string>
<string name="no" msgid="5141531044935541497">"ביטול"</string>
+ <string name="close" msgid="2318214661230355730">"סגירה"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"זהירות"</string>
<string name="loading" msgid="7933681260296021180">"טוען..."</string>
<string name="capital_on" msgid="1544682755514494298">"מופעל"</string>
@@ -1085,6 +1090,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"שינוי קנה-מידה"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"הצג תמיד"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"אפשר תכונה זו מחדש ב\'הגדרות מערכת\' < Google Apps < \'הורדות\'."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"האפליקציה לא מגיבה"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"ייתכן שהאפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> משתמשת ביותר מדי שטח זיכרון."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> אינו תומך בהגדרת הגודל הנוכחית של התצוגה, והתנהגותו עשויה להיות בלתי צפויה."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"הצג תמיד"</string>
<string name="smv_application" msgid="3307209192155442829">"האפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g> (תהליך <xliff:g id="PROCESS">%2$s</xliff:g>) הפר את מדיניות StrictMode באכיפה עצמית שלו."</string>
@@ -1466,9 +1473,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"טאבלט"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"טלוויזיה"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"טלפון"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"אוזניות"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"רמקולים של מעגן"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"אוזניות"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"מערכת"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"אודיו Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"צג אלחוטי"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index aa86db3..78e59a3 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"電話"</string>
<string name="map" msgid="6068210738233985748">"マップ"</string>
<string name="browse" msgid="6993590095938149861">"ブラウザ"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"空き容量わずか"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"一部のシステム機能が動作しない可能性があります"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"システムに十分な容量がありません。250MBの空き容量を確保して再起動してください。"</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"キャンセル"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"キャンセル"</string>
+ <string name="close" msgid="2318214661230355730">"閉じる"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"注意"</string>
<string name="loading" msgid="7933681260296021180">"読み込んでいます..."</string>
<string name="capital_on" msgid="1544682755514494298">"ON"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"スケール"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"常に表示"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"[システム設定]>[アプリ]>[ダウンロード済み]で再度有効にします。"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"このアプリは応答していません"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」のメモリの使用量は多すぎる可能性があります。"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」は現在の [表示サイズ] 設定に対応していないため、予期しない動作が発生するおそれがあります。"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"常に表示"</string>
<string name="smv_application" msgid="3307209192155442829">"アプリ「<xliff:g id="APPLICATION">%1$s</xliff:g>」(プロセス「<xliff:g id="PROCESS">%2$s</xliff:g>」)でStrictModeポリシー違反がありました。"</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"タブレット"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"テレビ"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"モバイル端末"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ヘッドホン"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"ホルダーのスピーカー"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ヘッドホン"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"システム"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth音声"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ワイヤレスディスプレイ"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index ea99337..d9947b0 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"ტელეფონი"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"ბრაუზერი"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"თავისუფალი ადგილი იწურება"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"სისტემის ზოგიერთმა ფუნქციამ შესაძლოა არ იმუშავოს"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"სისტემისათვის საკმარისი საცავი არ არის. დარწმუნდით, რომ იქონიოთ სულ მცირე 250 მბაიტი თავისუფალი სივრცე და დაიწყეთ ხელახლა."</string>
@@ -989,6 +993,8 @@
<string name="cancel" msgid="6442560571259935130">"გაუქმება"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"გაუქმება"</string>
+ <!-- no translation found for close (2318214661230355730) -->
+ <skip />
<string name="dialog_alert_title" msgid="2049658708609043103">"ყურადღება"</string>
<string name="loading" msgid="7933681260296021180">"ჩატვირთვა…"</string>
<string name="capital_on" msgid="1544682755514494298">"ჩართ."</string>
@@ -1045,6 +1051,10 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"მასშტაბი"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"ყოველთვის ჩვენება"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"ხელახალი გააქტიურება განყოფილებაში: სისტემის პარამეტრები > აპები > ჩამოტვირთულები."</string>
+ <!-- no translation found for top_app_killed_title (6814231368167994497) -->
+ <skip />
+ <!-- no translation found for top_app_killed_message (3487519022191609844) -->
+ <skip />
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის მიერ ეკრანის ამჟამინდელი პარამეტრები მხარდაუჭერელია და შეიძლება არასათანადოდ იმუშაოს."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"ყოველთვის ჩვენება"</string>
<string name="smv_application" msgid="3307209192155442829">"აპმა <xliff:g id="APPLICATION">%1$s</xliff:g> (პროცესი <xliff:g id="PROCESS">%2$s</xliff:g>) დაარღვია საკუთარი StrictMode დებულება."</string>
@@ -1420,9 +1430,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ტაბლეტი"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ტელევიზია"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"ტელეფონი"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ყურსასმენები"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"სპიკერების მიმაგრება"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ყურსასმენები"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"სისტემა"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth აუდიო"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"უსადენო ეკრანი"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index d02dc0e..95e1459 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Телефон"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"Браузер"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Жадта орын азайып барады"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Жүйенің кейбір функциялары жұмыс істемеуі мүмкін"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Жүйе үшін жад жеткіліксіз. 250 МБ бос орын бар екенін тексеріп, қайта іске қосыңыз."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Бас тарту"</string>
<string name="yes" msgid="5362982303337969312">"Жарайды"</string>
<string name="no" msgid="5141531044935541497">"Бас тарту"</string>
+ <string name="close" msgid="2318214661230355730">"ЖАБУ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Назар аударыңыз"</string>
<string name="loading" msgid="7933681260296021180">"Жүктелуде…"</string>
<string name="capital_on" msgid="1544682755514494298">"Қосулы"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Меже"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Үнемі көрсету"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Мұны «Жүйелік параметрлер» > «Қолданбалар» > «Жүктелгендер» тармағында қосыңыз."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Қолданба жауап бермеуде"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> жадтан тым көп орын пайдалануда."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасында \"Дисплей өлшемі\" параметрінің таңдалған мәніне қолдау көрсетілмейді, сондықтан дұрыс жұмыс істемеуі мүмкін."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Үнемі көрсету"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> қолданбасы (<xliff:g id="PROCESS">%2$s</xliff:g> процесі) өзі қолданған StrictMode саясатын бұзды."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Планшет"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ТД"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Телефон"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Құлақаспаптар"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Үндеткіштерді қондыру"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Құлақаспаптар"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Жүйе"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth aудио"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Сымсыз дисплей"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 38ee22f..ae11db4 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"ទូរសព្ទ"</string>
<string name="map" msgid="6068210738233985748">"ផែនទី"</string>
<string name="browse" msgid="6993590095938149861">"កម្មវិធីរុករកតាមអ៊ីនធឺណិត"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"អស់ទំហំផ្ទុក"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"មិនមានទំហំផ្ទុកគ្រប់គ្រាន់សម្រាប់ប្រព័ន្ធ។ សូមប្រាកដថាអ្នកមានទំហំទំនេរ 250MB ហើយចាប់ផ្ដើមឡើងវិញ។"</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"បោះបង់"</string>
<string name="yes" msgid="5362982303337969312">"យល់ព្រម"</string>
<string name="no" msgid="5141531044935541497">"បោះបង់"</string>
+ <string name="close" msgid="2318214661230355730">"បិទ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"ប្រយ័ត្ន"</string>
<string name="loading" msgid="7933681260296021180">"កំពុងផ្ទុក..."</string>
<string name="capital_on" msgid="1544682755514494298">"បើក"</string>
@@ -1047,6 +1052,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"មាត្រដ្ឋាន"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"បង្ហាញជានិច្ច"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"បើកវាឡើងវិញក្នុងការកំណត់ប្រព័ន្ធ > កម្មវិធី > ទាញយក។"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"កម្មវិធីមិនមានការឆ្លើយតបទេ"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> អាចកំពុងប្រើអង្គចងចាំច្រើនពេក។"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> មិនគាំទ្រការកំណត់ទំហំនៃការបង្ហាញបច្ចុប្បន្ន និងអាចមានសកម្មភាពខុសពីការរំពឹងទុក។"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"បង្ហាញជានិច្ច"</string>
<string name="smv_application" msgid="3307209192155442829">"កម្មវិធី <xliff:g id="APPLICATION">%1$s</xliff:g> (ដំណើរការ <xliff:g id="PROCESS">%2$s</xliff:g>) បានបំពានគោលនយោបាយរបៀបតឹងរ៉ឹងអនុវត្តដោយខ្លួនឯង។"</string>
@@ -1422,9 +1429,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"កុំព្យូទ័របន្ទះ"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ទូរទស្សន៍"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"ទូរសព្ទ"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"កាស"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"ភ្ជាប់អូប៉ាល័រ"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"កាស"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ប្រព័ន្ធ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"សំឡេងប៊្លូធូស"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"បង្ហាញបណ្ដាញឥតខ្សែ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 5116c5d0..125ccbb 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"ಫೋನ್"</string>
<string name="map" msgid="6068210738233985748">"ನಕ್ಷೆಗಳು"</string>
<string name="browse" msgid="6993590095938149861">"ಬ್ರೌಸರ್"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ಸಂಗ್ರಹಣೆ ಸ್ಥಳವು ತುಂಬಿದೆ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ಕೆಲವು ಸಿಸ್ಟಂ ಕಾರ್ಯವಿಧಾನಗಳು ಕಾರ್ಯನಿರ್ವಹಿಸದೇ ಇರಬಹುದು"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ಸಿಸ್ಟಂನಲ್ಲಿ ಸಾಕಷ್ಟು ಸಂಗ್ರಹಣೆಯಿಲ್ಲ. ನೀವು 250MB ನಷ್ಟು ಖಾಲಿ ಸ್ಥಳವನ್ನು ಹೊಂದಿರುವಿರಾ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಹಾಗೂ ಮರುಪ್ರಾರಂಭಿಸಿ."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"ರದ್ದುಮಾಡಿ"</string>
<string name="yes" msgid="5362982303337969312">"ಸರಿ"</string>
<string name="no" msgid="5141531044935541497">"ರದ್ದುಮಾಡಿ"</string>
+ <string name="close" msgid="2318214661230355730">"ಮುಚ್ಚಿ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"ಗಮನಿಸಿ"</string>
<string name="loading" msgid="7933681260296021180">"ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
<string name="capital_on" msgid="1544682755514494298">"ಆನ್ ಮಾಡಿ"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"ಮಾಪಕ"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"ಯಾವಾಗಲೂ ತೋರಿಸಿ"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"ಸಿಸ್ಟಂ ಸೆಟ್ಟಿಂಗ್ಗಳು > ಅಪ್ಲಿಕೇಶನ್ಗಳು > ಡೌನ್ಲೋಡ್ ಆಗಿರುವುದರಲ್ಲಿ ಇದನ್ನು ಮರು ಸಕ್ರಿಯಗೊಳಿಸಿ."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"ಅಪ್ಲಿಕೇಶನ್ ಪ್ರತಿಕ್ರಿಯಿಸುತ್ತಿಲ್ಲ"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಹೆಚ್ಚು ಮೆಮೊರಿಯನ್ನು ಬಳಸಿಕೊಳ್ಳುತ್ತಿರಬಹುದು."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಪ್ರಸ್ತುತ ಪ್ರದರ್ಶನ ಗಾತ್ರದ ಸೆಟ್ಟಿಂಗ್ ಅನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ ಮತ್ತು ಅನಿರೀಕ್ಷಿತವಾಗಿ ವರ್ತಿಸಬಹುದು."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"ಯಾವಾಗಲೂ ತೋರಿಸು"</string>
<string name="smv_application" msgid="3307209192155442829">"ಅಪ್ಲಿಕೇಶನ್ <xliff:g id="APPLICATION">%1$s</xliff:g> (ಪ್ರಕ್ರಿಯೆಯು <xliff:g id="PROCESS">%2$s</xliff:g>) ತನ್ನ ಸ್ವಯಂ-ಜಾರಿ ಕಠಿಣ ಮೋಡ್ ನೀತಿಯನ್ನು ಉಲ್ಲಂಘನೆ ಮಾಡಿದೆ."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ಟ್ಯಾಬ್ಲೆಟ್"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ಟಿವಿ"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"ಫೋನ್"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ಹೆಡ್ಫೋನ್ಗಳು"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"ಡಾಕ್ ಸ್ಪೀಕರ್ಗಳು"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ಹೆಡ್ಫೋನ್ಗಳು"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ಸಿಸ್ಟಂ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ಬ್ಲೂಟೂತ್ ಆಡಿಯೊ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ವಯರ್ಲೆಸ್ ಪ್ರದರ್ಶನ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 722b251..c253742 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -192,7 +192,7 @@
<string name="reboot_to_update_prepare" msgid="6305853831955310890">"업데이트 준비 중…"</string>
<string name="reboot_to_update_package" msgid="3871302324500927291">"업데이트 패키지 처리 중…"</string>
<string name="reboot_to_update_reboot" msgid="6428441000951565185">"다시 시작하는 중..."</string>
- <string name="reboot_to_reset_title" msgid="4142355915340627490">"공장 초기화"</string>
+ <string name="reboot_to_reset_title" msgid="4142355915340627490">"초기화"</string>
<string name="reboot_to_reset_message" msgid="2432077491101416345">"다시 시작하는 중..."</string>
<string name="shutdown_progress" msgid="2281079257329981203">"종료 중..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"태블릿이 종료됩니다."</string>
@@ -358,11 +358,11 @@
<string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"앱이 브로드캐스트가 끝난 후에 남은 브로드캐스트를 보낼 수 있도록 허용합니다. 앱을 지나치게 사용하면 태블릿에서 메모리를 너무 많이 사용하도록 하여 속도를 저하시키거나 불안정하게 만들 수 있습니다."</string>
<string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"앱이 브로드캐스트가 끝난 후에도 흥미로운 브로드캐스트를 보낼 수 있도록 허용합니다. 이 기능을 과도하게 사용하면 메모리 사용량이 많아져 TV 속도가 저하되거나 성능이 불안정해질 수 있습니다."</string>
<string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"앱이 브로드캐스트가 끝난 후에 남은 브로드캐스트를 보낼 수 있도록 허용합니다. 앱을 지나치게 사용하면 휴대전화에서 메모리를 너무 많이 사용하도록 하여 속도를 저하시키거나 불안정하게 만들 수 있습니다."</string>
- <string name="permlab_readContacts" msgid="8348481131899886131">"주소록 읽기"</string>
+ <string name="permlab_readContacts" msgid="8348481131899886131">"연락처 읽기"</string>
<string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"특정인과 전화, 이메일 또는 기타 수단으로 연락한 빈도를 포함하여 사용자 태블릿에 저장된 연락처에 대한 데이터를 앱이 읽도록 허용합니다. 이 권한을 사용하면 앱이 연락처 데이터를 저장할 수 있으며, 악성 앱이 사용자 모르게 연락처 데이터를 공유할 수도 있습니다."</string>
<string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"앱이 특정 연락처와 통화를 하거나 이메일을 주고받거나 다른 방법으로 연락한 횟수를 포함하여 TV에 저장된 연락처 관련 데이터를 읽을 수 있도록 허용합니다. 이 경우 앱이 연락처 데이터를 저장할 수 있으며 악성 앱이 사용자 몰래 연락처 데이터에 공유할 수도 있습니다."</string>
<string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"특정인과 전화, 이메일 또는 기타 수단으로 연락한 빈도를 포함하여 사용자 휴대전화에 저장된 연락처에 대한 데이터를 앱이 읽도록 허용합니다. 이 권한을 사용하면 앱이 연락처 데이터를 저장할 수 있으며, 악성 앱이 사용자 모르게 연락처 데이터를 공유할 수도 있습니다."</string>
- <string name="permlab_writeContacts" msgid="5107492086416793544">"주소록 수정"</string>
+ <string name="permlab_writeContacts" msgid="5107492086416793544">"연락처 수정"</string>
<string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"특정인과 전화, 이메일 또는 기타 수단으로 연락한 빈도를 포함하여 사용자 태블릿에 저장된 연락처에 대한 데이터를 앱이 수정할 수 있도록 허용합니다. 이 권한을 사용하면 앱이 연락처 데이터를 삭제할 수 있습니다."</string>
<string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"앱이 특정 연락처와 통화를 하거나 이메일을 주고받거나 다른 수단으로 연락한 횟수를 포함하여 TV에 저장된 연락처 관련 데이터를 수정할 수 있도록 허용합니다. 이 경우 앱이 연락처 데이터를 삭제할 수 있습니다."</string>
<string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"특정인과 전화, 이메일 또는 기타 수단으로 연락한 빈도를 포함하여 사용자 휴대전화에 저장된 연락처에 대한 데이터를 앱이 수정할 수 있도록 허용합니다. 이 권한을 사용하면 앱이 연락처 데이터를 삭제할 수 있습니다."</string>
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"전화"</string>
<string name="map" msgid="6068210738233985748">"지도"</string>
<string name="browse" msgid="6993590095938149861">"브라우저"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"저장 공간이 부족함"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"일부 시스템 기능이 작동하지 않을 수 있습니다."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"시스템의 저장 공간이 부족합니다. 250MB의 여유 공간이 확보한 후 다시 시작하세요."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"취소"</string>
<string name="yes" msgid="5362982303337969312">"확인"</string>
<string name="no" msgid="5141531044935541497">"취소"</string>
+ <string name="close" msgid="2318214661230355730">"닫기"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"주의"</string>
<string name="loading" msgid="7933681260296021180">"로드 중.."</string>
<string name="capital_on" msgid="1544682755514494298">"ON"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"배율"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"항상 표시"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"시스템 설정 > 앱 > 다운로드로 이동하여 이 모드를 다시 사용하도록 설정합니다."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"앱이 응답하지 않음"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 메모리를 과도하게 사용하는 것으로 보입니다."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱은 현재 디스플레이 크기 설정을 지원하지 않으며 예기치 않게 동작할 수 있습니다."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"항상 표시"</string>
<string name="smv_application" msgid="3307209192155442829">"앱 <xliff:g id="APPLICATION">%1$s</xliff:g>(프로세스 <xliff:g id="PROCESS">%2$s</xliff:g>)이(가) 자체 시행 StrictMode 정책을 위반했습니다."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"태블릿"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"휴대전화"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"헤드폰"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"도크 스피커"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"헤드폰"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"시스템"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"블루투스 오디오"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"무선 디스플레이"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 9cf927e..77f3b0c 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Телефон"</string>
<string name="map" msgid="6068210738233985748">"Карталар"</string>
<string name="browse" msgid="6993590095938149861">"Серепчи"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Сактагычта орун калбай баратат"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Системанын кээ бир функциялары иштебеши мүмкүн"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Тутумда сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз."</string>
@@ -989,6 +993,8 @@
<string name="cancel" msgid="6442560571259935130">"Жокко чыгаруу"</string>
<string name="yes" msgid="5362982303337969312">"Жарайт"</string>
<string name="no" msgid="5141531044935541497">"Жокко чыгаруу"</string>
+ <!-- no translation found for close (2318214661230355730) -->
+ <skip />
<string name="dialog_alert_title" msgid="2049658708609043103">"Көңүл буруңуз"</string>
<string name="loading" msgid="7933681260296021180">"Жүктөлүүдө…"</string>
<string name="capital_on" msgid="1544682755514494298">"ЖАНДЫРЫЛГАН"</string>
@@ -1045,6 +1051,10 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Шкала"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Ар дайым көрсөтүлсүн"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Муну тутум жөндөөлөрүнөн кайра иштетүү > Колдонмолор > Жүктөлүп алынган."</string>
+ <!-- no translation found for top_app_killed_title (6814231368167994497) -->
+ <skip />
+ <!-- no translation found for top_app_killed_message (3487519022191609844) -->
+ <skip />
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу көрүнүштүн тандалган өлчөмүн экранда көрсөтө албайт жана туура эмес иштеши мүмкүн."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Ар дайым көрсөтүлсүн"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосу (<xliff:g id="PROCESS">%2$s</xliff:g> процесси) өз алдынча иштеткен StrictMode саясатын бузду."</string>
@@ -1421,9 +1431,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Планшет"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Сыналгы"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Телефон"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Кулакчын"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Аудио док бекет"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Кулакчын"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Тутум"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth аудио"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Зымсыз дисплей"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 27601af..d53c702 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"ໂທລະສັບ"</string>
<string name="map" msgid="6068210738233985748">"ແຜນທີ່"</string>
<string name="browse" msgid="6993590095938149861">"ໂປຣແກຣມທ່ອງເວັບ"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນກຳລັງຈະເຕັມ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ການເຮັດວຽກບາງຢ່າງຂອງລະບົບບາງອາດຈະໃຊ້ບໍ່ໄດ້"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ບໍ່ມີບ່ອນເກັບຂໍ້ມູນພຽງພໍສຳລັບລະບົບ. ກວດສອບໃຫ້ແນ່ໃຈວ່າທ່ານມີພື້ນທີ່ຫວ່າງຢ່າງໜ້ອຍ 250MB ແລ້ວລອງໃໝ່."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"ຍົກເລີກ"</string>
<string name="yes" msgid="5362982303337969312">"ຕົກລົງ"</string>
<string name="no" msgid="5141531044935541497">"ຍົກເລີກ"</string>
+ <string name="close" msgid="2318214661230355730">"ປິດ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"ກະລຸນາຮັບຊາບ"</string>
<string name="loading" msgid="7933681260296021180">"ກຳລັງໂຫລດ..."</string>
<string name="capital_on" msgid="1544682755514494298">"ເປີດ"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"ຂະໜາດ"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"ສະແດງຕະຫຼອດເວລາ"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"ເປີດການເຮັດວຽກນີ້ຄືນໄດ້ໃນ ການຕັ້ງຄ່າລະບົບ > ແອັບຯ > ດາວໂຫລດແລ້ວ"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"ແອັບບໍ່ຕອບສະໜອງ"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> ອາດກຳລັງໃຊ້ໜ່ວຍຄວາມຈຳຫຼາຍເກີນໄປ"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ່ຮອງຮັບການຕັ້ງຄ່າຂະໜາດສະແດງຜົນປັດຈຸບັນ ແລະ ອາດມີຄວາມຜິດພາດໄດ້."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"ສະແດງທຸກເທື່ອ"</string>
<string name="smv_application" msgid="3307209192155442829">"ແອັບຯ <xliff:g id="APPLICATION">%1$s</xliff:g> (ໂປຣເຊສ <xliff:g id="PROCESS">%2$s</xliff:g>) ໄດ້ລະເມີດນະໂຍບາຍ StrictMode ທີ່ບັງຄັບໃຊ້ດ້ວຍໂຕເອງ."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ແທັບເລັດ"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ໂທລະພາບ"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"ໂທລະສັບ"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ຫູຟັງ"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"ບ່ອນຕັ້ງລຳໂພງ"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ຫູຟັງ"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ລະບົບ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ສຽງ Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ການສະແດງຜົນໄຮ້ສາຍ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 9e807b0..a3deaa7 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1020,6 +1020,10 @@
<string name="dial" msgid="4204975095406423102">"Telefonas"</string>
<string name="map" msgid="6068210738233985748">"Žemėlapiai"</string>
<string name="browse" msgid="6993590095938149861">"Naršyklė"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Mažėja laisvos saugyklos vietos"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kai kurios sistemos funkcijos gali neveikti"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistemos saugykloje nepakanka vietos. Įsitikinkite, kad yra 250 MB laisvos vietos, ir paleiskite iš naujo."</string>
@@ -1029,6 +1033,7 @@
<string name="cancel" msgid="6442560571259935130">"Atšaukti"</string>
<string name="yes" msgid="5362982303337969312">"Gerai"</string>
<string name="no" msgid="5141531044935541497">"Atšaukti"</string>
+ <string name="close" msgid="2318214661230355730">"UŽDARYTI"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Dėmesio"</string>
<string name="loading" msgid="7933681260296021180">"Įkeliama..."</string>
<string name="capital_on" msgid="1544682755514494298">"ĮJ."</string>
@@ -1085,6 +1090,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Mastelis"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Visada rodyti"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Įgalinkite jį iš naujo nuėję į „Sistemos nustatymai“ > „Programos“ > „Atsisiųsta“."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Programa nereaguoja"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ gali naudoti per daug atminties."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"Programoje „<xliff:g id="APP_NAME">%1$s</xliff:g>“ nepalaikomas dabartinis ekrano dydžio nustatymas ir ji gali netinkamai veikti."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Visada rodyti"</string>
<string name="smv_application" msgid="3307209192155442829">"Programa „<xliff:g id="APPLICATION">%1$s</xliff:g>“ (procesas „<xliff:g id="PROCESS">%2$s</xliff:g>“) pažeidė savo vykdomą „StrictMode“ politiką."</string>
@@ -1466,9 +1473,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Planšetinis kompiuteris"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefonas"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Ausinės"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Doko garsiakalbiai"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Ausinės"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"„Bluetooth“ garsas"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Belaidis rodymas"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 5058d43..c323fcc 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1000,6 +1000,10 @@
<string name="dial" msgid="4204975095406423102">"Tālrunis"</string>
<string name="map" msgid="6068210738233985748">"Kartes"</string>
<string name="browse" msgid="6993590095938149861">"Pārlūkprogramma"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Paliek maz brīvas vietas"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Dažas sistēmas funkcijas var nedarboties."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistēmai pietrūkst vietas. Atbrīvojiet vismaz 250 MB vietas un restartējiet ierīci."</string>
@@ -1009,6 +1013,7 @@
<string name="cancel" msgid="6442560571259935130">"Atcelt"</string>
<string name="yes" msgid="5362982303337969312">"Labi"</string>
<string name="no" msgid="5141531044935541497">"Atcelt"</string>
+ <string name="close" msgid="2318214661230355730">"AIZVĒRT"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Uzmanību!"</string>
<string name="loading" msgid="7933681260296021180">"Notiek ielāde..."</string>
<string name="capital_on" msgid="1544682755514494298">"IESLĒGT"</string>
@@ -1065,6 +1070,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Mērogs"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Rādīt vienmēr"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Atkārtoti iespējojiet šeit: Sistēmas iestatījumi > Lietotnes > Lejupielādētās."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Lietotne nereaģē"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Iespējams, lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> aizņem pārāk daudz vietas atmiņā."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"Lietotnē <xliff:g id="APP_NAME">%1$s</xliff:g> netiek atbalstīts pašreizējais displeja lieluma iestatījums, tādēļ tā var tikt attēlota neparedzētā veidā."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Rādīt vienmēr"</string>
<string name="smv_application" msgid="3307209192155442829">"Lietotne <xliff:g id="APPLICATION">%1$s</xliff:g> (process <xliff:g id="PROCESS">%2$s</xliff:g>) ir pārkāpusi savu pašieviesto StrictMode politiku."</string>
@@ -1443,9 +1450,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Planšetdators"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Tālrunis"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Austiņas"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Doka skaļruņi"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Austiņas"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistēma"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Bezvadu attēlošana"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index ef5430c..c3de4a9 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Телефон"</string>
<string name="map" msgid="6068210738233985748">"„Карти“"</string>
<string name="browse" msgid="6993590095938149861">"Прелистувач"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Меморијата е речиси полна"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некои системски функции може да не работат"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Нема доволно меморија во системот. Проверете дали има слободен простор од 250 МБ и рестартирајте."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
<string name="yes" msgid="5362982303337969312">"Во ред"</string>
<string name="no" msgid="5141531044935541497">"Откажи"</string>
+ <string name="close" msgid="2318214661230355730">"ЗАТВОРИ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Внимание"</string>
<string name="loading" msgid="7933681260296021180">"Се вчитува..."</string>
<string name="capital_on" msgid="1544682755514494298">"ВКЛУЧЕНО"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Размер"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Покажи секогаш"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Повторно овозможете го ова во Системски поставки > Апликации > Преземено."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Апликацијата не реагира"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> може да користи премногу меморија."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> не ја поддржува тековната поставка за големина на екранот и може да се однесува непредвидено."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Секогаш прикажувај"</string>
<string name="smv_application" msgid="3307209192155442829">"Апликацијата <xliff:g id="APPLICATION">%1$s</xliff:g> (процес <xliff:g id="PROCESS">%2$s</xliff:g>) ја прекрши политиката StrictMode што си ја наметна врз себеси."</string>
@@ -1422,9 +1429,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Таблет"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Телевизор"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Телефон"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Слушалки"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Приклучи звучници"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"ХДМИ"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Слушалки"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Систем"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Аудио на Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Безжичен приказ"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 6850119..2623167 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"ഫോണ്"</string>
<string name="map" msgid="6068210738233985748">"മാപ്സ്"</string>
<string name="browse" msgid="6993590095938149861">"ബ്രൗസർ"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"സംഭരണയിടം കഴിഞ്ഞു"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ചില സിസ്റ്റം പ്രവർത്തനങ്ങൾ പ്രവർത്തിക്കണമെന്നില്ല."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"സിസ്റ്റത്തിനായി മതിയായ സംഭരണമില്ല. 250MB സൗജന്യ സംഭരണമുണ്ടെന്ന് ഉറപ്പുവരുത്തി പുനരാരംഭിക്കുക."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"റദ്ദാക്കുക"</string>
<string name="yes" msgid="5362982303337969312">"ശരി"</string>
<string name="no" msgid="5141531044935541497">"റദ്ദാക്കുക"</string>
+ <string name="close" msgid="2318214661230355730">"അടയ്ക്കുക"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"ശ്രദ്ധിക്കുക"</string>
<string name="loading" msgid="7933681260296021180">"ലോഡുചെയ്യുന്നു..."</string>
<string name="capital_on" msgid="1544682755514494298">"ഓൺ"</string>
@@ -1026,7 +1031,7 @@
<string name="aerr_process_repeated" msgid="6235302956890402259">"<xliff:g id="PROCESS">%1$s</xliff:g> നിലയ്ക്കുന്നത് തുടരുന്നു"</string>
<string name="aerr_restart" msgid="7581308074153624475">"ആപ്പ് വീണ്ടും തുറക്കുക"</string>
<string name="aerr_report" msgid="5371800241488400617">"ഫീഡ്ബാക്ക് അയയ്ക്കുക"</string>
- <string name="aerr_close" msgid="2991640326563991340">"അടയ്ക്കുക"</string>
+ <string name="aerr_close" msgid="2991640326563991340">"അവസാനിപ്പിക്കുക"</string>
<string name="aerr_mute" msgid="1974781923723235953">"ഉപകരണം പുനഃരാരംഭിക്കുന്നത് വരെ മ്യൂട്ടുചെയ്യുക"</string>
<string name="aerr_wait" msgid="3199956902437040261">"കാത്തിരിക്കുക"</string>
<string name="aerr_close_app" msgid="3269334853724920302">"ആപ്പ് അടയ്ക്കുക"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"സ്കെയിൽ"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"എപ്പോഴും പ്രദര്ശിപ്പിക്കുക"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"സിസ്റ്റം ക്രമീകരണങ്ങൾ > അപ്ലിക്കേഷനുകൾ > ഡൗൺലോഡുചെയ്തവ എന്നതിൽ ഇത് വീണ്ടും പ്രവർത്തനക്ഷമമാക്കുക."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"ആപ്പ് പ്രതികരിക്കുന്നില്ല"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> വളരെയധികം മെമ്മറി ഉപയോഗിക്കുന്നുണ്ടാകാം."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"നിലവിലെ ഡിസ്പ്ലേ വലുപ്പ ക്രമീകരണത്തെ <xliff:g id="APP_NAME">%1$s</xliff:g> പിന്തുണയ്ക്കുന്നില്ല, അതിനാൽ പ്രതീക്ഷിക്കാത്ത തരത്തിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കാം."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"എല്ലായ്പ്പോഴും ദൃശ്യമാക്കുക"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> എന്ന അപ്ലിക്കേഷൻ (<xliff:g id="PROCESS">%2$s</xliff:g> പ്രോസസ്സ്) അതിന്റെ സ്വയം നിർബന്ധിത StrictMode നയം ലംഘിച്ചു."</string>
@@ -1189,7 +1196,7 @@
<string name="usb_notification_message" msgid="3370903770828407960">"കൂടുതൽ ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"അനലോഗ് ഓഡിയോ ആക്സസറി കണ്ടെത്തി"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"അറ്റാച്ചുചെയ്ത ഉപകരണം ഈ ഫോണിന് അനുയോജ്യമല്ല. കൂടുതലറിയാൻ ടാപ്പുചെയ്യുക."</string>
- <string name="adb_active_notification_title" msgid="6729044778949189918">"USB ഡീബഗ്ഗിംഗ് കണക്റ്റുചെയ്തു"</string>
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"USB ഡീബഗ്ഗിംഗ് കണക്റ്റ് ചെയ്തു"</string>
<string name="adb_active_notification_message" msgid="4948470599328424059">"USB ഡീബഗ്ഗിംഗ് പ്രവർത്തനരഹിതമാക്കാൻ ടാപ്പുചെയ്യുക."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ഡീബഗ്ഗുചെയ്യൽ പ്രവർത്തനരഹിതമാക്കാൻ തിരഞ്ഞെടുക്കുക."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"ബഗ് റിപ്പോർട്ട് എടുക്കുന്നു…"</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ടാബ്ലെറ്റ്"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ടിവി"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"ഫോണ്"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ഹെഡ്ഫോണുകൾ"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"ഡോക്ക് സ്പീക്കറുകൾ"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ഹെഡ്ഫോണുകൾ"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"സിസ്റ്റം"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ബ്ലൂടൂത്ത് ഓഡിയോ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"വയർലെസ് ഡിസ്പ്ലേ"</string>
@@ -1696,7 +1704,7 @@
<string name="floating_toolbar_open_overflow_description" msgid="4797287862999444631">"കൂടുതൽ ഓപ്ഷനുകള്"</string>
<string name="floating_toolbar_close_overflow_description" msgid="559796923090723804">"ഓവർഫ്ലോ അടയ്ക്കുക"</string>
<string name="maximize_button_text" msgid="7543285286182446254">"വലുതാക്കുക"</string>
- <string name="close_button_text" msgid="3937902162644062866">"അടയ്ക്കുക"</string>
+ <string name="close_button_text" msgid="3937902162644062866">"അവസാനിപ്പിക്കുക"</string>
<string name="notification_messaging_title_template" msgid="3452480118762691020">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<plurals name="selected_count" formatted="false" msgid="7187339492915744615">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> തിരഞ്ഞെടുത്തു</item>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 319ea01..ce09140 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Утас"</string>
<string name="map" msgid="6068210738233985748">"Газрын зураг"</string>
<string name="browse" msgid="6993590095938149861">"Хөтөч"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Сангийн хэмжээ дутагдаж байна"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Зарим систем функц ажиллахгүй байна"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Системд хангалттай сан байхгүй байна. 250MБ чөлөөтэй зай байгаа эсэхийг шалгаад дахин эхлүүлнэ үү."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Цуцлах"</string>
<string name="yes" msgid="5362982303337969312">"ОК"</string>
<string name="no" msgid="5141531044935541497">"Цуцлах"</string>
+ <string name="close" msgid="2318214661230355730">"ХААХ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Анхаар"</string>
<string name="loading" msgid="7933681260296021180">"Ачааллаж байна..."</string>
<string name="capital_on" msgid="1544682755514494298">"Идэвхтэй"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Цар хэмжээ"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Байнга харуулах"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Энийг Системийн тохиргоо > Апп > Татаж авсан дотроос дахин идэвхтэй болгох боломжтой."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Апп хариу өгөхгүй байна"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> хэт их санах ой ашиглаж байж болзошгүй."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> нь Дэлгэцийн хэмжээний одоогийн тохиргоог дэмждэггүй учир буруу ажиллаж болзошгүй."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Байнга харуулах"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> апп (<xliff:g id="PROCESS">%2$s</xliff:g> процесс) өөрийнхөө StrictMode бодлогыг зөрчив."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Таблет"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Tелевиз"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Утас"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Чихэвч"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Чанга яригчийг суулгах"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Чихэвч"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Систем"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth аудио"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Утасгүй дэлгэц"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 8436eb4..2d0cad7 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -641,7 +641,7 @@
<string name="phoneTypeTtyTdd" msgid="8606514378585000044">"TTY TDD"</string>
<string name="phoneTypeWorkMobile" msgid="1311426989184065709">"कार्य मोबाईल"</string>
<string name="phoneTypeWorkPager" msgid="649938731231157056">"कार्य पेजर"</string>
- <string name="phoneTypeAssistant" msgid="5596772636128562884">"सहाय्यक"</string>
+ <string name="phoneTypeAssistant" msgid="5596772636128562884">"साहाय्यक"</string>
<string name="phoneTypeMms" msgid="7254492275502768992">"MMS"</string>
<string name="eventTypeCustom" msgid="7837586198458073404">"सानुकूल"</string>
<string name="eventTypeBirthday" msgid="2813379844211390740">"वाढदिवस"</string>
@@ -674,7 +674,7 @@
<string name="orgTypeOther" msgid="3951781131570124082">"अन्य"</string>
<string name="orgTypeCustom" msgid="225523415372088322">"सानुकूल"</string>
<string name="relationTypeCustom" msgid="3542403679827297300">"सानुकूल"</string>
- <string name="relationTypeAssistant" msgid="6274334825195379076">"सहाय्यक"</string>
+ <string name="relationTypeAssistant" msgid="6274334825195379076">"साहाय्यक"</string>
<string name="relationTypeBrother" msgid="8757913506784067713">"भाऊ"</string>
<string name="relationTypeChild" msgid="1890746277276881626">"मूल"</string>
<string name="relationTypeDomesticPartner" msgid="6904807112121122133">"घरातील जोडीदार"</string>
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"फोन"</string>
<string name="map" msgid="6068210738233985748">"नकाशे"</string>
<string name="browse" msgid="6993590095938149861">"ब्राउझर"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"संचयन स्थान संपत आहे"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"काही सिस्टम कार्ये कार्य करू शकत नाहीत"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"सिस्टीमसाठी पुरेसे संचयन नाही. आपल्याकडे 250MB मोकळे स्थान असल्याचे सुनिश्चित करा आणि रीस्टार्ट करा."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"रद्द करा"</string>
<string name="yes" msgid="5362982303337969312">"ठीक"</string>
<string name="no" msgid="5141531044935541497">"रद्द करा"</string>
+ <string name="close" msgid="2318214661230355730">"बंद करा"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"लक्ष द्या"</string>
<string name="loading" msgid="7933681260296021180">"लोड करीत आहे..."</string>
<string name="capital_on" msgid="1544682755514494298">"चालू"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"स्केल"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"नेहमी दर्शवा"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"सिस्टम सेटिंग्ज > Apps > डाउनलोड केलेले मध्ये हे पुन्हा-सक्षम करा."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"अॅप प्रतिसाद देत नाही"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> कदाचित बरीच मेमरी वापरत आहे."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> वर्तमान डिस्प्ले आकार सेटिंगला समर्थन देत नाही आणि अनपेक्षित वर्तन करू शकते."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"नेहमी दर्शवा"</string>
<string name="smv_application" msgid="3307209192155442829">"अॅप <xliff:g id="APPLICATION">%1$s</xliff:g> (प्रक्रिया <xliff:g id="PROCESS">%2$s</xliff:g>) ने तिच्या स्वयं-लागू केलेल्या StrictMode धोरणाचे उल्लंघन केले आहे."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"टॅबलेट"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"टीव्ही"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"फोन"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"हेडफोन"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"स्पीकर डॉक करा"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"हेडफोन"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"सिस्टम"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ब्लूटूथ ऑडिओ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"वायरलेस डिस्प्ले"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 67d81d4..536f2c2 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Peta"</string>
<string name="browse" msgid="6993590095938149861">"Penyemak imbas"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang storan semakin berkurangan"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak berfungsi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Tidak cukup storan untuk sistem. Pastikan anda mempunyai 250MB ruang kosong dan mulakan semula."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Batal"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Batal"</string>
+ <string name="close" msgid="2318214661230355730">"TUTUP"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Perhatian"</string>
<string name="loading" msgid="7933681260296021180">"Memuatkan…"</string>
<string name="capital_on" msgid="1544682755514494298">"HIDUP"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Skala"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Sentiasa tunjukkan"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Dayakan semula kod kompak ini tetapan Sistem > Apl > Dimuat turun."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Apl tidak bertindak balas"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> mungkin menggunakan terlalu banyak memori."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak menyokong tetapan saiz Paparan semasa dan mungkin menunjukkan gelagat yang tidak dijangka."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Sentiasa tunjukkan"</string>
<string name="smv_application" msgid="3307209192155442829">"Apl <xliff:g id="APPLICATION">%1$s</xliff:g> (proses <xliff:g id="PROCESS">%2$s</xliff:g>) telah melanggar dasar Mod Tegasnya sendiri."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Fon kepala"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Pembesar suara dok"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Fon kepala"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Paparan wayarles"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index d432505..c0539a5 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"ဖုန်း"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"ဘရောင်ဇာ"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"သိမ်းဆည်သော နေရာ နည်းနေပါသည်"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"တချို့ စနစ်လုပ်ငန်းများ အလုပ် မလုပ်ခြင်း ဖြစ်နိုင်ပါသည်"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"စနစ်အတွက် သိုလှောင်ခန်း မလုံလောက်ပါ။ သင့်ဆီမှာ နေရာလွတ် ၂၅၀ MB ရှိတာ စစ်ကြည့်ပြီး စတင်ပါ။"</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"မလုပ်တော့"</string>
<string name="yes" msgid="5362982303337969312">"အိုကေ"</string>
<string name="no" msgid="5141531044935541497">"မလုပ်တော့"</string>
+ <string name="close" msgid="2318214661230355730">"ပိတ်ရန်"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"သတိပြုရန်"</string>
<string name="loading" msgid="7933681260296021180">"တင်နေ…"</string>
<string name="capital_on" msgid="1544682755514494298">"ဖွင့်ရန်"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"စကေး"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"အမြဲပြသရန်"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"ဒါကို စနစ် ဆက်တင်များထဲ ပြန်ဖွင့်ပေးရန် > Apps > ဒေါင်းလုဒ် လုပ်ပြီး။"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"အက်ပ်က တုံ့ပြန်မှုမရှိပါ"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> က နေရာအလွန်အကျွံ ယူထားပုံရပါသည်။"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် လက်ရှိ မျက်နှာပြင်အရွယ်အစားကို ပံ့ပိုးထားခြင်း မရှိပါ။ မမျှော်လင့်နိုင်သည့် ချွတ်ယွင်းချက်များ ဖြစ်ပေါ်နိုင်ပါသည်။"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"အမြဲပြပါ"</string>
<string name="smv_application" msgid="3307209192155442829">"app <xliff:g id="APPLICATION">%1$s</xliff:g> (လုပ်ငန်းစဉ် <xliff:g id="PROCESS">%2$s</xliff:g>) က ကိုယ်တိုင် ပြဌာန်းခဲ့သည့် StrictMode မူဝါဒကို ချိုးဖောက်ခဲ့သည်။"</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"တက်ဘလက်"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"တီဗွီ"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"ဖုန်း"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"နားကြပ်"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"အထိုင်ရှိသော စပီကာများ"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"နားကြပ်"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"စနစ်"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ဘလူးတုသ် အသံ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ကြိုးမဲ့ပြသခြင်း"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 07a622c..4eb0f1d 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Kart"</string>
<string name="browse" msgid="6993590095938149861">"Nettleser"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lite ledig lagringsplass"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Det er ikke nok lagringsplass for systemet. Kontrollér at du har 250 MB ledig plass, og start på nytt."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Avbryt"</string>
+ <string name="close" msgid="2318214661230355730">"LUKK"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Merk"</string>
<string name="loading" msgid="7933681260296021180">"Laster inn …"</string>
<string name="capital_on" msgid="1544682755514494298">"På"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Skala"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Vis alltid"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Reaktiver dette i systeminnstillingene > Apper > Nedlastet."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Appen svarer ikke"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> bruker muligens for mye minne."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> støtter ikke den nåværende innstillingen for skjermstørrelse og fungerer kanskje ikke som den skal."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Vis alltid"</string>
<string name="smv_application" msgid="3307209192155442829">"Appen <xliff:g id="APPLICATION">%1$s</xliff:g> (prosessen <xliff:g id="PROCESS">%2$s</xliff:g>) har brutt de selvpålagte StrictMode-retningslinjene."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Nettbrett"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Google TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Hodetelefoner"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dokkhøyttalere"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Hodetelefoner"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-lyd"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Trådløs skjerm"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 581f344..16743d5 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"फोन गर्नुहोस्"</string>
<string name="map" msgid="6068210738233985748">"नक्सा"</string>
<string name="browse" msgid="6993590095938149861">"ब्राउजर"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"भण्डारण ठाउँ सकिँदै छ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"सायद केही प्रणाली कार्यक्रमहरूले काम गर्दैनन्"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"प्रणालीको लागि पर्याप्त भण्डारण छैन। तपाईँसँग २५० मेगा बाइट ठाउँ खाली भएको निश्चित गर्नुहोस् र फेरि सुरु गर्नुहोस्।"</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"रद्द गर्नुहोस्"</string>
<string name="yes" msgid="5362982303337969312">"ठीक छ"</string>
<string name="no" msgid="5141531044935541497">"रद्द गर्नुहोस्"</string>
+ <string name="close" msgid="2318214661230355730">"बन्द गर्नुहोस्"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"सावधानी"</string>
<string name="loading" msgid="7933681260296021180">"लोड हुँदै..."</string>
<string name="capital_on" msgid="1544682755514494298">"चालु"</string>
@@ -1051,6 +1056,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"स्केल"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"सधैँ देखाउनुहोस्"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"प्रणाली सेटिङहरूमा यसलाई पुनःसक्षम गराउनुहोस् > अनुप्रयोगहरू > डाउनलोड गरेको।"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"अनुप्रयोग चलिरहेको छैन"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले अत्यधिक मेमोरी प्रयोग गरिरहेको हुनसक्छ।"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले हालको प्रदर्शनको आकार सम्बन्धी सेटिङलाई समर्थन गर्दैन र अप्रत्याशित तरिकाले व्यवहार गर्न सक्छ।"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"सधैँ देखाउनुहोस्"</string>
<string name="smv_application" msgid="3307209192155442829">"अनुप्रयोग <xliff:g id="APPLICATION">%1$s</xliff:g> (प्रक्रिया <xliff:g id="PROCESS">%2$s</xliff:g>) ले यसको स्वयं-लागु गरिएको स्ट्रिटमोड नीति उलङ्घन गरेको छ।"</string>
@@ -1426,9 +1433,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ट्याब्लेट"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"फोन"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"हेडफोनहरू"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"डक स्पिकरहरू"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"हेडफोनहरू"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"प्रणाली"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"ब्लुटुथ अडियो"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ताररहित प्रदर्शन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 45dd701..e914caf 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefoon"</string>
<string name="map" msgid="6068210738233985748">"Kaarten"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Opslagruimte is bijna vol"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bepaalde systeemfuncties werken mogelijk niet"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Onvoldoende opslagruimte voor het systeem. Zorg ervoor dat je 250 MB vrije ruimte hebt en start opnieuw."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Annuleren"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Annuleren"</string>
+ <string name="close" msgid="2318214661230355730">"SLUITEN"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Let op"</string>
<string name="loading" msgid="7933681260296021180">"Laden..."</string>
<string name="capital_on" msgid="1544682755514494298">"AAN"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Schaal"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Altijd weergeven"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"U kunt dit opnieuw inschakelen via Systeeminstellingen > Apps > Gedownload."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"App reageert niet"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> gebruikt mogelijk te veel geheugen"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> biedt geen ondersteuning voor de huidige instelling voor weergavegrootte en kan onverwacht gedrag vertonen."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Altijd weergeven"</string>
<string name="smv_application" msgid="3307209192155442829">"De app <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) heeft het zelf afgedwongen StrictMode-beleid geschonden."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Tv"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefoon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Hoofdtelefoon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dockluidsprekers"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Hoofdtelefoon"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Systeem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Draadloze weergave"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index bc09332..7917b23 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -45,10 +45,10 @@
<string name="invalidPuk" msgid="8761456210898036513">"ਇੱਕ PUK ਕੋਡ ਟਾਈਪ ਕਰੋ ਜੋ 8 ਜਾਂ ਵੱਧ ਸੰਖਿਆਵਾਂ ਦਾ ਹੋਵੇ।"</string>
<string name="needPuk" msgid="919668385956251611">"ਤੁਹਾਡਾ ਸਿਮ ਕਾਰਡ PUK-ਲੌਕਡ ਹੈ। ਇਸਨੂੰ ਅਣਲਾਕ ਕਰਨ ਲਈ PUK ਕੋਡ ਟਾਈਪ ਕਰੋ।"</string>
<string name="needPuk2" msgid="4526033371987193070">"SIM ਕਾਰਡ ਅਨਬਲੌਕ ਕਰਨ ਲਈ PUK2 ਟਾਈਪ ਕਰੋ।"</string>
- <string name="enablePin" msgid="209412020907207950">"ਅਸਫਲ, SIM/RUIM ਲੌਕ ਨੂੰ ਚਾਲੂ ਕਰੋ।"</string>
+ <string name="enablePin" msgid="209412020907207950">"ਅਸਫਲ, SIM/RUIM ਲਾਕ ਨੂੰ ਚਾਲੂ ਕਰੋ।"</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1251012001539225582">
- <item quantity="one">ਇਸਤੋਂ ਪਹਿਲਾਂ ਕਿ SIM ਲੌਕ ਹੋਵੇ, ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ।</item>
- <item quantity="other">ਇਸਤੋਂ ਪਹਿਲਾਂ ਕਿ SIM ਲੌਕ ਹੋਵੇ, ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ।</item>
+ <item quantity="one">ਇਸਤੋਂ ਪਹਿਲਾਂ ਕਿ SIM ਲਾਕ ਹੋਵੇ, ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ।</item>
+ <item quantity="other">ਇਸਤੋਂ ਪਹਿਲਾਂ ਕਿ SIM ਲਾਕ ਹੋਵੇ, ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ।</item>
</plurals>
<string name="imei" msgid="2625429890869005782">"IMEI"</string>
<string name="meid" msgid="4841221237681254195">"MEID"</string>
@@ -183,7 +183,7 @@
<string name="silent_mode" msgid="7167703389802618663">"ਸਾਈਲੈਂਟ ਮੋਡ"</string>
<string name="turn_on_radio" msgid="3912793092339962371">"ਵਾਇਰਲੈਸ ਚਾਲੂ ਕਰੋ"</string>
<string name="turn_off_radio" msgid="8198784949987062346">"ਵਾਇਰਲੈਸ ਬੰਦ ਕਰੋ"</string>
- <string name="screen_lock" msgid="799094655496098153">"ਸਕ੍ਰੀਨ ਲੌਕ"</string>
+ <string name="screen_lock" msgid="799094655496098153">"ਸਕ੍ਰੀਨ ਲਾਕ"</string>
<string name="power_off" msgid="4266614107412865048">"ਪਾਵਰ ਬੰਦ"</string>
<string name="silent_mode_silent" msgid="319298163018473078">"ਰਿੰਗਰ ਬੰਦ"</string>
<string name="silent_mode_vibrate" msgid="7072043388581551395">"ਰਿੰਗਰ ਥਰਥਰਾਹਟ"</string>
@@ -207,7 +207,7 @@
<string name="global_actions" product="tablet" msgid="408477140088053665">"ਟੈਬਲੈੱਟ ਵਿਕਲਪ"</string>
<string name="global_actions" product="tv" msgid="7240386462508182976">"TV ਚੋਣਾਂ"</string>
<string name="global_actions" product="default" msgid="2406416831541615258">"ਫ਼ੋਨ ਚੋਣਾਂ"</string>
- <string name="global_action_lock" msgid="2844945191792119712">"ਸਕ੍ਰੀਨ ਲੌਕ"</string>
+ <string name="global_action_lock" msgid="2844945191792119712">"ਸਕ੍ਰੀਨ ਲਾਕ"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"ਪਾਵਰ ਬੰਦ"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"ਸੰਕਟਕਾਲ"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"ਬਗ ਰਿਪੋਰਟ"</string>
@@ -340,7 +340,7 @@
<string name="permdesc_systemAlertWindow" msgid="2393776099672266188">"ਇਹ ਐਪ ਹੋਰ ਐਪਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਜਾਂ ਸਕ੍ਰੀਨ ਦੇ ਹੋਰ ਭਾਗਾਂ \'ਤੇ ਵਿਖਾਈ ਦੇ ਸਕਦੀ ਹੈ। ਇਹ ਸਧਾਰਨ ਐਪ ਵਰਤੋਂ ਵਿੱਚ ਵਿਘਨ ਪਾ ਸਕਦੀ ਹੈ ਅਤੇ ਹੋਰ ਐਪਾਂ ਦੇ ਵਿਖਾਈ ਦੇਣ ਦੇ ਤਰੀਕੇ ਨੂੰ ਬਦਲ ਸਕਦੀ ਹੈ।"</string>
<string name="permlab_runInBackground" msgid="7365290743781858803">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚਲਾਓ"</string>
<string name="permdesc_runInBackground" msgid="7370142232209999824">"ਇਹ ਐਪ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲ ਸਕਦੀ ਹੈ। ਇਸ ਨਾਲ ਬੈਟਰੀ ਵਧੇਰੇ ਤੇਜ਼ੀ ਨਾਲ ਖਤਮ ਹੋ ਸਕਦੀ ਹੈ।"</string>
- <string name="permlab_useDataInBackground" msgid="8694951340794341809">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਡੈਟੇ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
+ <string name="permlab_useDataInBackground" msgid="8694951340794341809">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਡਾਟੇ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
<string name="permdesc_useDataInBackground" msgid="6049514223791806027">"ਇਹ ਐਪ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਡਾਟੇ ਦੀ ਵਰਤੋਂ ਕਰ ਸਕਦੀ ਹੈ। ਇਸ ਨਾਲ ਡਾਟਾ ਵਰਤੋਂ ਵਧ ਸਕਦੀ ਹੈ।"</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"ਐਪ ਨੂੰ ਹਮੇਸ਼ਾਂ ਚਲਾਏ ਰੱਖੋ"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"ਐਪ ਨੂੰ ਮੈਮਰੀ ਵਿੱਚ ਖੁਦ ਦੇ ਭਾਗਾਂ ਨੂੰ ਸਥਾਈ ਬਣਾਉਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਟੈਬਲੈੱਟ ਨੂੰ ਹੌਲੀ ਕਰਦੇ ਹੋਏ ਹੋਰਾਂ ਐਪਾਂ ਲਈ ਉਪਲਬਧ ਮੈਮਰੀ ਨੂੰ ਸੀਮਿਤ ਕਰ ਸਕਦਾ ਹੈ।"</string>
@@ -464,7 +464,7 @@
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"ਐਪ ਨੂੰ ਬਲੂਟੁੱਥ ਤੇ ਬਲੂਟੁੱਥ ਦਾ ਸੰਰੂਪਣ ਦੇਖਣ, ਜੋੜਾਬੱਧ ਕੀਤੇ ਡੀਵਾਈਸਾਂ ਨਾਲ ਕਨੈਕਸ਼ਨ ਬਣਾਉਣ ਅਤੇ ਸਵੀਕਾਰ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_nfc" msgid="4423351274757876953">"ਨਜ਼ਦੀਕੀ ਖੇਤਰ ਸੰਚਾਰ ਤੇ ਨਿਯੰਤਰਣ ਪਾਓ"</string>
<string name="permdesc_nfc" msgid="7120611819401789907">"ਐਪ ਨੂੰ ਨਜ਼ਦੀਕੀ ਖੇਤਰ ਸੰਚਾਰ (NFC) ਟੈਗਾਂ, ਕਾਰਡਾਂ ਅਤੇ ਰੀਡਰਾਂ ਨਾਲ ਸੰਚਾਰ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
- <string name="permlab_disableKeyguard" msgid="3598496301486439258">"ਆਪਣਾ ਸਕ੍ਰੀਨ ਲੌਕ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
+ <string name="permlab_disableKeyguard" msgid="3598496301486439258">"ਆਪਣਾ ਸਕ੍ਰੀਨ ਲਾਕ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ਐਪ ਨੂੰ ਕੀਲਾਕ ਅਤੇ ਕਿਸੇ ਵੀ ਸੰਬੰਧਿਤ ਪਾਸਵਰਡ ਸੁਰੱਖਿਆ ਨੂੰ ਬੰਦ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਫ਼ੋਨ ਇੱਕ ਇਨਕਮਿੰਗ ਫ਼ੋਨ ਕਾਲ ਪ੍ਰਾਪਤ ਕਰਨ ਵੇਲੇ ਬੰਦ ਕਰਦਾ ਹੈ, ਫਿਰ ਜਦੋਂ ਕਾਲ ਖਤਮ ਹੁੰਦੀ ਹੈ ਤਾਂ ਕੀਲਾਕ ਨੂੰ ਮੁੜ-ਚਾਲੂ ਕਰਦਾ ਹੈ।"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਾਰਡਵੇਅਰ ਵਿਵਸਥਿਤ ਕਰੋ"</string>
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"ਐਪ ਨੂੰ ਵਰਤੋਂ ਲਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸ਼ਾਮਲ ਕਰਨ ਅਤੇ ਮਿਟਾਉਣ ਦੀਆਂ ਵਿਧੀਆਂ ਦੀ ਬੇਨਤੀ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
@@ -552,15 +552,15 @@
<string name="policydesc_limitPassword" msgid="2502021457917874968">"ਸਕ੍ਰੀਨ ਲਾਕ ਪਾਸਵਰਡਾਂ ਅਤੇ ਪਿੰਨ ਵਿੱਚ ਆਗਿਆ ਦਿੱਤੀ ਲੰਮਾਈ ਅਤੇ ਅੱਖਰਾਂ ਤੇ ਨਿਯੰਤਰਣ ਪਾਓ।"</string>
<string name="policylab_watchLogin" msgid="5091404125971980158">"ਸਕ੍ਰੀਨ ਅਣਲਾਕ ਕਰਨ ਦੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ \'ਤੇ ਨਿਗਰਾਨੀ ਰੱਖੋ"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਟੈਬਲੈੱਟ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਟੈਬਲੈੱਟ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
- <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਨਲੌਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗ਼ਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ TV ਨੂੰ ਲੌਕ ਕਰੋ ਜਾਂ TV ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗ਼ਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
+ <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗ਼ਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ TV ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ TV ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗ਼ਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
<string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਫ਼ੋਨ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਫ਼ੋਨ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਟੈਬਲੈੱਟ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਟੈਬਲੈੱਟ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
- <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਨਲੌਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗ਼ਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ TV ਨੂੰ ਲੌਕ ਕਰੋ ਜਾਂ TV ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗ਼ਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗ਼ਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ TV ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ TV ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗ਼ਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਫ਼ੋਨ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਫ਼ੋਨ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
- <string name="policylab_resetPassword" msgid="4934707632423915395">"ਸਕ੍ਰੀਨ ਲੌਕ ਬਦਲੋ"</string>
- <string name="policydesc_resetPassword" msgid="1278323891710619128">"ਸਕ੍ਰੀਨ ਲੌਕ ਬਦਲੋ।"</string>
- <string name="policylab_forceLock" msgid="2274085384704248431">"ਸਕ੍ਰੀਨ ਲੌਕ ਕਰੋ"</string>
- <string name="policydesc_forceLock" msgid="1141797588403827138">"ਇਸਤੇ ਨਿਯੰਤਰਣ ਪਾਓ ਕਿ ਸਕ੍ਰਿਨ ਕਿਵੇਂ ਅਤੇ ਕਦੋਂ ਲੌਕ ਹੁੰਦੀ ਹੈ।"</string>
+ <string name="policylab_resetPassword" msgid="4934707632423915395">"ਸਕ੍ਰੀਨ ਲਾਕ ਬਦਲੋ"</string>
+ <string name="policydesc_resetPassword" msgid="1278323891710619128">"ਸਕ੍ਰੀਨ ਲਾਕ ਬਦਲੋ।"</string>
+ <string name="policylab_forceLock" msgid="2274085384704248431">"ਸਕ੍ਰੀਨ ਲਾਕ ਕਰੋ"</string>
+ <string name="policydesc_forceLock" msgid="1141797588403827138">"ਇਸਤੇ ਨਿਯੰਤਰਣ ਪਾਓ ਕਿ ਸਕ੍ਰਿਨ ਕਿਵੇਂ ਅਤੇ ਕਦੋਂ ਲਾਕ ਹੁੰਦੀ ਹੈ।"</string>
<string name="policylab_wipeData" msgid="3910545446758639713">"ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ"</string>
<string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"ਇੱਕ ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਟੈਬਲੈੱਟ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
<string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"ਇੱਕ ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਟੀਵੀ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
@@ -577,8 +577,8 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"ਲੋੜ ਹੈ ਕਿ ਸਟੋਰ ਕੀਤਾ ਐਪ ਡਾਟਾ ਇਨਕ੍ਰਿਪਟ ਕੀਤਾ ਜਾਏ।"</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"ਕੈਮਰੇ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"ਸਾਰੇ ਡੀਵਾਈਸ ਕੈਮਰਿਆਂ ਦੀ ਵਰਤੋਂ ਰੋਕੋ।"</string>
- <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"ਸਕ੍ਰੀਨ ਲੌਕ ਦੀਆਂ ਕੁਝ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
- <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"ਸਕ੍ਰੀਨ ਲੌਕ ਦੀਆਂ ਕੁਝ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦੀ ਵਰਤੋਂ ਰੋਕੋ।"</string>
+ <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"ਸਕ੍ਰੀਨ ਲਾਕ ਦੀਆਂ ਕੁਝ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
+ <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"ਸਕ੍ਰੀਨ ਲਾਕ ਦੀਆਂ ਕੁਝ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦੀ ਵਰਤੋਂ ਰੋਕੋ।"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"ਘਰ"</item>
<item msgid="869923650527136615">"ਮੋਬਾਈਲ"</item>
@@ -704,7 +704,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"ਅਣਲਾਕ ਕਰਨ ਲਈ, ਪਹਿਲਾਂ ਮੀਨੂ ਫਿਰ 0 ਦਬਾਓ।"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ਐਮਰਜੈਂਸੀ ਨੰਬਰ"</string>
<string name="lockscreen_carrier_default" msgid="6169005837238288522">"ਕੋਈ ਸੇਵਾ ਨਹੀਂ"</string>
- <string name="lockscreen_screen_locked" msgid="7288443074806832904">"ਸਕ੍ਰੀਨ ਲੌਕ ਕੀਤੀ।"</string>
+ <string name="lockscreen_screen_locked" msgid="7288443074806832904">"ਸਕ੍ਰੀਨ ਲਾਕ ਕੀਤੀ।"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ ਜਾਂ ਸੰਕਟਕਾਲੀਨ ਕਾਲ ਕਰੋ।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ।"</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਪੈਟਰਨ ਡ੍ਰਾ ਕਰੋ"</string>
@@ -731,11 +731,11 @@
<string name="lockscreen_transport_rew_description" msgid="6944412838651990410">"ਰੀਵਾਈਂਡ ਕਰੋ"</string>
<string name="lockscreen_transport_ffw_description" msgid="42987149870928985">"ਤੇਜ਼ੀ ਨਾਲ ਅੱਗੇ ਭੇਜੋ"</string>
<string name="emergency_calls_only" msgid="6733978304386365407">"ਕੇਵਲ ਐਮਰਜੈਂਸੀ ਕਾਲਾਂ"</string>
- <string name="lockscreen_network_locked_message" msgid="143389224986028501">"ਨੈੱਟਵਰਕ ਲੌਕ ਕੀਤਾ"</string>
+ <string name="lockscreen_network_locked_message" msgid="143389224986028501">"ਨੈੱਟਵਰਕ ਲਾਕ ਕੀਤਾ"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"SIM ਕਾਰਡ PUK-ਲੌਕਡ ਹੈ।"</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"ਵਰਤੋਂਕਾਰ ਗਾਈਡ ਦੇਖੋ ਜਾਂ ਗਾਹਕ ਸੇਵਾ ਨੂੰ ਫ਼ੋਨ ਕਰੋ।"</string>
- <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM ਕਾਰਡ ਲੌਕ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
- <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM ਕਾਰਡ ਅਨਲੌਕ ਕਰ ਰਿਹਾ ਹੈ…"</string>
+ <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM ਕਾਰਡ ਲਾਕ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
+ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM ਕਾਰਡ ਅਣਲਾਕ ਕਰ ਰਿਹਾ ਹੈ…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਣਲਾਕ ਪੈਟਰਨ ਗਲਤ ਢੰਗ ਨਾਲ ਉਲੀਕਿਆ ਹੈ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਪਾਸਵਰਡ ਗਲਤ ਢੰਗ ਨਾਲ ਟਾਈਪ ਕੀਤਾ ਹੈ।\n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"ਤੁਸੀਂ ਆਪਣਾ ਪਿੰਨ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਗਲਤ ਢੰਗ ਨਾਲ ਟਾਈਪ ਕੀਤਾ ਹੈ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"ਫ਼ੋਨ ਕਰੋ"</string>
<string name="map" msgid="6068210738233985748">"ਨਕਸ਼ੇ"</string>
<string name="browse" msgid="6993590095938149861">"ਬ੍ਰਾਊਜ਼ਰ"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ਸਟੋਰੇਜ ਦੀ ਜਗ੍ਹਾ ਖਤਮ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ਕੁਝ ਸਿਸਟਮ ਫੰਕਸ਼ਨ ਕੰਮ ਨਹੀਂ ਵੀ ਕਰ ਸਕਦੇ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ਸਿਸਟਮ ਲਈ ਲੋੜੀਂਦੀ ਸਟੋਰੇਜ ਨਹੀਂ ਹੈ। ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡੇ ਕੋਲ 250MB ਖਾਲੀ ਜਗ੍ਹਾ ਹੈ ਅਤੇ ਮੁੜ-ਚਾਲੂ ਕਰੋ।"</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"ਰੱਦ ਕਰੋ"</string>
<string name="yes" msgid="5362982303337969312">"ਠੀਕ"</string>
<string name="no" msgid="5141531044935541497">"ਰੱਦ ਕਰੋ"</string>
+ <string name="close" msgid="2318214661230355730">"ਬੰਦ ਕਰੋ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"ਧਿਆਨ ਦਿਓ"</string>
<string name="loading" msgid="7933681260296021180">"ਲੋਡ ਹੋ ਰਿਹਾ ਹੈ..."</string>
<string name="capital_on" msgid="1544682755514494298">"ਚਾਲੂ"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"ਸਕੇਲ"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"ਹਮੇਸ਼ਾਂ ਦਿਖਾਓ"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"ਸਿਸਟਮ ਸੈਟਿੰਗਾਂ > ਐਪਾਂ > ਡਾਊਨਲੋਡ ਕੀਤਿਆਂ ਵਿੱਚ ਇਸਨੂੰ ਮੁੜ-ਸਮਰੱਥ ਬਣਾਓ।"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"ਐਪ ਪ੍ਰਤਿਕਿਰਿਆ ਨਹੀਂ ਦੇ ਰਹੀ ਹੈ"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"ਸ਼ਾਇਦ <xliff:g id="APP_NAME">%1$s</xliff:g> ਵੱਲੋਂ ਬਹੁਤ ਜ਼ਿਆਦਾ ਮੈਮੋਰੀ ਦੀ ਵਰਤੋਂ ਕੀਤੀ ਜਾ ਰਹੀ ਹੋਵੇ।"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਵਰਤਮਾਨ ਡਿਸਪਲੇ ਆਕਾਰ ਸੈਟਿੰਗ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ ਹੈ ਅਤੇ ਅਣਕਿਆਸੇ ਤੌਰ \'ਤੇ ਵਿਹਾਰ ਕਰ ਸਕਦੀ ਹੈ।"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"ਹਮੇਸ਼ਾ ਦਿਖਾਓ"</string>
<string name="smv_application" msgid="3307209192155442829">"ਐਪ <xliff:g id="APPLICATION">%1$s</xliff:g> (ਪ੍ਰਕਿਰਿਆ <xliff:g id="PROCESS">%2$s</xliff:g>) ਨੇ ਆਪਣੀ ਖੁਦ-ਲਾਗੂ ਕੀਤੀ ਸਟ੍ਰਿਕਟਮੋਡ ਨੀਤੀ ਦੀ ਉਲੰਘਣਾ ਕੀਤੀ ਹੈ।"</string>
@@ -1221,7 +1228,7 @@
<string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"ਇਹ ਡੀਵਾਈਸ ਇਸ <xliff:g id="NAME">%s</xliff:g> ਨੂੰ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦਾ ਹੈ। ਕਿਸੇ ਸਮਰਥਿਤ ਫਾਰਮੈਟ ਵਿੱਚ ਸਥਾਪਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="ext_media_unsupported_notification_message" product="tv" msgid="3725436899820390906">"ਇਹ ਡੀਵਾਈਸ ਇਸ <xliff:g id="NAME">%s</xliff:g> ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦਾ ਹੈ। ਕਿਸੇ ਸਮਰਥਿਤ ਫਾਰਮੈਟ ਵਿੱਚ ਸਥਾਪਤ ਕਰਨ ਲਈ ਚੁਣੋ।"</string>
<string name="ext_media_badremoval_notification_title" msgid="3206248947375505416">"<xliff:g id="NAME">%s</xliff:g> ਨੂੰ ਅਚਨਚੇਤ ਹਟਾਇਆ ਗਿਆ"</string>
- <string name="ext_media_badremoval_notification_message" msgid="380176703346946313">"ਡੇਟਾ ਦੇ ਨੁਕਸਾਨ ਤੋਂ ਬੱਚਣ ਲਈ ਹਟਾਉਣ ਤੋਂ ਪਹਿਲਾਂ <xliff:g id="NAME">%s</xliff:g> ਅਨਮਾਊਂਟ ਕਰੋ"</string>
+ <string name="ext_media_badremoval_notification_message" msgid="380176703346946313">" ਡਾਟਾ ਦੇ ਨੁਕਸਾਨ ਤੋਂ ਬੱਚਣ ਲਈ ਹਟਾਉਣ ਤੋਂ ਪਹਿਲਾਂ <xliff:g id="NAME">%s</xliff:g> ਅਨਮਾਊਂਟ ਕਰੋ"</string>
<string name="ext_media_nomedia_notification_title" msgid="1704840188641749091">"ਹਟਾਇਆ <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_nomedia_notification_message" msgid="6471542972147056586">"<xliff:g id="NAME">%s</xliff:g> ਨੂੰ ਹਟਾਇਆ ਗਿਆ, ਕੋਈ ਨਵਾਂ ਸੰਮਿਲਿਤ ਕਰੋ"</string>
<string name="ext_media_unmounting_notification_title" msgid="640674168454809372">"ਅਜੇ ਵੀ <xliff:g id="NAME">%s</xliff:g> ਨੂੰ ਕੱਢ ਰਿਹਾ ਹੈ..."</string>
@@ -1232,11 +1239,11 @@
<string name="ext_media_missing_title" msgid="620980315821543904">"<xliff:g id="NAME">%s</xliff:g> ਲਾਪਤਾ"</string>
<string name="ext_media_missing_message" msgid="5761133583368750174">"ਇਸ ਡੀਵਾਈਸ ਨੂੰ ਮੁੜ ਸੰਮਿਲਿਤ ਕਰੋ"</string>
<string name="ext_media_move_specific_title" msgid="1471100343872375842">"<xliff:g id="NAME">%s</xliff:g> ਮੂਵ ਕਰ ਰਿਹਾ ਹੈ"</string>
- <string name="ext_media_move_title" msgid="1022809140035962662">"ਡੇਟਾ ਮੂਵ ਕਰ ਰਿਹਾ ਹੈ"</string>
+ <string name="ext_media_move_title" msgid="1022809140035962662">" ਡਾਟਾ ਮੂਵ ਕਰ ਰਿਹਾ ਹੈ"</string>
<string name="ext_media_move_success_title" msgid="8575300932957954671">"ਮੂਵ ਸੰਪੂਰਣ"</string>
- <string name="ext_media_move_success_message" msgid="4199002148206265426">"ਡੇਟਾ ਨੂੰ <xliff:g id="NAME">%s</xliff:g> ਵਿੱਚ ਮੂਵ ਕੀਤਾ ਗਿਆ"</string>
- <string name="ext_media_move_failure_title" msgid="7613189040358789908">"ਡੇਟਾ ਨੂੰ ਮੂਵ ਨਹੀਂ ਕਰ ਸਕਿਆ"</string>
- <string name="ext_media_move_failure_message" msgid="1978096440816403360">"ਡੇਟਾ ਮੂਲ ਸਥਾਨ \'ਤੇ ਛੱਡਿਆ ਗਿਆ"</string>
+ <string name="ext_media_move_success_message" msgid="4199002148206265426">" ਡਾਟਾ ਨੂੰ <xliff:g id="NAME">%s</xliff:g> ਵਿੱਚ ਮੂਵ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="ext_media_move_failure_title" msgid="7613189040358789908">" ਡਾਟਾ ਨੂੰ ਮੂਵ ਨਹੀਂ ਕਰ ਸਕਿਆ"</string>
+ <string name="ext_media_move_failure_message" msgid="1978096440816403360">" ਡਾਟਾ ਮੂਲ ਸਥਾਨ \'ਤੇ ਛੱਡਿਆ ਗਿਆ"</string>
<string name="ext_media_status_removed" msgid="6576172423185918739">"ਹਟਾਏ ਗਏ"</string>
<string name="ext_media_status_unmounted" msgid="2551560878416417752">"ਹਟਾਇਆ ਗਿਆ"</string>
<string name="ext_media_status_checking" msgid="6193921557423194949">"ਜਾਂਚ ਕਰ ਰਿਹਾ ਹੈ..."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ਟੈਬਲੈੱਟ"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"ਫ਼ੋਨ ਕਰੋ"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ਹੈੱਡਫ਼ੋਨ"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"ਡੌਕ ਸਪੀਕਰਸ"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ਹੈੱਡਫ਼ੋਨ"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ਸਿਸਟਮ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth ਆਡੀਓ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"ਵਾਇਰਲੈੱਸ ਡਿਸਪਲੇ"</string>
@@ -1457,7 +1465,7 @@
<string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"ਸਿਮ ਹੁਣ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ। ਜਾਰੀ ਰੱਖਣ ਲਈ PUK ਕੋਡ ਦਾਖਲ ਕਰੋ। ਵੇਰਵਿਆਂ ਲਈ ਕੈਰੀਅਰ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"</string>
<string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"ਇੱਛਤ ਪਿੰਨ ਕੋਡ ਦਾਖਲ ਕਰੋ"</string>
<string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"ਇੱਛਤ ਪਿੰਨ ਕੋਡ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ"</string>
- <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM ਕਾਰਡ ਅਨਲੌਕ ਕਰ ਰਿਹਾ ਹੈ…"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM ਕਾਰਡ ਅਣਲਾਕ ਕਰ ਰਿਹਾ ਹੈ…"</string>
<string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"ਗਲਤ ਪਿੰਨ ਕੋਡ।"</string>
<string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"ਕੋਈ ਪਿੰਨ ਟਾਈਪ ਕਰੋ ਜੋ 4 ਤੋਂ 8 ਨੰਬਰਾਂ ਦਾ ਹੋਵੇ।"</string>
<string name="kg_invalid_sim_puk_hint" msgid="6025069204539532000">"PUK ਕੋਡ 8 ਸੰਖਿਆਵਾਂ ਦਾ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ।"</string>
@@ -1721,7 +1729,7 @@
<string name="new_sms_notification_content" msgid="7002938807812083463">"ਦੇਖਣ ਲਈ SMS ਐਪ ਖੋਲ੍ਹੋ"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"ਕੁਝ ਪ੍ਰਕਾਰਜਾਤਮਕਤਾ ਸੀਮਿਤ ਹੋ ਸਕਦੀ ਹੈ"</string>
<string name="user_encrypted_message" msgid="4923292604515744267">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
- <string name="user_encrypted_detail" msgid="5708447464349420392">"ਵਰਤੋਂਕਾਰ ਡਾਟਾ ਲੌਕ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="user_encrypted_detail" msgid="5708447464349420392">"ਵਰਤੋਂਕਾਰ ਡਾਟਾ ਲਾਕ ਕੀਤਾ ਗਿਆ"</string>
<string name="profile_encrypted_detail" msgid="3700965619978314974">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਲਾਕ ਕੀਤੀ ਗਈ"</string>
<string name="profile_encrypted_message" msgid="6964994232310195874">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਅਣਲਾਕ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="usb_mtp_launch_notification_title" msgid="8359219638312208932">"<xliff:g id="PRODUCT_NAME">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੋਈ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 5bca5a9..0989576 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -257,7 +257,7 @@
<string name="notification_channel_foreground_service" msgid="3931987440602669158">"Aplikacje zużywające baterię"</string>
<string name="foreground_service_app_in_background" msgid="1060198778219731292">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> zużywa baterię"</string>
<string name="foreground_service_apps_in_background" msgid="7175032677643332242">"Liczba aplikacji zużywających baterię: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Kliknij, by wyświetlić szczegóły wykorzystania baterii i transmisji danych"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Kliknij, by wyświetlić szczegóły wykorzystania baterii i użycia danych"</string>
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Tryb awaryjny"</string>
<string name="android_system_label" msgid="6577375335728551336">"System Android"</string>
@@ -347,7 +347,7 @@
<string name="permlab_runInBackground" msgid="7365290743781858803">"działanie w tle"</string>
<string name="permdesc_runInBackground" msgid="7370142232209999824">"Ta aplikacja może działać w tle. Bateria może się szybciej rozładowywać."</string>
<string name="permlab_useDataInBackground" msgid="8694951340794341809">"transmisja danych w tle"</string>
- <string name="permdesc_useDataInBackground" msgid="6049514223791806027">"Ta aplikacja może przesyłać i odbierać dane w tle. Transmisja danych może się zwiększyć."</string>
+ <string name="permdesc_useDataInBackground" msgid="6049514223791806027">"Ta aplikacja może przesyłać i odbierać dane w tle. Użycie danych może się zwiększyć."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"sprawianie, że aplikacja jest cały czas uruchomiona"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Pozwala aplikacji na trwałe zapisywanie swoich fragmentów w pamięci. Może to zmniejszyć ilość pamięci dostępnej dla innych aplikacji i spowolnić działanie tabletu."</string>
<string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Pozwala aplikacji zapewnić nieusuwalność swoich fragmentów z pamięci. Może to ograniczyć ilość pamięci dostępną dla innych aplikacji i spowalniać działanie telewizora."</string>
@@ -1020,6 +1020,10 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Mapy"</string>
<string name="browse" msgid="6993590095938149861">"Internet"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kończy się miejsce"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektóre funkcje systemu mogą nie działać"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Za mało pamięci w systemie. Upewnij się, że masz 250 MB wolnego miejsca i uruchom urządzenie ponownie."</string>
@@ -1029,6 +1033,7 @@
<string name="cancel" msgid="6442560571259935130">"Anuluj"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Anuluj"</string>
+ <string name="close" msgid="2318214661230355730">"ZAMKNIJ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Uwaga"</string>
<string name="loading" msgid="7933681260296021180">"Wczytuję…"</string>
<string name="capital_on" msgid="1544682755514494298">"Wł."</string>
@@ -1085,6 +1090,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Skala"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Zawsze pokazuj"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Włącz ponownie, wybierając Ustawienia systemowe > Aplikacje > Pobrane."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Aplikacja nie reaguje"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> może wykorzystywać za dużo pamięci"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> nie obsługuje obecnie ustawionego rozmiaru wyświetlacza i może działać niestabilnie."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Zawsze pokazuj"</string>
<string name="smv_application" msgid="3307209192155442829">"Aplikacja <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) naruszyła wymuszone przez siebie zasady StrictMode."</string>
@@ -1426,7 +1433,7 @@
<string name="storage_usb_drive_label" msgid="4501418548927759953">"Dysk USB (<xliff:g id="MANUFACTURER">%s</xliff:g>)"</string>
<string name="storage_usb" msgid="3017954059538517278">"Nośnik USB"</string>
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Edytuj"</string>
- <string name="data_usage_warning_title" msgid="3620440638180218181">"Alert transmisji danych"</string>
+ <string name="data_usage_warning_title" msgid="3620440638180218181">"Alert użycia danych"</string>
<string name="data_usage_warning_body" msgid="6660692274311972007">"Kliknij, by wyświetlić użycie i ustawienia."</string>
<string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Osiągnięto limit danych 2G/3G"</string>
<string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Osiągnięto limit danych 4G"</string>
@@ -1466,9 +1473,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Telewizor"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Słuchawki"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Głośniki stacji dokującej"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Słuchawki"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Dźwięk Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wyświetlacz bezprzewodowy"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 43c0bb1..a9e0ac9 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefone"</string>
<string name="map" msgid="6068210738233985748">"Mapas"</string>
<string name="browse" msgid="6993590095938149861">"Navegador"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Pouco espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema podem não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Cancelar"</string>
+ <string name="close" msgid="2318214661230355730">"FECHAR"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Atenção"</string>
<string name="loading" msgid="7933681260296021180">"Carregando…"</string>
<string name="capital_on" msgid="1544682755514494298">"LIG"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Escala"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Mostrar sempre"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Reativar isso em Configurações do sistema > Apps > Transferidos."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"O app não está respondendo"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> pode estar usando muita memória."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com a configuração atual de tamanho de exibição e pode se comportar de forma inesperada."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Mostrar sempre"</string>
<string name="smv_application" msgid="3307209192155442829">"O app <xliff:g id="APPLICATION">%1$s</xliff:g>, processo <xliff:g id="PROCESS">%2$s</xliff:g>, violou a política StrictMode imposta automaticamente."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefone"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Fones de ouvido"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Alto-falantes da dock"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Fones de ouvido"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Áudio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Display sem fio"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index d130f97..cd26bd5 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telemóvel"</string>
<string name="map" msgid="6068210738233985748">"Mapas"</string>
<string name="browse" msgid="6993590095938149861">"Navegador"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Está quase sem espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema poderão não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não existe armazenamento suficiente para o sistema. Certifique-se de que tem 250 MB de espaço livre e reinicie."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Cancelar"</string>
+ <string name="close" msgid="2318214661230355730">"FECHAR"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Atenção"</string>
<string name="loading" msgid="7933681260296021180">"A carregar…"</string>
<string name="capital_on" msgid="1544682755514494298">"Ativado"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Escala"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Mostrar sempre"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Reative este modo nas Definições do Sistema > Aplicações > Transferidas."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"A aplicação não está a responder"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"A aplicação <xliff:g id="APP_NAME">%1$s</xliff:g> pode estar a utilizar demasiada memória."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> não suporta a definição de Tamanho do ecrã atual e pode ter um comportamento inesperado."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Mostrar sempre"</string>
<string name="smv_application" msgid="3307209192155442829">"A aplicação <xliff:g id="APPLICATION">%1$s</xliff:g> (processo <xliff:g id="PROCESS">%2$s</xliff:g>) violou a política StrictMode auto-imposta."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telemóvel"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Auscultadores"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Altif. estação ancoragem"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Auscultadores"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Áudio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Visualização sem fios"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 43c0bb1..a9e0ac9 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefone"</string>
<string name="map" msgid="6068210738233985748">"Mapas"</string>
<string name="browse" msgid="6993590095938149861">"Navegador"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Pouco espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema podem não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Cancelar"</string>
+ <string name="close" msgid="2318214661230355730">"FECHAR"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Atenção"</string>
<string name="loading" msgid="7933681260296021180">"Carregando…"</string>
<string name="capital_on" msgid="1544682755514494298">"LIG"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Escala"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Mostrar sempre"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Reativar isso em Configurações do sistema > Apps > Transferidos."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"O app não está respondendo"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> pode estar usando muita memória."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com a configuração atual de tamanho de exibição e pode se comportar de forma inesperada."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Mostrar sempre"</string>
<string name="smv_application" msgid="3307209192155442829">"O app <xliff:g id="APPLICATION">%1$s</xliff:g>, processo <xliff:g id="PROCESS">%2$s</xliff:g>, violou a política StrictMode imposta automaticamente."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefone"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Fones de ouvido"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Alto-falantes da dock"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Fones de ouvido"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Áudio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Display sem fio"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index d496941..67f17b25 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1000,6 +1000,10 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Hărți"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spațiul de stocare aproape ocupat"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Este posibil ca unele funcții de sistem să nu funcționeze"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Spațiu de stocare insuficient pentru sistem. Asigurați-vă că aveți 250 MB de spațiu liber și reporniți."</string>
@@ -1009,6 +1013,7 @@
<string name="cancel" msgid="6442560571259935130">"Anulați"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Anulați"</string>
+ <string name="close" msgid="2318214661230355730">"ÎNCHIDEȚI"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Atenție"</string>
<string name="loading" msgid="7933681260296021180">"Se încarcă…"</string>
<string name="capital_on" msgid="1544682755514494298">"DA"</string>
@@ -1065,6 +1070,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Scară"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Afișați întotdeauna"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Reactivați acest mod din Setări de sistem > Aplicații > Descărcate."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Aplicația nu răspunde"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Este posibil ca <xliff:g id="APP_NAME">%1$s</xliff:g> să utilizeze prea multă memorie."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu acceptă setarea actuală pentru Dimensiunea afișării și este posibil să aibă un comportament neașteptat."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Afișează întotdeauna"</string>
<string name="smv_application" msgid="3307209192155442829">"Aplicația <xliff:g id="APPLICATION">%1$s</xliff:g> (procesul <xliff:g id="PROCESS">%2$s</xliff:g>) a încălcat propria politică StrictMode."</string>
@@ -1443,9 +1450,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tabletă"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Căști"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Difuz. dispozit. andocare"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Căști"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Ecran wireless"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 564dbbb..70631d0 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1020,6 +1020,10 @@
<string name="dial" msgid="4204975095406423102">"Телефон"</string>
<string name="map" msgid="6068210738233985748">"Карты"</string>
<string name="browse" msgid="6993590095938149861">"Браузер"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Недостаточно памяти"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некоторые функции могут не работать"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Недостаточно свободного места для системы. Освободите не менее 250 МБ дискового пространства и перезапустите устройство."</string>
@@ -1029,6 +1033,7 @@
<string name="cancel" msgid="6442560571259935130">"Отмена"</string>
<string name="yes" msgid="5362982303337969312">"ОК"</string>
<string name="no" msgid="5141531044935541497">"Отмена"</string>
+ <string name="close" msgid="2318214661230355730">"ЗАКРЫТЬ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Внимание"</string>
<string name="loading" msgid="7933681260296021180">"Загрузка…"</string>
<string name="capital_on" msgid="1544682755514494298">"I"</string>
@@ -1085,6 +1090,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Масштаб"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Всегда показывать"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Включить эту функцию можно в меню \"Настройки > Приложения > Загруженные\"."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Приложение не отвечает"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Возможно, приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" использует слишком много памяти."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" не поддерживает выбранный масштаб изображения на экране и может работать некорректно."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Всегда показывать"</string>
<string name="smv_application" msgid="3307209192155442829">"Приложение \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" (процесс: <xliff:g id="PROCESS">%2$s</xliff:g>) нарушило собственную политику StrictMode."</string>
@@ -1466,9 +1473,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Планшетный ПК"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Телевизор"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Телефон"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Наушники"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Динамики док-станции"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Наушники"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Система"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Воспроизведение звука через Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Беспроводной монитор"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 39caed3..11c42dc 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -982,6 +982,10 @@
<string name="dial" msgid="4204975095406423102">"දුරකථනය"</string>
<string name="map" msgid="6068210738233985748">"සිතියම්"</string>
<string name="browse" msgid="6993590095938149861">"බ්රවුසරය"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ආචයනය ඉඩ ප්රමාණය අඩු වී ඇත"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"සමහර පද්ධති කාර්යයන් ක්රියා නොකරනු ඇත"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"පද්ධතිය සඳහා ප්රමාණවත් ඉඩ නොමැත. ඔබට 250MB නිදහස් ඉඩක් තිබෙන ඔබට තිබෙන බව සහතික කරගෙන නැවත උත්සාහ කරන්න."</string>
@@ -991,6 +995,7 @@
<string name="cancel" msgid="6442560571259935130">"අවලංගු කරන්න"</string>
<string name="yes" msgid="5362982303337969312">"හරි"</string>
<string name="no" msgid="5141531044935541497">"අවලංගු කරන්න"</string>
+ <string name="close" msgid="2318214661230355730">"වසන්න"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"අවධානය"</string>
<string name="loading" msgid="7933681260296021180">"පූරණය වෙමින්..."</string>
<string name="capital_on" msgid="1544682755514494298">"සක්රීයයි"</string>
@@ -1047,6 +1052,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"පරිමාණය"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"සැමවිටම පෙන්වන්න"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"පද්ධති සැකසීම් තුළ මෙය නැවත ක්රියාත්මක කරන්න > යෙදුම් > බාගන්නා ලදි."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"යෙදුම ප්රතිචාර නොදක්වයි"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> මතකය ඉතා වැඩියෙන් භාවිත කරනවා විය හැකිය."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> වත්මන් සංදර්ශක තරම සඳහා සහාය නොදක්වන අතර අනපේක්ෂිත ලෙස හැසිරීමට හැකිය."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"සැම විටම පෙන්වන්න"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> යෙදුම (<xliff:g id="PROCESS">%2$s</xliff:g> ක්රියාවලිය) එහි StrictMode කොන්දේසිය උල්ලංඝනය කර ඇත."</string>
@@ -1422,9 +1429,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ටැබ්ලට්ය"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"රූපවාහිනී"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"දුරකථනය"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ඉස් බණු"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"නාදක ඩොක් කරන්න"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ඉස් බණු"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"පද්ධතිය"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"බ්ලූටූත් ශ්රව්ය"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"නොරැහැන් සංදර්ශකය"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 11b9c88..1eb24ac 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1020,6 +1020,10 @@
<string name="dial" msgid="4204975095406423102">"Telefón"</string>
<string name="map" msgid="6068210738233985748">"Mapy"</string>
<string name="browse" msgid="6993590095938149861">"Prehliadač"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nedostatok ukladacieho priestoru"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektoré systémové funkcie nemusia fungovať"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"V úložisku nie je dostatok voľného miesta pre systém. Zaistite, aby ste mali 250 MB voľného miesta a zariadenie reštartujte."</string>
@@ -1029,6 +1033,7 @@
<string name="cancel" msgid="6442560571259935130">"Zrušiť"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Zrušiť"</string>
+ <string name="close" msgid="2318214661230355730">"ZAVRIEŤ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Upozornenie"</string>
<string name="loading" msgid="7933681260296021180">"Načítava sa…"</string>
<string name="capital_on" msgid="1544682755514494298">"I"</string>
@@ -1085,6 +1090,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Prispôsobiť veľkosť"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Vždy zobraziť"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Povoľte to znova v sekcii Nastavenia systému > Aplikácie > Stiahnuté súbory."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Aplikácia nereaguje"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> pravdepodobne používa príliš veľa pamäte."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> aktuálne nastavenie veľkosti zobrazenia nepodporuje a môže sa správať neočakávane."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Vždy zobrazovať"</string>
<string name="smv_application" msgid="3307209192155442829">"Aplikácia <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) porušila svoje vlastné vynútené pravidlá StrictMode."</string>
@@ -1466,9 +1473,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televízor"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefón"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Slúchadlá"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Reproduktory doku"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Slúchadlá"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Systém"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Bezdrôtové zobrazenie"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 71b7126..4396cd3 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1020,6 +1020,10 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Zemljevidi"</string>
<string name="browse" msgid="6993590095938149861">"Brskalnik"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Prostor za shranjevanje bo pošel"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nekatere sistemske funkcije morda ne delujejo"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"V shrambi ni dovolj prostora za sistem. Sprostite 250 MB prostora in znova zaženite napravo."</string>
@@ -1029,6 +1033,7 @@
<string name="cancel" msgid="6442560571259935130">"Prekliči"</string>
<string name="yes" msgid="5362982303337969312">"V redu"</string>
<string name="no" msgid="5141531044935541497">"Prekliči"</string>
+ <string name="close" msgid="2318214661230355730">"ZAPRI"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Pozor"</string>
<string name="loading" msgid="7933681260296021180">"Nalaganje …"</string>
<string name="capital_on" msgid="1544682755514494298">"VKLOPLJENO"</string>
@@ -1085,6 +1090,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Lestvica"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Vedno pokaži"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Znova omogočite to v sistemskih nastavitvah > Aplikacije > Preneseno."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Aplikacija se ne odziva"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> morda uporablja preveč pomnilnika."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> ne podpira trenutne nastavitve velikosti zaslona, kar lahko vodi v nepričakovano delovanje."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Vedno pokaži"</string>
<string name="smv_application" msgid="3307209192155442829">"Aplikacija <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) krši svoj samouveljavljiv pravilnik o strogem načinu."</string>
@@ -1466,9 +1473,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablični računalnik"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televizor"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Slušalke"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Zvočniki stojala"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Slušalke"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Zvok prek Bluetootha"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Brezžični prikaz"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 3ad574a..d62f95f 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefoni"</string>
<string name="map" msgid="6068210738233985748">"Hartat"</string>
<string name="browse" msgid="6993590095938149861">"Shfletuesi"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Hapësira ruajtëse po mbaron"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Disa funksione të sistemit mund të mos punojnë"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nuk ka hapësirë të mjaftueshme ruajtjeje për sistemin. Sigurohu që të kesh 250 MB hapësirë të lirë dhe pastaj të rifillosh."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Anulo"</string>
<string name="yes" msgid="5362982303337969312">"Në rregull"</string>
<string name="no" msgid="5141531044935541497">"Anulo"</string>
+ <string name="close" msgid="2318214661230355730">"MBYLL"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Kujdes!"</string>
<string name="loading" msgid="7933681260296021180">"Po ngarkohet..."</string>
<string name="capital_on" msgid="1544682755514494298">"Aktivizuar"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Shkalla"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Shfaq gjithnjë"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Aktivizoje sërish këtë te \"Cilësimet e sistemit\" > \"Aplikacionet\" > \"Të shkarkuara\"."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Aplikacioni nuk po përgjigjet"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> mund të jetë duke përdorur shumë memorie."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk mbështet cilësimin aktual të madhësisë së ekranit dhe mund të shfaqë sjellje të papritura."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Shfaq gjithmonë"</string>
<string name="smv_application" msgid="3307209192155442829">"Aplikacioni <xliff:g id="APPLICATION">%1$s</xliff:g> (procesi <xliff:g id="PROCESS">%2$s</xliff:g>) ka shkelur politikën e tij të vetë-imponuar \"Modaliteti i ashpër\" (StrictMode)."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Televizori"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Kufjet"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Altoparlantët e stacionit"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Kufjet"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistemi"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audioja e \"bluetooth-it\""</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Ekran pa tel"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index f9b865d..37552af 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1000,6 +1000,10 @@
<string name="dial" msgid="4204975095406423102">"Позови"</string>
<string name="map" msgid="6068210738233985748">"Мапе"</string>
<string name="browse" msgid="6993590095938149861">"Прегледач"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Меморијски простор је на измаку"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Неке системске функције можда не функционишу"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Нема довољно меморијског простора за систем. Уверите се да имате 250 MB слободног простора и поново покрените."</string>
@@ -1009,6 +1013,7 @@
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
<string name="yes" msgid="5362982303337969312">"Потврди"</string>
<string name="no" msgid="5141531044935541497">"Откажи"</string>
+ <string name="close" msgid="2318214661230355730">"ЗАТВОРИ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Пажња"</string>
<string name="loading" msgid="7933681260296021180">"Учитава се…"</string>
<string name="capital_on" msgid="1544682755514494298">"ДА"</string>
@@ -1065,6 +1070,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Размера"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Увек приказуј"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Поново омогућите у менију Системска подешавања > Апликације > Преузето."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Апликација не реагује"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> можда користи превише меморије."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> не подржава тренутно подешавање величине приказа и може да се понаша неочекивано."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Увек приказуј"</string>
<string name="smv_application" msgid="3307209192155442829">"Апликација <xliff:g id="APPLICATION">%1$s</xliff:g> (процес <xliff:g id="PROCESS">%2$s</xliff:g>) је прекршила самонаметнуте StrictMode смернице."</string>
@@ -1443,9 +1450,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Таблет"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ТВ"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Телефон"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Слушалице"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Звучници базне станице"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Слушалице"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Систем"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth аудио"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Бежични екран"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 3f7cecb..64d8cba 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Kartor"</string>
<string name="browse" msgid="6993590095938149861">"Webbläsare"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lagringsutrymmet börjar ta slut"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Det kan hända att vissa systemfunktioner inte fungerar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Det finns inte tillräckligt med utrymme för systemet. Kontrollera att du har ett lagringsutrymme på minst 250 MB och starta om."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Avbryt"</string>
+ <string name="close" msgid="2318214661230355730">"STÄNG"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Obs!"</string>
<string name="loading" msgid="7933681260296021180">"Läser in …"</string>
<string name="capital_on" msgid="1544682755514494298">"PÅ"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Anpassning"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Visa alltid"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Aktivera detta igen i Systeminställningar > Appar > Hämtat."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Appen svarar inte"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> kanske använder för mycket minne."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> har inte stöd för den nuvarande inställningen för skärmstorlek och kanske inte fungerar som förväntat."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Visa alltid"</string>
<string name="smv_application" msgid="3307209192155442829">"Appen <xliff:g id="APPLICATION">%1$s</xliff:g> (processen <xliff:g id="PROCESS">%2$s</xliff:g>) har brutit mot sin egen StrictMode-policy."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Surfplatta"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Mobil"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Hörlurar"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dockningsstationens högtalare"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Hörlurar"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-ljud"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Trådlös skärm"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 8bda7f8..c3ee444 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -978,6 +978,10 @@
<string name="dial" msgid="4204975095406423102">"Simu"</string>
<string name="map" msgid="6068210738233985748">"Ramani"</string>
<string name="browse" msgid="6993590095938149861">"Kivinjari"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nafasi ya kuhifadhi inakaribia kujaa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Hifadhi haitoshi kwa ajili ya mfumo. Hakikisha una MB 250 za nafasi ya hifadhi isiyotumika na uanzishe upya."</string>
@@ -987,6 +991,7 @@
<string name="cancel" msgid="6442560571259935130">"Ghairi"</string>
<string name="yes" msgid="5362982303337969312">"Sawa"</string>
<string name="no" msgid="5141531044935541497">"Ghairi"</string>
+ <string name="close" msgid="2318214661230355730">"FUNGA"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Zingatia"</string>
<string name="loading" msgid="7933681260296021180">"Inapakia…"</string>
<string name="capital_on" msgid="1544682755514494298">"Washa"</string>
@@ -1043,6 +1048,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Kipimo"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Onyesha kila wakati"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Wezesha tena hii katika mipangilio ya Mfumo > Programu > iliyopakuliwa."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Programu haifanyi kazi"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Huenda <xliff:g id="APP_NAME">%1$s</xliff:g> inatumia hifadhi nyingi mno."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> haiwezi kutumia mipangilio ya sasa ya ukubwa wa Skrini na huenda isifanye kazi vizuri."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Onyesha kila wakati"</string>
<string name="smv_application" msgid="3307209192155442829">"Programu <xliff:g id="APPLICATION">%1$s</xliff:g> (utaratibu <xliff:g id="PROCESS">%2$s</xliff:g>) imeenda kinyume na sera yake ya StrictMode."</string>
@@ -1418,9 +1425,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Kompyuta kibao"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Runinga"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Simu"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Vipokeasauti"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Vipasa sauti vya kituo"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Vipokeasauti"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Mfumo"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Sauti ya Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Uonyeshaji usiotumia waya"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 9405608..cd2a566 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"ஃபோன்"</string>
<string name="map" msgid="6068210738233985748">"வரைபடம்"</string>
<string name="browse" msgid="6993590095938149861">"உலாவி"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"சேமிப்பிடம் குறைகிறது"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"சில அமைப்பு செயல்பாடுகள் வேலை செய்யாமல் போகலாம்"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"முறைமையில் போதுமான சேமிப்பகம் இல்லை. 250மெ.பை. அளவு காலி இடவசதி இருப்பதை உறுதிசெய்து மீண்டும் தொடங்கவும்."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"ரத்துசெய்"</string>
<string name="yes" msgid="5362982303337969312">"சரி"</string>
<string name="no" msgid="5141531044935541497">"ரத்துசெய்"</string>
+ <string name="close" msgid="2318214661230355730">"மூடு"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"கவனத்திற்கு"</string>
<string name="loading" msgid="7933681260296021180">"ஏற்றுகிறது..."</string>
<string name="capital_on" msgid="1544682755514494298">"ஆன்"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"அளவு"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"எப்போதும் காட்டு"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"சிஸ்டம் அமைப்பு > பயன்பாடுகள் > பதிவிறக்கம் என்பதில் இதை மீண்டும் இயக்கவும்."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"பயன்பாடு செயல்படவில்லை"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> பயன்பாடு, அதிகளவு நினைவகத்தைப் பயன்படுத்தக்கூடும்."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"தற்போதைய திரை அளவு அமைப்பை <xliff:g id="APP_NAME">%1$s</xliff:g> ஆதரிக்காததால், அது வழக்கத்திற்கு மாறாகச் செயல்படக்கூடும்."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"எப்போதும் காட்டு"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> பயன்பாடு (செயல்முறை <xliff:g id="PROCESS">%2$s</xliff:g>), தனது சுய-செயலாக்க StrictMode கொள்கையை மீறியது."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"டேப்லெட்"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"டிவி"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"ஃபோன்"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ஹெட்ஃபோன்கள்"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"மொபைல் வைக்கும் கருவியின் ஸ்பீக்கர்கள்"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ஹெட்ஃபோன்கள்"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"சிஸ்டம்"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"புளூடூத் ஆடியோ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"வயர்லெஸ் காட்சி"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index bd984c5..90981e7 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"ఫోన్"</string>
<string name="map" msgid="6068210738233985748">"మ్యాప్స్"</string>
<string name="browse" msgid="6993590095938149861">"బ్రౌజర్"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"నిల్వ ఖాళీ అయిపోతోంది"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"కొన్ని సిస్టమ్ కార్యాచరణలు పని చేయకపోవచ్చు"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"సిస్టమ్ కోసం తగినంత నిల్వ లేదు. మీకు 250MB ఖాళీ స్థలం ఉందని నిర్ధారించుకుని, పునఃప్రారంభించండి."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"రద్దు చేయి"</string>
<string name="yes" msgid="5362982303337969312">"సరే"</string>
<string name="no" msgid="5141531044935541497">"రద్దు చేయి"</string>
+ <string name="close" msgid="2318214661230355730">"మూసివేయండి"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"గమనిక"</string>
<string name="loading" msgid="7933681260296021180">"లోడ్ చేస్తోంది…"</string>
<string name="capital_on" msgid="1544682755514494298">"ఆన్లో ఉంది"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"ప్రమాణం"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"ఎల్లప్పుడూ చూపు"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"సిస్టమ్ సెట్టింగ్లు > అనువర్తనాలు > డౌన్లోడ్ చేసినవిలో దీన్ని పునఃప్రారంభించండి."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"యాప్ ప్రతిస్పందించలేదు"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> చాలా ఎక్కువ మెమరీని ఉపయోగించుకోవచ్చు."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రస్తుత ప్రదర్శన పరిమాణ సెట్టింగ్కు మద్దతు ఇవ్వదు, దీని వలన ఊహించని సమస్యలు తలెత్తవచ్చు."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"ఎల్లప్పుడూ చూపు"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> యాప్ (<xliff:g id="PROCESS">%2$s</xliff:g> ప్రాసెస్) అది స్వయంగా అమలు చేసే ఖచ్చితమైన మోడ్ విధానాన్ని ఉల్లంఘించింది."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"టాబ్లెట్"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"టీవీ"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"ఫోన్"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"హెడ్ఫోన్లు"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"డాక్ స్పీకర్లు"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"హెడ్ఫోన్లు"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"సిస్టమ్"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"బ్లూటూత్ ఆడియో"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"వైర్లెస్ డిస్ప్లే"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 2653dea..310bd95 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"โทรศัพท์"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"เบราว์เซอร์"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"พื้นที่จัดเก็บเหลือน้อย"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"บางฟังก์ชันระบบอาจไม่ทำงาน"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"พื้นที่เก็บข้อมูลไม่เพียงพอสำหรับระบบ โปรดตรวจสอบว่าคุณมีพื้นที่ว่าง 250 MB แล้วรีสตาร์ท"</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"ยกเลิก"</string>
<string name="yes" msgid="5362982303337969312">"ตกลง"</string>
<string name="no" msgid="5141531044935541497">"ยกเลิก"</string>
+ <string name="close" msgid="2318214661230355730">"ปิด"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"โปรดทราบ"</string>
<string name="loading" msgid="7933681260296021180">"กำลังโหลด..."</string>
<string name="capital_on" msgid="1544682755514494298">"เปิด"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"สเกล"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"แสดงเสมอ"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"เปิดใช้งานอีกครั้งในการตั้งค่าระบบ > แอปพลิเคชัน > ดาวน์โหลด"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"แอปไม่ตอบสนอง"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> อาจใช้หน่วยความจำมากเกินไป"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> ไม่สนับสนุนการตั้งค่าขนาดการแสดงผลปัจจุบันและอาจแสดงผลผิดปกติ"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"แสดงเสมอ"</string>
<string name="smv_application" msgid="3307209192155442829">"แอปพลิเคชัน <xliff:g id="APPLICATION">%1$s</xliff:g> (กระบวนการ <xliff:g id="PROCESS">%2$s</xliff:g>) ละเมิดนโยบาย StrictMode ที่บังคับใช้ด้วยตัวเอง"</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"แท็บเล็ต"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"ทีวี"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"โทรศัพท์"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"หูฟัง"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"ลำโพงแท่นชาร์จ"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"หูฟัง"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"ระบบ"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"เสียงบลูทูธ"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"การแสดงผลแบบไร้สาย"</string>
@@ -1631,7 +1639,7 @@
<string name="package_updated_device_owner" msgid="1847154566357862089">"อัปเดตโดยผู้ดูแลระบบ"</string>
<string name="package_deleted_device_owner" msgid="2307122077550236438">"ลบโดยผู้ดูแลระบบ"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"เพื่อช่วยยืดอายุการใช้งานแบตเตอรี่ โหมดประหยัดแบตเตอรี่จะลดการทำงานของอุปกรณ์และจำกัดการสั่น บริการตำแหน่ง และข้อมูลแบ็กกราวด์ส่วนใหญ่ สำหรับอีเมล การรับส่งข้อความ และแอปอื่นๆ ที่ใช้การซิงค์จะไม่อัปเดตหากคุณไม่เปิดขึ้นมา\n\nโหมดประหยัดแบตเตอรี่จะปิดโดยอัตโนมัติขณะชาร์จอุปกรณ์"</string>
- <string name="data_saver_description" msgid="6015391409098303235">"เพื่อช่วยลดปริมาณการใช้อินเทอร์เน็ต โปรแกรมประหยัดอินเทอร์เน็ตจะช่วยป้องกันไม่ให้แอปบางส่วนส่งหรือรับข้อมูลเครือข่ายมือถือในพื้นหลัง แอปที่คุณกำลังใช้งานสามารถเข้าถึงข้อมูลเครือข่ายมือถือได้ แต่อาจไม่บ่อยเท่าเดิม ตัวอย่างเช่น ภาพต่างๆ จะไม่แสดงจนกว่าคุณจะแตะที่ภาพเหล่านั้น"</string>
+ <string name="data_saver_description" msgid="6015391409098303235">"เพื่อช่วยลดปริมาณการใช้อินเทอร์เน็ต โปรแกรมประหยัดอินเทอร์เน็ตจะช่วยป้องกันไม่ให้บางแอปส่งหรือรับข้อมูลเครือข่ายมือถือในการทำงานเบื้องหลัง แอปที่คุณกำลังใช้งานสามารถเข้าถึงข้อมูลเครือข่ายมือถือได้ แต่อาจไม่บ่อยเท่าเดิม ตัวอย่างเช่น ภาพต่างๆ จะไม่แสดงจนกว่าคุณจะแตะที่ภาพเหล่านั้น"</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"เปิดการประหยัดอินเทอร์เน็ตไหม"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"เปิด"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index da41b9e..50d09a0 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telepono"</string>
<string name="map" msgid="6068210738233985748">"Mga Mapa"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nauubusan na ang puwang ng storage"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Maaaring hindi gumana nang tama ang ilang paggana ng system"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Walang sapat na storage para sa system. Tiyaking mayroon kang 250MB na libreng espasyo at i-restart."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Kanselahin"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Kanselahin"</string>
+ <string name="close" msgid="2318214661230355730">"ISARA"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Bigyang pansin"</string>
<string name="loading" msgid="7933681260296021180">"Naglo-load…"</string>
<string name="capital_on" msgid="1544682755514494298">"I-ON"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Sukat"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Palaging ipakita"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Muling paganahin ito sa mga setting ng System > Apps > Na-download."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Hindi tumutugon ang app"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Maaaring gumagamit ang <xliff:g id="APP_NAME">%1$s</xliff:g> ng masyadong maraming memory."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"Hindi sinusuportahan ng <xliff:g id="APP_NAME">%1$s</xliff:g> ang kasalukuyang setting ng laki ng Display at maaaring may mangyaring hindi inaasahan."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Palaging ipakita"</string>
<string name="smv_application" msgid="3307209192155442829">"Ang app na <xliff:g id="APPLICATION">%1$s</xliff:g> (prosesong <xliff:g id="PROCESS">%2$s</xliff:g>) ay lumabag sa sarili nitong ipinapatupad na patakarang StrictMode."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telepono"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Mga Headphone"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Mga speaker ng dock"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Mga Headphone"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio sa Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Wireless display"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 41a3eae..9bead0c 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Haritalar"</string>
<string name="browse" msgid="6993590095938149861">"Tarayıcı"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Depolama alanı bitiyor"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bazı sistem işlevleri çalışmayabilir"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistem için yeterli depolama alanı yok. 250 MB boş alanınızın bulunduğundan emin olun ve yeniden başlatın."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"İptal"</string>
<string name="yes" msgid="5362982303337969312">"Tamam"</string>
<string name="no" msgid="5141531044935541497">"İptal"</string>
+ <string name="close" msgid="2318214661230355730">"KAPAT"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Dikkat"</string>
<string name="loading" msgid="7933681260296021180">"Yükleniyor..."</string>
<string name="capital_on" msgid="1544682755514494298">"AÇIK"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Ölçek"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Her zaman göster"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Bunu Sistem ayarları > Uygulamalar > İndirilenler bölümünden yeniden etkinleştirin."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Uygulama yanıt vermiyor"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> çok fazla bellek kullanıyor olabilir."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> geçerli Ekran boyutu ayarını desteklemiyor ve beklenmedik bir şekilde davranabilir."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Her zaman göster"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması (<xliff:g id="PROCESS">%2$s</xliff:g> işlemi) kendiliğinden uyguladığı StrictMode politikasını ihlal etti."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Kulaklıklar"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Yuva hoparlörleri"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Kulaklıklar"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth ses"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Kablosuz ekran"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index f0e2c1d..cb2f5b4 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1020,6 +1020,10 @@
<string name="dial" msgid="4204975095406423102">"Телефонувати"</string>
<string name="map" msgid="6068210738233985748">"Карти"</string>
<string name="browse" msgid="6993590095938149861">"Веб-переглядач"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Закінчується пам’ять"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Деякі системні функції можуть не працювати"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Недостатньо місця для системи. Переконайтесь, що на пристрої є 250 МБ вільного місця, і повторіть спробу."</string>
@@ -1029,6 +1033,7 @@
<string name="cancel" msgid="6442560571259935130">"Скасувати"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Скасувати"</string>
+ <string name="close" msgid="2318214661230355730">"ЗАКРИТИ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Увага"</string>
<string name="loading" msgid="7933681260296021180">"Завантаження..."</string>
<string name="capital_on" msgid="1544682755514494298">"УВІМК"</string>
@@ -1085,6 +1090,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Масштаб"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Завжди показувати"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Знову ввімкнути це в меню Налаштування системи > Програми > Завантажені."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Додаток не відповідає"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"Можливо, додаток <xliff:g id="APP_NAME">%1$s</xliff:g> використовує забагато пам’яті."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> не підтримує поточне налаштування розміру екрана та може працювати неналежним чином."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Завжди показувати"</string>
<string name="smv_application" msgid="3307209192155442829">"Програма <xliff:g id="APPLICATION">%1$s</xliff:g> (процес <xliff:g id="PROCESS">%2$s</xliff:g>) порушила свою самозастосовну політику StrictMode."</string>
@@ -1466,9 +1473,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Планшетний ПК"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Телевізор"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Телефон"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Навушники"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Динаміки док-станції"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Навушники"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Система"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Аудіо Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Бездротовий екран"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 2d9717d..ab69fc3 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"فون کریں"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"براؤزر"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"اسٹوریج کی جگہ ختم ہو رہی ہے"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ممکن ہے سسٹم کے کچھ فنکشنز کام نہ کریں"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"سسٹم کیلئے کافی اسٹوریج نہیں ہے۔ اس بات کو یقینی بنائیں کہ آپ کے پاس 250MB خالی جگہ ہے اور دوبارہ شروع کریں۔"</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"منسوخ کریں"</string>
<string name="yes" msgid="5362982303337969312">"ٹھیک ہے"</string>
<string name="no" msgid="5141531044935541497">"منسوخ کریں"</string>
+ <string name="close" msgid="2318214661230355730">"بند کریں"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"توجہ دیں"</string>
<string name="loading" msgid="7933681260296021180">"لوڈ ہو رہا ہے…"</string>
<string name="capital_on" msgid="1544682755514494298">"آن"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"پیمانہ"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"ہمیشہ دکھائیں"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"سسٹم ترتیبات > ایپس > ڈاؤن لوڈ کردہ میں اسے دوبارہ فعال کریں۔"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"ایپ جواب نہیں دے رہی ہے"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> کافی زیادہ میموری استعمال کر سکتی ہے۔"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> میں موجودہ ڈسپلے سائز ترتیبات کی معاونت نہیں ہے اور ہو سکتا ہے غیر متوقع طریقے سے کام کرے۔"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"ہمیشہ دکھائیں"</string>
<string name="smv_application" msgid="3307209192155442829">"ایپ <xliff:g id="APPLICATION">%1$s</xliff:g> (کارروائی <xliff:g id="PROCESS">%2$s</xliff:g>) نے خود نافذ کی گئی StrictMode پالیسی کی خلاف ورزی کی ہے۔"</string>
@@ -1303,9 +1310,9 @@
<string name="submit" msgid="1602335572089911941">"جمع کرائیں"</string>
<string name="car_mode_disable_notification_title" msgid="3164768212003864316">"کار وضع فعال ہے"</string>
<string name="car_mode_disable_notification_message" msgid="6301524980144350051">"کار موڈ سے خارج ہونے کیلئے تھپتھپائیں۔"</string>
- <string name="tethered_notification_title" msgid="3146694234398202601">"ٹیتھرنگ یا ہاٹ اسپاٹ فعال"</string>
+ <string name="tethered_notification_title" msgid="3146694234398202601">"ٹیدرنگ یا ہاٹ اسپاٹ فعال"</string>
<string name="tethered_notification_message" msgid="2113628520792055377">"سیٹ اپ کرنے کیلئے تھپتھپائیں۔"</string>
- <string name="disable_tether_notification_title" msgid="7526977944111313195">"ربط بنانا غیر فعال ہے"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"ٹیدرنگ غیر فعال ہے"</string>
<string name="disable_tether_notification_message" msgid="2913366428516852495">"تفصیلات کے لئے اپنے منتظم سے رابطہ کریں"</string>
<string name="back_button_label" msgid="2300470004503343439">"واپس جائیں"</string>
<string name="next_button_label" msgid="1080555104677992408">"اگلا"</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ٹیبلیٹ"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"فون"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ہیڈ فونز"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"ڈاک اسپیکرز"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"ہیڈ فونز"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"سسٹم"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"بلوٹوتھ آڈیو"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"وائرلیس ڈسپلے"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index f639e80..f402e26 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Xaritalar"</string>
<string name="browse" msgid="6993590095938149861">"Brauzer"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Xotirada bo‘sh joy tugamoqda"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Ba‘zi tizim funksiyalari ishlamasligi mumkin"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Tizim uchun xotirada joy yetarli emas. Avval 250 megabayt joy bo‘shatib, keyin qurilmani o‘chirib yoqing."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Bekor qilish"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Bekor qilish"</string>
+ <string name="close" msgid="2318214661230355730">"YOPISH"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Diqqat"</string>
<string name="loading" msgid="7933681260296021180">"Yuklanmoqda…"</string>
<string name="capital_on" msgid="1544682755514494298">"I"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Masshtab"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Doimo ko‘rsatish"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Uni Tizim sozlamalari > Ilovalar > Yuklab olingan menyusidan qayta yoqing."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Ilova javob bermayapti"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> ortiqcha xotira ishlatmoqda."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"“<xliff:g id="APP_NAME">%1$s</xliff:g>” ilovasi joriy ekran o‘lchami sozlamalariga mos kelmasligi va noto‘g‘ri ishlashi mumkin."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Har doim ko‘rsatilsin"</string>
<string name="smv_application" msgid="3307209192155442829">"“<xliff:g id="APPLICATION">%1$s</xliff:g>” ilovasi (jarayaon: <xliff:g id="PROCESS">%2$s</xliff:g>) o‘zining StrictMode qoidasini buzdi."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Planshet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Quloq karnaychalari"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Taglik karnaylar"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Quloq karnaychalari"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Tizim"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Ovozni Bluetooth orqali chiqarish"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Simsiz monitor"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 58e0e34..9a057cf 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Điện thoại"</string>
<string name="map" msgid="6068210738233985748">"Bản đồ"</string>
<string name="browse" msgid="6993590095938149861">"Trình duyệt"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Sắp hết dung lượng lưu trữ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Một số chức năng hệ thống có thể không hoạt động"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Không đủ bộ nhớ cho hệ thống. Đảm bảo bạn có 250 MB dung lượng trống và khởi động lại."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Hủy"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Hủy"</string>
+ <string name="close" msgid="2318214661230355730">"ĐÓNG"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Chú ý"</string>
<string name="loading" msgid="7933681260296021180">"Đang tải…"</string>
<string name="capital_on" msgid="1544682755514494298">"BẬT"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Tỷ lệ"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Luôn hiển thị"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Bật lại chế độ này trong cài đặt Hệ thống > Ứng dụng > Đã tải xuống."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Ứng dụng không phản hồi"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> có thể đang sử dụng quá nhiều bộ nhớ."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> không hỗ trợ cài đặt kích thước Màn hình hiện tại và có thể hoạt động không như mong đợi."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Luôn hiển thị"</string>
<string name="smv_application" msgid="3307209192155442829">"Ứng dụng <xliff:g id="APPLICATION">%1$s</xliff:g> (quá trình <xliff:g id="PROCESS">%2$s</xliff:g>) đã vi phạm chính sách StrictMode tự thi hành của mình."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Máy tính bảng"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Điện thoại"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Tai nghe"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Loa đế"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Tai nghe"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Hệ thống"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Âm thanh Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Hiển thị không dây"</string>
@@ -1630,7 +1638,7 @@
<string name="package_installed_device_owner" msgid="6875717669960212648">"Do quản trị viên của bạn cài đặt"</string>
<string name="package_updated_device_owner" msgid="1847154566357862089">"Do quản trị viên của bạn cập nhật"</string>
<string name="package_deleted_device_owner" msgid="2307122077550236438">"Do quản trị viên của bạn xóa"</string>
- <string name="battery_saver_description" msgid="1960431123816253034">"Để giúp tăng tuổi thọ pin, trình tiết kiệm pin sẽ giảm hiệu suất thiết bị của bạn và hạn chế rung, dịch vụ vị trí và hầu hết dữ liệu nền. Email, nhắn tin và các ứng dụng khác dựa trên đồng bộ hóa có thể không cập nhật nếu bạn không mở chúng.\n\nTrình tiết kiệm pin tự động tắt khi thiết bị của bạn đang sạc."</string>
+ <string name="battery_saver_description" msgid="1960431123816253034">"Để giúp tăng thời lượng pin, trình tiết kiệm pin sẽ giảm hiệu suất thiết bị và hạn chế rung, dịch vụ vị trí và hầu hết dữ liệu nền. Ứng dụng email, nhắn tin và các ứng dụng khác dựa trên đồng bộ hóa có thể không cập nhật nếu bạn không mở.\n\nTrình tiết kiệm pin tự động tắt khi thiết bị của bạn đang sạc."</string>
<string name="data_saver_description" msgid="6015391409098303235">"Để giúp giảm mức sử dụng dữ liệu, Trình tiết kiệm dữ liệu chặn một số ứng dụng gửi hoặc nhận dữ liệu trong nền. Ứng dụng mà bạn hiện sử dụng có thể truy cập dữ liệu nhưng có thể thực hiện việc đó ít thường xuyên hơn. Ví dụ: hình ảnh sẽ không hiển thị cho đến khi bạn nhấn vào hình ảnh đó."</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"Bật Trình tiết kiệm dữ liệu?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"Bật"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index e87f76c..962b05f 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"电话"</string>
<string name="map" msgid="6068210738233985748">"地图"</string>
<string name="browse" msgid="6993590095938149861">"浏览器"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"存储空间不足"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"某些系统功能可能无法正常使用"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系统存储空间不足。请确保您有250MB的可用空间,然后重新启动。"</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="yes" msgid="5362982303337969312">"确定"</string>
<string name="no" msgid="5141531044935541497">"取消"</string>
+ <string name="close" msgid="2318214661230355730">"关闭"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"注意"</string>
<string name="loading" msgid="7933681260296021180">"正在加载..."</string>
<string name="capital_on" msgid="1544682755514494298">"开启"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"缩放"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"始终显示"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"在“系统设置”>“应用”>“已下载”中重新启用此模式。"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"应用没有响应"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g>可能占用了过多内存。"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g>不支持当前的显示大小设置,因此可能无法正常显示。"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"一律显示"</string>
<string name="smv_application" msgid="3307209192155442829">"“<xliff:g id="APPLICATION">%1$s</xliff:g>”应用(<xliff:g id="PROCESS">%2$s</xliff:g> 进程)违反了自我强制执行的严格模式 (StrictMode) 政策。"</string>
@@ -1137,7 +1144,7 @@
<string name="wifi_p2p_enabled_notification_message" msgid="8064677407830620023">"点按即可查看相关设置"</string>
<string name="accept" msgid="1645267259272829559">"接受"</string>
<string name="decline" msgid="2112225451706137894">"拒绝"</string>
- <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"邀请已发送"</string>
+ <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"已发出邀请"</string>
<string name="wifi_p2p_invitation_to_connect_title" msgid="4958803948658533637">"连接邀请"</string>
<string name="wifi_p2p_from_message" msgid="570389174731951769">"发件人:"</string>
<string name="wifi_p2p_to_message" msgid="248968974522044099">"收件人:"</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"平板电脑"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"电视"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"手机"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"耳机"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"基座扬声器"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"耳机"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"系统"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"蓝牙音频"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"无线显示"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 56ea6a0..4b5a4ec 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"撥打電話"</string>
<string name="map" msgid="6068210738233985748">"地圖"</string>
<string name="browse" msgid="6993590095938149861">"瀏覽器"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"儲存空間即將用盡"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"部分系統功能可能無法運作"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系統儲存空間不足。請確認裝置有 250 MB 的可用空間,然後重新啟動。"</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="yes" msgid="5362982303337969312">"確定"</string>
<string name="no" msgid="5141531044935541497">"取消"</string>
+ <string name="close" msgid="2318214661230355730">"關閉"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"注意"</string>
<string name="loading" msgid="7933681260296021180">"正在載入..."</string>
<string name="capital_on" msgid="1544682755514494298">"開啟"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"比例"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"永遠顯示"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"前往 [系統設定] > [應用程式] > [下載] 重新啟用這個模式。"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"應用程式沒有回應"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」可能佔用大量記憶體。"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」不支援目前的「螢幕」尺寸設定,畫面可能無法如預期顯示。"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"永遠顯示"</string>
<string name="smv_application" msgid="3307209192155442829">"應用程式 <xliff:g id="APPLICATION">%1$s</xliff:g> (處理程序 <xliff:g id="PROCESS">%2$s</xliff:g>) 已違反其自行強制實施的嚴格模式 (StrictMode) 政策。"</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"平板電腦"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"電視"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"手機"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"耳機"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"插座喇叭"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"耳機"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"系統"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"藍牙音頻"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"無線螢幕分享"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 17c80b5..5166b87 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"電話"</string>
<string name="map" msgid="6068210738233985748">"地圖"</string>
<string name="browse" msgid="6993590095938149861">"瀏覽器"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"儲存空間即將用盡"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"部分系統功能可能無法運作"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系統儲存空間不足。請確定你已釋出 250MB 的可用空間,然後重新啟動。"</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="yes" msgid="5362982303337969312">"確定"</string>
<string name="no" msgid="5141531044935541497">"取消"</string>
+ <string name="close" msgid="2318214661230355730">"關閉"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"注意"</string>
<string name="loading" msgid="7933681260296021180">"載入中…"</string>
<string name="capital_on" msgid="1544682755514494298">"開啟"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"比例"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"一律顯示"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"前往 [系統設定] > [應用程式] > [下載] 重新啟用這個模式。"</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"應用程式沒有回應"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」可能使用了過多記憶體。"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」不支援目前的顯示大小設定,可能會發生非預期的行為。"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"一律顯示"</string>
<string name="smv_application" msgid="3307209192155442829">"應用程式 <xliff:g id="APPLICATION">%1$s</xliff:g> (處理程序 <xliff:g id="PROCESS">%2$s</xliff:g>) 已違反其自行強制實施的嚴格模式 (StrictMode) 政策。"</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"平板電腦"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"電視"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"手機"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"耳機"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"座架喇叭"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"耳機"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"系統"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"藍牙音訊"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"無線螢幕分享"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 88fb1d7..b7a6ae4 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -980,6 +980,10 @@
<string name="dial" msgid="4204975095406423102">"Ifoni"</string>
<string name="map" msgid="6068210738233985748">"Amamephu"</string>
<string name="browse" msgid="6993590095938149861">"Isiphequluli"</string>
+ <!-- no translation found for sms (8250353543787396737) -->
+ <skip />
+ <!-- no translation found for add_contact (7990645816259405444) -->
+ <skip />
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Isikhala sokulondoloza siyaphela"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Eminye imisebenzi yohlelo ingahle ingasebenzi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Akusona isitoreji esanele sesistimu. Qiniseka ukuthi unesikhala esikhululekile esingu-250MB uphinde uqalise kabusha."</string>
@@ -989,6 +993,7 @@
<string name="cancel" msgid="6442560571259935130">"Khansela"</string>
<string name="yes" msgid="5362982303337969312">"KULUNGILE"</string>
<string name="no" msgid="5141531044935541497">"Khansela"</string>
+ <string name="close" msgid="2318214661230355730">"VALA"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Qaphela"</string>
<string name="loading" msgid="7933681260296021180">"Iyalayisha…"</string>
<string name="capital_on" msgid="1544682755514494298">"VULIWE"</string>
@@ -1045,6 +1050,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Isilinganisi"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Bonisa njalo"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Yenza kuphinde kusebenze kuzilungiselelo Zesistimue > Izinhlelo zokusebenza > Okulayishiwe."</string>
+ <string name="top_app_killed_title" msgid="6814231368167994497">"I-App ayiphenduli"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> ingasebenzisa imemori eningi."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayisekeli isilungiselelo sosayizi sokubonisa samanje futhi ingasebenza ngokungalindelekile."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Bonisa njalo"</string>
<string name="smv_application" msgid="3307209192155442829">"Inqubo <xliff:g id="APPLICATION">%1$s</xliff:g> (yohlelo <xliff:g id="PROCESS">%2$s</xliff:g>) iphule inqubomgomo oziphoqelela yona Yemodi Ebukhali."</string>
@@ -1420,9 +1427,10 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Ithebulethi"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"I-TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Ifoni"</string>
- <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Ama-headphone"</string>
<string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Izipikha ze-Dock"</string>
- <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
+ <string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
+ <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Ama-headphone"</string>
+ <string name="default_audio_route_name_usb" msgid="1234984851352637769">"I-USB"</string>
<string name="default_audio_route_category_name" msgid="3722811174003886946">"Isistimu"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Umsindo we-Bluetooth"</string>
<string name="wireless_display_route_description" msgid="9070346425023979651">"Ukubonisa okungenazintambo"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 61b41239..11cdb76 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2056,11 +2056,11 @@
Corresponds to {@link android.view.Window#setNavigationBarColor(int)}. -->
<attr name="navigationBarColor" format="color" />
- <!-- @hide
- Shows 1dp line of the specified color between the navigation bar and the app content.
+ <!-- Shows a thin line of the specified color between the navigation bar and the app
+ content.
<p>For this to take effect, the window must be drawing the system bar backgrounds with
{@link android.R.attr#windowDrawsSystemBarBackgrounds} and the navigation bar must not
- have been requested to be translucent with
+ have been requested to be translucent with
{@link android.R.attr#windowTranslucentNavigation}. -->
<attr name="navigationBarDividerColor" format="color" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 83bb443..dcb56a2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1127,6 +1127,11 @@
<!-- Is the lock-screen disabled for new users by default -->
<bool name="config_disableLockscreenByDefault">false</bool>
+ <!-- If true, enables verification of the lockscreen credential in the factory reset protection
+ flow. This should be true if gatekeeper / weaver credentials can still be checked after a
+ factory reset. -->
+ <bool name="config_enableCredentialFactoryResetProtection">true</bool>
+
<!-- Control the behavior when the user long presses the home button.
0 - Nothing
1 - Launch all apps intent
@@ -1363,7 +1368,7 @@
<!-- The package of the time zone rules updater application. Expected to be the same
for all Android devices that support APK-based time zone rule updates.
- A package-targeted android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK intent
+ A package-targeted com.android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK intent
will be sent to the updater app if the system server detects an update to the updater or
data app packages.
The package referenced here must have the android.permission.UPDATE_TIME_ZONE_RULES
@@ -1374,7 +1379,7 @@
<!-- The package of the time zone rules data application. Expected to be configured
by OEMs to reference their own priv-app APK package.
- A package-targeted android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK intent
+ A package-targeted com.android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK intent
will be sent to the updater app if the system server detects an update to the updater or
data app packages.
[This is only used if config_enableUpdateableTimeZoneRules and
@@ -3035,7 +3040,7 @@
<bool name="config_handleVolumeKeysInWindowManager">false</bool>
<!-- Volume level of in-call notification tone playback [0..1] -->
- <item name="config_inCallNotificationVolume" format="float" type="dimen">.25</item>
+ <item name="config_inCallNotificationVolume" format="float" type="dimen">.10</item>
<!-- URI for in call notification sound -->
<string translatable="false" name="config_inCallNotificationSound">/system/media/audio/ui/InCallNotification.ogg</string>
@@ -3087,4 +3092,8 @@
booted. -->
<integer name="config_stableDeviceDisplayWidth">-1</integer>
<integer name="config_stableDeviceDisplayHeight">-1</integer>
+
+ <!-- Decide whether to display 'No service' on status bar instead of 'Emergency calls only'
+ when SIM is unready. -->
+ <bool name="config_display_no_service_when_sim_unready">false</bool>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 2c64789..b3d0053 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -346,11 +346,6 @@
<dimen name="notification_text_size">14sp</dimen>
<!-- Size of notification text titles (see TextAppearance.StatusBar.EventContent.Title) -->
<dimen name="notification_title_text_size">14sp</dimen>
-
- <!-- Size of notification text (see TextAppearance.StatusBar.EventContent) when colorized -->
- <dimen name="notification_text_size_colorized">16sp</dimen>
- <!-- Size of notification text titles (see TextAppearance.StatusBar.EventContent.Title) when colorized -->
- <dimen name="notification_title_text_size_colorized">20sp</dimen>
<!-- Size of smaller notification text (see TextAppearance.StatusBar.EventContent.Line2, Info, Time) -->
<dimen name="notification_subtext_size">12sp</dimen>
@@ -525,6 +520,12 @@
<dimen name="floating_toolbar_vertical_margin">8dp</dimen>
<dimen name="content_rect_bottom_clip_allowance">20dp</dimen>
+ <!-- Magnifier dimensions -->
+ <dimen name="magnifier_width">200dp</dimen>
+ <dimen name="magnifier_height">48dp</dimen>
+ <dimen name="magnifier_elevation">2dp</dimen>
+ <dimen name="magnifier_offset">42dp</dimen>
+
<dimen name="chooser_grid_padding">0dp</dimen>
<!-- Spacing around the background change frome service to non-service -->
<dimen name="chooser_service_spacing">8dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9b2f185..108ac1c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2842,21 +2842,13 @@
=============================================================== -->
<eat-comment />
- <public-group type="attr" first-id="0x01010569">
- <public name="showWhenLocked"/>
- <public name="turnScreenOn"/>
- <public name="classLoader" />
- </public-group>
+ <public type="attr" name="showWhenLocked" id="0x01010569" />
+ <public type="attr" name="turnScreenOn" id="0x0101056a" />
+ <public type="attr" name="classLoader" id="0x0101056b" />
+ <public type="attr" name="windowLightNavigationBar" id="0x0101056c" />
+ <public type="attr" name="navigationBarDividerColor" id="0x0101056d" />
- <public-group type="style" first-id="0x010302e0">
- </public-group>
-
- <public-group type="id" first-id="0x01020044">
- </public-group>
-
- <public-group type="string" first-id="0x0104001a">
- <public name="autofill"/>
- </public-group>
+ <public type="string" name="autofill" id="0x0104001a"/>
<!-- ===============================================================
Resources added in version P of the platform
@@ -2875,8 +2867,16 @@
=============================================================== -->
<eat-comment />
- <public-group type="attr" first-id="0x0101056c">
- <public name="windowLightNavigationBar" />
+ <public-group type="attr" first-id="0x0101056e">
+ </public-group>
+
+ <public-group type="style" first-id="0x010302e0">
+ </public-group>
+
+ <public-group type="id" first-id="0x01020044">
+ </public-group>
+
+ <public-group type="string" first-id="0x0104001b">
</public-group>
<!-- ===============================================================
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7416113..085f8dd 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -340,18 +340,19 @@
<!-- Work profile deleted notification--> <skip />
<!-- Shows up in the notification's title when the system deletes the work profile. [CHAR LIMIT=NONE] -->
<string name="work_profile_deleted">Work profile deleted</string>
- <!-- Content text for a notification. The Title of the notification is "work_profile_deleted",
- i.e. "Work profile deleted". This says that the profile is deleted by the system as a result of
- the current profile owner gone missing. [CHAR LIMIT=100]-->
+ <!-- Content text for a notification. The Title of the notification is "Work profile deleted".
+ This says that the profile is deleted by the system as a result of the current profile owner gone missing. [CHAR LIMIT=100]-->
<string name="work_profile_deleted_description">Work profile deleted due to missing admin app</string>
- <!-- Content text for an expanded notification. The Title of the notification is "work_profile_deleted",
- i.e. "Work profile deleted". This further explains that the profile is deleted by the system
- as a result of the current profile admin gone missing. [CHAR LIMIT=NONE]-->
+ <!-- Content text for an expanded notification. The Title of the notification is "Work profile deleted".
+ This further explains that the profile is deleted by the system as a result of the current profile admin gone missing. [CHAR LIMIT=NONE]-->
<string name="work_profile_deleted_details">The work profile admin app is either missing or corrupted.
As a result, your work profile and related data have been deleted. Contact your admin for assistance.</string>
- <!-- Content text for a notification. The Title of the notification is "work_profile_deleted",
+ <!-- Content text for a notification. The Title of the notification is "Work profile deleted",
This indicates that a work profile has been deleted. [CHAR LIMIT=NONE]-->
<string name="work_profile_deleted_description_dpm_wipe">Your work profile is no longer available on this device</string>
+ <!-- Content text for a notification. The Title of the notification is "Work profile deleted",
+ This indicates that a work profile has been deleted because the maximum failed password attempts as been reached. [CHAR LIMIT=NONE]-->
+ <string name="work_profile_deleted_reason_maximum_password_failure">Too many password attempts</string>
<!-- Content title for a notification. This notification indicates that the device is managed
and network logging was activated by a device owner. [CHAR LIMIT=NONE]-->
@@ -2685,6 +2686,12 @@
<!-- Label for item in the text selection menu to trigger a Browser app [CHAR LIMIT=20] -->
<string name="browse">Browser</string>
+ <!-- Label for item in the text selection menu to trigger an SMS app [CHAR LIMIT=20] -->
+ <string name="sms">SMS</string>
+
+ <!-- Label for item in the text selection menu to trigger adding a contact [CHAR LIMIT=20] -->
+ <string name="add_contact">Contact</string>
+
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the title of that notification. -->
<string name="low_internal_storage_view_title">Storage space running out</string>
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the message of that notification. -->
@@ -2709,6 +2716,8 @@
<string name="yes">OK</string>
<!-- Preference framework strings. -->
<string name="no">Cancel</string>
+ <!-- Preference framework strings. -->
+ <string name="close">CLOSE</string>
<!-- This is the generic "attention" string to be used in attention dialogs. Typically
combined with setIconAttribute(android.R.attr.alertDialogIcon)
(or setIcon(android.R.drawable.ic_dialog_alert) on legacy versions of the platform) -->
@@ -2841,6 +2850,11 @@
<!-- [CHAR LIMIT=200] Compat mode dialog: hint to re-enable compat mode dialog. -->
<string name="screen_compat_mode_hint">Re-enable this in System settings > Apps > Downloaded.</string>
+ <!-- Text of the alert that is displayed when a top application is killed by lmk. -->
+ <string name="top_app_killed_title">App isn\'t responding</string>
+ <!-- Top app killed by lmk dialog message. -->
+ <string name="top_app_killed_message"><xliff:g id="app_name">%1$s</xliff:g> may be using too much memory.</string>
+
<!-- [CHAR LIMIT=200] Unsupported display size dialog: message. Refers to "Display size" setting. -->
<string name="unsupported_display_size_message"><xliff:g id="app_name">%1$s</xliff:g> does not support the current Display size setting and may behave unexpectedly.</string>
<!-- [CHAR LIMIT=50] Unsupported display size dialog: check box label. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c4a45ee..cc74f17 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -516,6 +516,8 @@
<java-symbol type="string" name="dial" />
<java-symbol type="string" name="map" />
<java-symbol type="string" name="browse" />
+ <java-symbol type="string" name="sms" />
+ <java-symbol type="string" name="add_contact" />
<java-symbol type="string" name="textSelectionCABTitle" />
<java-symbol type="string" name="BaMmi" />
<java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
@@ -1153,6 +1155,7 @@
<java-symbol type="string" name="work_profile_deleted_description" />
<java-symbol type="string" name="work_profile_deleted_details" />
<java-symbol type="string" name="work_profile_deleted_description_dpm_wipe" />
+ <java-symbol type="string" name="work_profile_deleted_reason_maximum_password_failure" />
<java-symbol type="string" name="network_logging_notification_title" />
<java-symbol type="string" name="network_logging_notification_text" />
<java-symbol type="string" name="factory_reset_warning" />
@@ -1893,6 +1896,9 @@
<java-symbol type="string" name="anr_application_process" />
<java-symbol type="string" name="anr_process" />
<java-symbol type="string" name="anr_title" />
+ <java-symbol type="string" name="top_app_killed_title" />
+ <java-symbol type="string" name="top_app_killed_message" />
+ <java-symbol type="string" name="close" />
<java-symbol type="string" name="car_mode_disable_notification_message" />
<java-symbol type="string" name="car_mode_disable_notification_title" />
<java-symbol type="string" name="chooser_wallpaper" />
@@ -2469,6 +2475,14 @@
<java-symbol type="drawable" name="ft_avd_tooverflow_animation" />
<java-symbol type="attr" name="floatingToolbarDividerColor" />
+ <!-- Magnifier -->
+ <java-symbol type="id" name="magnifier_image" />
+ <java-symbol type="layout" name="magnifier" />
+ <java-symbol type="dimen" name="magnifier_width" />
+ <java-symbol type="dimen" name="magnifier_height" />
+ <java-symbol type="dimen" name="magnifier_elevation" />
+ <java-symbol type="dimen" name="magnifier_offset" />
+
<java-symbol type="string" name="date_picker_prev_month_button" />
<java-symbol type="string" name="date_picker_next_month_button" />
<java-symbol type="layout" name="date_picker_month_item_material" />
@@ -3027,6 +3041,8 @@
<java-symbol type="string" name="foreground_service_tap_for_details" />
<java-symbol type="string" name="foreground_service_multiple_separator" />
+ <java-symbol type="bool" name="config_enableCredentialFactoryResetProtection" />
+
<!-- ETWS primary messages -->
<java-symbol type="string" name="etws_primary_default_message_earthquake" />
<java-symbol type="string" name="etws_primary_default_message_tsunami" />
@@ -3058,9 +3074,6 @@
<java-symbol type="array" name="config_allowedSystemInstantAppSettings" />
<java-symbol type="array" name="config_allowedSecureInstantAppSettings" />
- <java-symbol type="dimen" name="notification_text_size_colorized" />
- <java-symbol type="dimen" name="notification_title_text_size_colorized" />
-
<java-symbol type="bool" name="config_handleVolumeKeysInWindowManager" />
<java-symbol type="dimen" name="config_inCallNotificationVolume" />
<java-symbol type="string" name="config_inCallNotificationSound" />
@@ -3086,4 +3099,5 @@
<java-symbol type="integer" name="config_stableDeviceDisplayWidth" />
<java-symbol type="integer" name="config_stableDeviceDisplayHeight" />
+ <java-symbol type="bool" name="config_display_no_service_when_sim_unready" />
</resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 9dafa7a..9bea3ee 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1336,6 +1336,7 @@
<!-- Default theme for Settings and activities launched from Settings. -->
<style name="Theme.Material.Settings" parent="Theme.Material.Light.LightStatusBar">
+ <item name="homeAsUpIndicator">@drawable/ic_ab_back_material_settings</item>
<item name="colorPrimary">@color/primary_material_settings_light</item>
<item name="colorPrimaryDark">@color/primary_dark_material_settings_light</item>
<item name="colorSecondary">@color/secondary_material_settings_light</item>
diff --git a/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttf b/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttf
new file mode 100644
index 0000000..cf769ed
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttx b/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttx
new file mode 100644
index 0000000..04d85922
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttx
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.9">
+
+ <GlyphOrder>
+ <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="space"/>
+ <GlyphID id="2" name="R"/>
+ <GlyphID id="3" name="a"/>
+ <GlyphID id="4" name="y"/>
+ </GlyphOrder>
+
+ <head>
+ <!-- Most of this table will be recalculated by the compiler -->
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x26d0d624"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Tue Oct 3 23:00:00 2017"/>
+ <modified value="Tue Oct 3 23:33:15 2017"/>
+ <xMin value="-1500"/>
+ <yMin value="0"/>
+ <xMax value="5000"/>
+ <yMax value="1000"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <indexToLocFormat value="0"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <advanceWidthMax value="1000"/>
+ <minLeftSideBearing value="-1500"/>
+ <minRightSideBearing value="-4000"/>
+ <xMaxExtent value="5000"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ <numberOfHMetrics value="1"/>
+ </hhea>
+
+ <maxp>
+ <!-- Most of this table will be recalculated by the compiler -->
+ <tableVersion value="0x10000"/>
+ <numGlyphs value="5"/>
+ <maxPoints value="4"/>
+ <maxContours value="1"/>
+ <maxCompositePoints value="0"/>
+ <maxCompositeContours value="0"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ <maxComponentDepth value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="121"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="1000" lsb="0"/>
+ <mtx name="R" width="1000" lsb="0"/>
+ <mtx name="a" width="1000" lsb="0"/>
+ <mtx name="space" width="1000" lsb="0"/>
+ <mtx name="y" width="1000" lsb="-1500"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="64" language="0" nGroups="4">
+ <map code="0x20" name="space"/><!-- SPACE -->
+ <map code="0x52" name="R"/><!-- LATIN CAPITAL LETTER R -->
+ <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+ <map code="0x79" name="y"/><!-- LATIN SMALL LETTER Y -->
+ </cmap_format_12>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+
+ <!-- The xMin, yMin, xMax and yMax values
+ will be recalculated by the compiler. -->
+
+ <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+ <TTGlyph name="R" xMin="0" yMin="0" xMax="5000" yMax="1000">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="0" y="1000" on="1"/>
+ <pt x="5000" y="1000" on="1"/>
+ <pt x="5000" y="0" on="1"/>
+ </contour>
+ <instructions/>
+ </TTGlyph>
+
+ <TTGlyph name="a"/><!-- contains no outline data -->
+
+ <TTGlyph name="space"/><!-- contains no outline data -->
+
+ <TTGlyph name="y" xMin="-1500" yMin="0" xMax="1000" yMax="1000">
+ <contour>
+ <pt x="-1500" y="0" on="1"/>
+ <pt x="-1500" y="1000" on="1"/>
+ <pt x="1000" y="1000" on="1"/>
+ <pt x="1000" y="0" on="1"/>
+ </contour>
+ <instructions/>
+ </TTGlyph>
+
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Font for LineBreakingOverhangsTest
+ </namerecord>
+ <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Font for LineBreakingOverhangsTest
+ </namerecord>
+ <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttf b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttf
new file mode 100644
index 0000000..1bad6fe
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttx b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttx
new file mode 100644
index 0000000..0cf0f79
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttx
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="0em"/>
+ <GlyphID id="2" name="1em"/>
+ <GlyphID id="3" name="3em"/>
+ <GlyphID id="4" name="5em"/>
+ <GlyphID id="5" name="7em"/>
+ <GlyphID id="6" name="10em"/>
+ <GlyphID id="7" name="50em"/>
+ <GlyphID id="8" name="100em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="100"/>
+ <created value="Fri Mar 17 07:26:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="50" lsb="0"/>
+ <mtx name="0em" width="0" lsb="0"/>
+ <mtx name="1em" width="100" lsb="0"/>
+ <mtx name="3em" width="300" lsb="0"/>
+ <mtx name="5em" width="500" lsb="0"/>
+ <mtx name="7em" width="700" lsb="0"/>
+ <mtx name="10em" width="1000" lsb="0"/>
+ <mtx name="50em" width="5000" lsb="0"/>
+ <mtx name="100em" width="10000" lsb="0"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_12 format="12" reserved="0" length="6" nGroups="1" platformID="3" platEncID="10" language="0">
+ <map code="0x0020" name="10em" />
+ <map code="0x002e" name="10em" /> <!-- . -->
+ <map code="0x0043" name="100em" /> <!-- C -->
+ <map code="0x0049" name="1em" /> <!-- I -->
+ <map code="0x004c" name="50em" /> <!-- L -->
+ <map code="0x0056" name="5em" /> <!-- V -->
+ <map code="0x0058" name="10em" /> <!-- X -->
+ <map code="0x005f" name="0em" /> <!-- _ -->
+ <map code="0xfffd" name="7em" /> <!-- REPLACEMENT CHAR -->
+ <map code="0x10331" name="10em" />
+ </cmap_format_12>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="0em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="5em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="7em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="10em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="50em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="100em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Font for StaticLayoutLineBreakingTest
+ </namerecord>
+ <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Font for StaticLayoutLineBreakingTest
+ </namerecord>
+ <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/src/android/content/pm/AppCacheTest.java b/core/tests/coretests/src/android/content/pm/AppCacheTest.java
deleted file mode 100644
index a5fdef6..0000000
--- a/core/tests/coretests/src/android/content/pm/AppCacheTest.java
+++ /dev/null
@@ -1,725 +0,0 @@
-/*
- * Copyright (C) 2006 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.content.pm;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.StatFs;
-import android.os.UserHandle;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.MediumTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.filters.Suppress;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public class AppCacheTest extends AndroidTestCase {
- private static final boolean localLOGV = false;
- public static final String TAG="AppCacheTest";
- public final long MAX_WAIT_TIME=60*1000;
- public final long WAIT_TIME_INCR=10*1000;
- private static final long THRESHOLD=5;
- private static final long ACTUAL_THRESHOLD=10;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- if(localLOGV) Log.i(TAG, "Cleaning up cache directory first");
- cleanUpCacheDirectory();
- }
-
- void cleanUpDirectory(File pDir, String dirName) {
- File testDir = new File(pDir, dirName);
- if(!testDir.exists()) {
- return;
- }
- String fList[] = testDir.list();
- for(int i = 0; i < fList.length; i++) {
- File file = new File(testDir, fList[i]);
- if(file.isDirectory()) {
- cleanUpDirectory(testDir, fList[i]);
- } else {
- file.delete();
- }
- }
- testDir.delete();
- }
-
- void cleanUpCacheDirectory() {
- File testDir = mContext.getCacheDir();
- if(!testDir.exists()) {
- return;
- }
-
- String fList[] = testDir.list();
- if(fList == null) {
- testDir.delete();
- return;
- }
- for(int i = 0; i < fList.length; i++) {
- File file = new File(testDir, fList[i]);
- if(file.isDirectory()) {
- cleanUpDirectory(testDir, fList[i]);
- } else {
- file.delete();
- }
- }
- }
-
- @SmallTest
- public void testDeleteAllCacheFiles() {
- String testName="testDeleteAllCacheFiles";
- cleanUpCacheDirectory();
- }
-
- void failStr(String errMsg) {
- Log.w(TAG, "errMsg="+errMsg);
- fail(errMsg);
- }
-
- void failStr(Exception e) {
- Log.w(TAG, "e.getMessage="+e.getMessage());
- Log.w(TAG, "e="+e);
- }
-
- long getFreeStorageBlks(StatFs st) {
- st.restat("/data");
- return st.getFreeBlocks();
- }
-
- long getFreeStorageSize(StatFs st) {
- st.restat("/data");
- return (long) st.getFreeBlocks() * (long) st.getBlockSize();
- }
-
- @LargeTest
- @Suppress // Failing.
- public void testFreeApplicationCacheAllFiles() throws Exception {
- boolean TRACKING = true;
- StatFs st = new StatFs("/data");
- long blks1 = getFreeStorageBlks(st);
- long availableMem = getFreeStorageSize(st);
- File cacheDir = mContext.getCacheDir();
- assertNotNull(cacheDir);
- createTestFiles1(cacheDir, "testtmpdir", 5);
- long blks2 = getFreeStorageBlks(st);
- if(localLOGV || TRACKING) Log.i(TAG, "blk1="+blks1+", blks2="+blks2);
- //this should free up the test files that were created earlier
- if (!invokePMFreeApplicationCache(availableMem)) {
- fail("Could not successfully invoke PackageManager free app cache API");
- }
- long blks3 = getFreeStorageBlks(st);
- if(localLOGV || TRACKING) Log.i(TAG, "blks3="+blks3);
- verifyTestFiles1(cacheDir, "testtmpdir", 5);
- }
-
- public void testFreeApplicationCacheSomeFiles() throws Exception {
- StatFs st = new StatFs("/data");
- long blks1 = getFreeStorageBlks(st);
- File cacheDir = mContext.getCacheDir();
- assertNotNull(cacheDir);
- createTestFiles1(cacheDir, "testtmpdir", 5);
- long blks2 = getFreeStorageBlks(st);
- Log.i(TAG, "blk1="+blks1+", blks2="+blks2);
- long diff = (blks1-blks2-2);
- if (!invokePMFreeApplicationCache(diff * st.getBlockSize())) {
- fail("Could not successfully invoke PackageManager free app cache API");
- }
- long blks3 = getFreeStorageBlks(st);
- //blks3 should be greater than blks2 and less than blks1
- if(!((blks3 <= blks1) && (blks3 >= blks2))) {
- failStr("Expected "+(blks1-blks2)+" number of blocks to be freed but freed only "
- +(blks1-blks3));
- }
- }
-
- /**
- * This method opens an output file writes to it, opens the same file as an input
- * stream, reads the contents and verifies the data that was written earlier can be read
- */
- public void openOutFileInAppFilesDir(File pFile, String pFileOut) {
- FileOutputStream fos = null;
- try {
- fos = new FileOutputStream(pFile);
- } catch (FileNotFoundException e1) {
- failStr("Error when opening file "+e1);
- return;
- }
- try {
- fos.write(pFileOut.getBytes());
- fos.close();
- } catch (FileNotFoundException e) {
- failStr(e.getMessage());
- } catch (IOException e) {
- failStr(e.getMessage());
- }
- int count = pFileOut.getBytes().length;
- byte[] buffer = new byte[count];
- try {
- FileInputStream fis = new FileInputStream(pFile);
- fis.read(buffer, 0, count);
- fis.close();
- } catch (FileNotFoundException e) {
- failStr("Failed when verifing output opening file "+e.getMessage());
- } catch (IOException e) {
- failStr("Failed when verifying output, reading from written file "+e);
- }
- String str = new String(buffer);
- assertEquals(str, pFileOut);
- }
-
- /*
- * This test case verifies that output written to a file
- * using Context.openFileOutput has executed successfully.
- * The operation is verified by invoking Context.openFileInput
- */
- @MediumTest
- public void testAppFilesCreateFile() {
- String fileName = "testFile1.txt";
- String fileOut = "abcdefghijklmnopqrstuvwxyz";
- Context con = super.getContext();
- try {
- FileOutputStream fos = con.openFileOutput(fileName, Context.MODE_PRIVATE);
- fos.close();
- } catch (FileNotFoundException e) {
- failStr(e);
- } catch (IOException e) {
- failStr(e);
- }
- }
-
- @SmallTest
- public void testAppCacheCreateFile() {
- String fileName = "testFile1.txt";
- String fileOut = "abcdefghijklmnopqrstuvwxyz";
- Context con = super.getContext();
- File file = new File(con.getCacheDir(), fileName);
- openOutFileInAppFilesDir(file, fileOut);
- cleanUpCacheDirectory();
- }
-
- @MediumTest
- public void testAppCreateCacheFiles() {
- File cacheDir = mContext.getCacheDir();
- String testDirName = "testtmp";
- File testTmpDir = new File(cacheDir, testDirName);
- testTmpDir.mkdir();
- int numDirs = 3;
- File fileArr[] = new File[numDirs];
- for(int i = 0; i < numDirs; i++) {
- fileArr[i] = new File(testTmpDir, "dir"+(i+1));
- fileArr[i].mkdir();
- }
- byte buffer[] = getBuffer();
- Log.i(TAG, "Size of bufer="+buffer.length);
- for(int i = 0; i < numDirs; i++) {
- for(int j = 1; j <= (i); j++) {
- File file1 = new File(fileArr[i], "testFile"+j+".txt");
- FileOutputStream fos = null;
- try {
- fos = new FileOutputStream(file1);
- for(int k = 1; k < 10; k++) {
- fos.write(buffer);
- }
- Log.i(TAG, "wrote 10K bytes to "+file1);
- fos.close();
- } catch (FileNotFoundException e) {
- Log.i(TAG, "Excetion ="+e);
- fail("Error when creating outputstream "+e);
- } catch(IOException e) {
- Log.i(TAG, "Excetion ="+e);
- fail("Error when writing output "+e);
- }
- }
- }
- }
-
- byte[] getBuffer() {
- String sbuffer = "a";
- for(int i = 0; i < 10; i++) {
- sbuffer += sbuffer;
- }
- return sbuffer.getBytes();
- }
-
- long getFileNumBlocks(long fileSize, long blkSize) {
- long ret = fileSize/blkSize;
- if(ret*blkSize < fileSize) {
- ret++;
- }
- return ret;
- }
-
- //@LargeTest
- public void testAppCacheClear() {
- String dataDir="/data/data";
- StatFs st = new StatFs(dataDir);
- long blkSize = st.getBlockSize();
- long totBlks = st.getBlockCount();
- long availableBlks = st.getFreeBlocks();
- long thresholdBlks = (totBlks * THRESHOLD) / 100L;
- String testDirName = "testdir";
- //create directory in cache
- File testDir = new File(mContext.getCacheDir(), testDirName);
- testDir.mkdirs();
- byte[] buffer = getBuffer();
- int i = 1;
- if(localLOGV) Log.i(TAG, "availableBlks="+availableBlks+", thresholdBlks="+thresholdBlks);
- long createdFileBlks = 0;
- int imax = 300;
- while((availableBlks > thresholdBlks) &&(i < imax)) {
- File testFile = new File(testDir, "testFile"+i+".txt");
- if(localLOGV) Log.i(TAG, "Creating "+i+"th test file "+testFile);
- int jmax = i;
- i++;
- FileOutputStream fos;
- try {
- fos = new FileOutputStream(testFile);
- } catch (FileNotFoundException e) {
- Log.i(TAG, "Failed creating test file:"+testFile);
- continue;
- }
- boolean err = false;
- for(int j = 1; j <= jmax;j++) {
- try {
- fos.write(buffer);
- } catch (IOException e) {
- Log.i(TAG, "Failed to write to file:"+testFile);
- err = true;
- }
- }
- try {
- fos.close();
- } catch (IOException e) {
- Log.i(TAG, "Failed closing file:"+testFile);
- }
- if(err) {
- continue;
- }
- createdFileBlks += getFileNumBlocks(testFile.length(), blkSize);
- st.restat(dataDir);
- availableBlks = st.getFreeBlocks();
- }
- st.restat(dataDir);
- long availableBytes = st.getFreeBlocks()*blkSize;
- long shouldFree = (ACTUAL_THRESHOLD-THRESHOLD)*totBlks;
- //would have run out of memory
- //wait for some time and confirm cache is deleted
- try {
- Log.i(TAG, "Sleeping for 2 minutes...");
- Thread.sleep(2*60*1000);
- } catch (InterruptedException e) {
- fail("Exception when sleeping "+e);
- }
- boolean removedFlag = false;
- long existingFileBlks = 0;
- for(int k = 1; k <i; k++) {
- File testFile = new File(testDir, "testFile"+k+".txt");
- if(!testFile.exists()) {
- removedFlag = true;
- if(localLOGV) Log.i(TAG, testFile+" removed");
- } else {
- existingFileBlks += getFileNumBlocks(testFile.length(), blkSize);
- }
- }
- if(localLOGV) Log.i(TAG, "createdFileBlks="+createdFileBlks+
- ", existingFileBlks="+existingFileBlks);
- long fileSize = createdFileBlks-existingFileBlks;
- //verify fileSize number of bytes have been cleared from cache
- if(localLOGV) Log.i(TAG, "deletedFileBlks="+fileSize+" shouldFreeBlks="+shouldFree);
- if((fileSize > (shouldFree-blkSize) && (fileSize < (shouldFree+blkSize)))) {
- Log.i(TAG, "passed");
- }
- assertTrue("Files should have been removed", removedFlag);
- }
-
- //createTestFiles(new File(super.getContext().getCacheDir(), "testtmp", "dir", 3)
- void createTestFiles1(File cacheDir, String testFilePrefix, int numTestFiles) {
- byte buffer[] = getBuffer();
- for(int i = 0; i < numTestFiles; i++) {
- File file1 = new File(cacheDir, testFilePrefix+i+".txt");
- FileOutputStream fos = null;
- try {
- fos = new FileOutputStream(file1);
- for(int k = 1; k < 10; k++) {
- fos.write(buffer);
- }
- fos.close();
- } catch (FileNotFoundException e) {
- Log.i(TAG, "Exception ="+e);
- fail("Error when creating outputstream "+e);
- } catch(IOException e) {
- Log.i(TAG, "Exception ="+e);
- fail("Error when writing output "+e);
- }
- try {
- //introduce sleep for 1 s to avoid common time stamps for files being created
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- fail("Exception when sleeping "+e);
- }
- }
- }
-
- void verifyTestFiles1(File cacheDir, String testFilePrefix, int numTestFiles) {
- List<String> files = new ArrayList<String>();
- for(int i = 0; i < numTestFiles; i++) {
- File file1 = new File(cacheDir, testFilePrefix+i+".txt");
- if(file1.exists()) {
- files.add(file1.getName());
- }
- }
- if (files.size() > 0) {
- fail("Files should have been deleted: "
- + Arrays.toString(files.toArray(new String[files.size()])));
- }
- }
-
- void createTestFiles2(File cacheDir, String rootTestDirName, String subDirPrefix, int numDirs, String testFilePrefix) {
- Context con = super.getContext();
- File testTmpDir = new File(cacheDir, rootTestDirName);
- testTmpDir.mkdir();
- File fileArr[] = new File[numDirs];
- for(int i = 0; i < numDirs; i++) {
- fileArr[i] = new File(testTmpDir, subDirPrefix+(i+1));
- fileArr[i].mkdir();
- }
- byte buffer[] = getBuffer();
- for(int i = 0; i < numDirs; i++) {
- for(int j = 1; j <= (i); j++) {
- File file1 = new File(fileArr[i], testFilePrefix+j+".txt");
- FileOutputStream fos = null;
- try {
- fos = new FileOutputStream(file1);
- for(int k = 1; k < 10; k++) {
- fos.write(buffer);
- }
- fos.close();
- } catch (FileNotFoundException e) {
- Log.i(TAG, "Exception ="+e);
- fail("Error when creating outputstream "+e);
- } catch(IOException e) {
- Log.i(TAG, "Exception ="+e);
- fail("Error when writing output "+e);
- }
- try {
- //introduce sleep for 10 ms to avoid common time stamps for files being created
- Thread.sleep(10);
- } catch (InterruptedException e) {
- fail("Exception when sleeping "+e);
- }
- }
- }
- }
-
- class PackageDataObserver extends IPackageDataObserver.Stub {
- public boolean retValue = false;
- private boolean doneFlag = false;
- public void onRemoveCompleted(String packageName, boolean succeeded)
- throws RemoteException {
- synchronized(this) {
- retValue = succeeded;
- doneFlag = true;
- notifyAll();
- }
- }
- public boolean isDone() {
- return doneFlag;
- }
- }
-
- IPackageManager getPm() {
- return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
- }
-
- boolean invokePMDeleteAppCacheFiles() throws Exception {
- try {
- String packageName = mContext.getPackageName();
- PackageDataObserver observer = new PackageDataObserver();
- //wait on observer
- synchronized(observer) {
- getPm().deleteApplicationCacheFiles(packageName, observer);
- long waitTime = 0;
- while(!observer.isDone() || (waitTime > MAX_WAIT_TIME)) {
- observer.wait(WAIT_TIME_INCR);
- waitTime += WAIT_TIME_INCR;
- }
- if(!observer.isDone()) {
- throw new Exception("timed out waiting for PackageDataObserver.onRemoveCompleted");
- }
- }
- return observer.retValue;
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
- return false;
- } catch (InterruptedException e) {
- Log.w(TAG, "InterruptedException :"+e);
- return false;
- }
- }
-
- boolean invokePMFreeApplicationCache(long idealStorageSize) throws Exception {
- try {
- String packageName = mContext.getPackageName();
- PackageDataObserver observer = new PackageDataObserver();
- //wait on observer
- synchronized(observer) {
- getPm().freeStorageAndNotify(null, idealStorageSize, 0, observer);
- long waitTime = 0;
- while(!observer.isDone() || (waitTime > MAX_WAIT_TIME)) {
- observer.wait(WAIT_TIME_INCR);
- waitTime += WAIT_TIME_INCR;
- }
- if(!observer.isDone()) {
- throw new Exception("timed out waiting for PackageDataObserver.onRemoveCompleted");
- }
- }
- return observer.retValue;
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
- return false;
- } catch (InterruptedException e) {
- Log.w(TAG, "InterruptedException :"+e);
- return false;
- }
- }
-
- boolean invokePMFreeStorage(long idealStorageSize, FreeStorageReceiver r,
- PendingIntent pi) throws Exception {
- try {
- // Spin lock waiting for call back
- synchronized(r) {
- getPm().freeStorage(null, idealStorageSize, 0, pi.getIntentSender());
- long waitTime = 0;
- while(!r.isDone() && (waitTime < MAX_WAIT_TIME)) {
- r.wait(WAIT_TIME_INCR);
- waitTime += WAIT_TIME_INCR;
- }
- if(!r.isDone()) {
- throw new Exception("timed out waiting for call back from PendingIntent");
- }
- }
- return r.getResultCode() == 1;
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
- return false;
- } catch (InterruptedException e) {
- Log.w(TAG, "InterruptedException :"+e);
- return false;
- }
- }
-
- @LargeTest
- public void testDeleteAppCacheFiles() throws Exception {
- String testName="testDeleteAppCacheFiles";
- File cacheDir = mContext.getCacheDir();
- createTestFiles1(cacheDir, "testtmpdir", 5);
- assertTrue(invokePMDeleteAppCacheFiles());
- //confirm files dont exist
- verifyTestFiles1(cacheDir, "testtmpdir", 5);
- }
-
- class PackageStatsObserver extends IPackageStatsObserver.Stub {
- public boolean retValue = false;
- public PackageStats stats;
- private boolean doneFlag = false;
-
- public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
- throws RemoteException {
- synchronized(this) {
- retValue = succeeded;
- stats = pStats;
- doneFlag = true;
- notifyAll();
- }
- }
- public boolean isDone() {
- return doneFlag;
- }
- }
-
- @SmallTest
- public void testGetSystemSharedLibraryNames() throws Exception {
- try {
- String[] sharedLibs = getPm().getSystemSharedLibraryNames();
- if (localLOGV) {
- for (String str : sharedLibs) {
- Log.i(TAG, str);
- }
- }
- } catch (RemoteException e) {
- fail("Failed invoking getSystemSharedLibraryNames with exception:" + e);
- }
- }
-
- class FreeStorageReceiver extends BroadcastReceiver {
- public static final String ACTION_FREE = "com.android.unit_tests.testcallback";
- private boolean doneFlag = false;
-
- public boolean isDone() {
- return doneFlag;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if(intent.getAction().equalsIgnoreCase(ACTION_FREE)) {
- if (localLOGV) Log.i(TAG, "Got notification: clear cache succeeded "+getResultCode());
- synchronized (this) {
- doneFlag = true;
- notifyAll();
- }
- }
- }
- }
-
- // TODO: flaky test, omit from LargeTest for now
- //@LargeTest
- public void testFreeStorage() throws Exception {
- boolean TRACKING = true;
- StatFs st = new StatFs("/data");
- long blks1 = getFreeStorageBlks(st);
- if(localLOGV || TRACKING) Log.i(TAG, "Available free blocks="+blks1);
- long availableMem = getFreeStorageSize(st);
- File cacheDir = mContext.getCacheDir();
- assertNotNull(cacheDir);
- createTestFiles1(cacheDir, "testtmpdir", 5);
- long blks2 = getFreeStorageBlks(st);
- if(localLOGV || TRACKING) Log.i(TAG, "Available blocks after writing test files in application cache="+blks2);
- // Create receiver and register it
- FreeStorageReceiver receiver = new FreeStorageReceiver();
- mContext.registerReceiver(receiver, new IntentFilter(FreeStorageReceiver.ACTION_FREE));
- PendingIntent pi = PendingIntent.getBroadcast(mContext,
- 0, new Intent(FreeStorageReceiver.ACTION_FREE), 0);
- // Invoke PackageManager api
- if (!invokePMFreeStorage(availableMem, receiver, pi)) {
- fail("Could not invoke PackageManager free storage API");
- }
- long blks3 = getFreeStorageBlks(st);
- if(localLOGV || TRACKING) Log.i(TAG, "Available blocks after freeing cache"+blks3);
- assertEquals(receiver.getResultCode(), 1);
- mContext.unregisterReceiver(receiver);
- // Verify result
- verifyTestFiles1(cacheDir, "testtmpdir", 5);
- }
-
- /* utility method used to create observer and check async call back from PackageManager.
- * ClearApplicationUserData
- */
- boolean invokePMClearApplicationUserData() throws Exception {
- try {
- String packageName = mContext.getPackageName();
- PackageDataObserver observer = new PackageDataObserver();
- //wait on observer
- synchronized(observer) {
- getPm().clearApplicationUserData(packageName, observer, 0 /* TODO: Other users */);
- long waitTime = 0;
- while(!observer.isDone() || (waitTime > MAX_WAIT_TIME)) {
- observer.wait(WAIT_TIME_INCR);
- waitTime += WAIT_TIME_INCR;
- }
- if(!observer.isDone()) {
- throw new Exception("timed out waiting for PackageDataObserver.onRemoveCompleted");
- }
- }
- return observer.retValue;
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
- return false;
- } catch (InterruptedException e) {
- Log.w(TAG, "InterruptedException :"+e);
- return false;
- }
- }
-
- void verifyUserDataCleared(File pDir) {
- if(localLOGV) Log.i(TAG, "Verifying "+pDir);
- if(pDir == null) {
- return;
- }
- String fileList[] = pDir.list();
- if(fileList == null) {
- return;
- }
- int imax = fileList.length;
- //look recursively in user data dir
- for(int i = 0; i < imax; i++) {
- if(localLOGV) Log.i(TAG, "Found entry "+fileList[i]+ "in "+pDir);
- if("lib".equalsIgnoreCase(fileList[i])) {
- if(localLOGV) Log.i(TAG, "Ignoring lib directory");
- continue;
- }
- fail(pDir+" should be empty or contain only lib subdirectory. Found "+fileList[i]);
- }
- }
-
- File getDataDir() {
- try {
- ApplicationInfo appInfo = getPm().getApplicationInfo(mContext.getPackageName(), 0,
- UserHandle.myUserId());
- return new File(appInfo.dataDir);
- } catch (RemoteException e) {
- throw new RuntimeException("Pacakge manager dead", e);
- }
- }
-
- @Suppress
- @LargeTest
- public void testClearApplicationUserDataWithTestData() throws Exception {
- File cacheDir = mContext.getCacheDir();
- createTestFiles1(cacheDir, "testtmpdir", 5);
- if(localLOGV) {
- Log.i(TAG, "Created test data Waiting for 60seconds before continuing");
- Thread.sleep(60*1000);
- }
- assertTrue(invokePMClearApplicationUserData());
- //confirm files dont exist
- verifyUserDataCleared(getDataDir());
- }
-
- @Suppress
- @SmallTest
- public void testClearApplicationUserDataWithNoTestData() throws Exception {
- assertTrue(invokePMClearApplicationUserData());
- //confirm files dont exist
- verifyUserDataCleared(getDataDir());
- }
-
- @Suppress
- @LargeTest
- public void testClearApplicationUserDataNoObserver() throws Exception {
- getPm().clearApplicationUserData(mContext.getPackageName(), null, UserHandle.myUserId());
- //sleep for 1 minute
- Thread.sleep(60*1000);
- //confirm files dont exist
- verifyUserDataCleared(getDataDir());
- }
-
-}
diff --git a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
index 55092fa..b627619 100644
--- a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
@@ -16,11 +16,9 @@
package android.content.pm;
+import static android.os.storage.VolumeInfo.STATE_MOUNTED;
+
import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.test.AndroidTestCase;
@@ -36,16 +34,10 @@
import java.util.List;
import java.util.UUID;
-import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.os.storage.VolumeInfo.STATE_MOUNTED;
-
public class PackageHelperTests extends AndroidTestCase {
private static final boolean localLOGV = true;
public static final String TAG = "PackageHelperTests";
protected final String PREFIX = "android.content.pm";
- private IStorageManager mSm;
- private String fullId;
- private String fullId2;
private static final String sInternalVolPath = "/data";
private static final String sAdoptedVolPath = "/mnt/expand/123";
@@ -147,34 +139,11 @@
}
}
- private IStorageManager getSm() {
- IBinder service = ServiceManager.getService("mount");
- if (service != null) {
- return IStorageManager.Stub.asInterface(service);
- } else {
- Log.e(TAG, "Can't get mount service");
- }
- return null;
- }
-
- private void cleanupContainers() throws RemoteException {
- Log.d(TAG,"cleanUp");
- IStorageManager sm = getSm();
- String[] containers = sm.getSecureContainerList();
- for (int i = 0; i < containers.length; i++) {
- if (containers[i].startsWith(PREFIX)) {
- Log.d(TAG,"cleaing up "+containers[i]);
- sm.destroySecureContainer(containers[i], true);
- }
- }
- }
-
@Override
protected void setUp() throws Exception {
super.setUp();
sStorageManager = createStorageManagerMock();
if (localLOGV) Log.i(TAG, "Cleaning out old test containers");
- cleanupContainers();
}
@Override
@@ -182,55 +151,6 @@
super.tearDown();
sStorageManager = null;
if (localLOGV) Log.i(TAG, "Cleaning out old test containers");
- cleanupContainers();
- }
-
- public void testMountAndPullSdCard() throws Exception {
- fullId = PREFIX;
- fullId2 = PackageHelper.createSdDir(1024 * MB_IN_BYTES, fullId, "none",
- android.os.Process.myUid(), true);
-
- Log.d(TAG, "getSdDir=" + PackageHelper.getSdDir(fullId));
- PackageHelper.unMountSdDir(fullId);
-
- Runnable r1 = getMountRunnable();
- Runnable r2 = getDestroyRunnable();
- Thread thread = new Thread(r1);
- Thread thread2 = new Thread(r2);
- thread2.start();
- thread.start();
- }
-
- public Runnable getMountRunnable() {
- Runnable r = new Runnable () {
- public void run () {
- try {
- Thread.sleep(5);
- String path = PackageHelper.mountSdDir(fullId, "none",
- android.os.Process.myUid());
- Log.e(TAG, "mount done " + path);
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Throwable t) {
- Log.e(TAG, "mount failed", t);
- }
- }
- };
- return r;
- }
-
- public Runnable getDestroyRunnable() {
- Runnable r = new Runnable () {
- public void run () {
- try {
- PackageHelper.destroySdDir(fullId);
- Log.e(TAG, "destroy done: " + fullId);
- } catch (Throwable t) {
- Log.e(TAG, "destroy failed", t);
- }
- }
- };
- return r;
}
public void testResolveInstallVolumeInternal_SystemApp() throws IOException {
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index b8fa06c..a317c99 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -46,13 +46,8 @@
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.StatFs;
import android.os.SystemClock;
-import android.os.storage.IStorageManager;
-import android.os.storage.StorageListener;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageResultCode;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.system.ErrnoException;
@@ -103,8 +98,6 @@
private static final int APP_INSTALL_SDCARD = PackageHelper.APP_INSTALL_EXTERNAL;
- private boolean mOrigState;
-
void failStr(String errMsg) {
Log.w(TAG, "errMsg=" + errMsg);
fail(errMsg);
@@ -114,29 +107,6 @@
failStr(e.getMessage());
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mOrigState = checkMediaState(Environment.MEDIA_MOUNTED);
- if (!mountMedia()) {
- Log.i(TAG, "sdcard not mounted? Some of these tests might fail");
- }
- }
-
- @Override
- protected void tearDown() throws Exception {
- // Restore media state.
- boolean newState = checkMediaState(Environment.MEDIA_MOUNTED);
- if (newState != mOrigState) {
- if (mOrigState) {
- mountMedia();
- } else {
- unmountMedia();
- }
- }
- super.tearDown();
- }
-
private abstract static class GenericReceiver extends BroadcastReceiver {
private boolean doneFlag = false;
@@ -782,17 +752,6 @@
sampleInstallFromRawResource(0, true);
}
- @LargeTest
- public void testInstallSdcard() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- mountMedia();
- sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, true);
- }
-
/* ------------------------- Test replacing packages -------------- */
class ReplaceReceiver extends GenericReceiver {
String pkgName;
@@ -1081,240 +1040,6 @@
deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DELETE_KEEP_DATA);
}
- /* sdcard mount/unmount tests ***** */
-
- class SdMountReceiver extends GenericReceiver {
- String pkgNames[];
-
- boolean status = true;
-
- SdMountReceiver(String[] pkgNames) {
- this.pkgNames = pkgNames;
- IntentFilter filter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
- super.setFilter(filter);
- }
-
- public boolean notifyNow(Intent intent) {
- Log.i(TAG, "okay 1");
- String action = intent.getAction();
- if (!Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
- return false;
- }
- String rpkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- for (String pkg : pkgNames) {
- boolean found = false;
- for (String rpkg : rpkgList) {
- if (rpkg.equals(pkg)) {
- found = true;
- break;
- }
- }
- if (!found) {
- status = false;
- return true;
- }
- }
- return true;
- }
- }
-
- class SdUnMountReceiver extends GenericReceiver {
- String pkgNames[];
-
- boolean status = true;
-
- SdUnMountReceiver(String[] pkgNames) {
- this.pkgNames = pkgNames;
- IntentFilter filter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- super.setFilter(filter);
- }
-
- public boolean notifyNow(Intent intent) {
- String action = intent.getAction();
- if (!Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
- return false;
- }
- String rpkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- for (String pkg : pkgNames) {
- boolean found = false;
- for (String rpkg : rpkgList) {
- if (rpkg.equals(pkg)) {
- found = true;
- break;
- }
- }
- if (!found) {
- status = false;
- return true;
- }
- }
- return true;
- }
- }
-
- IStorageManager getSm() {
- IBinder service = ServiceManager.getService("mount");
- if (service != null) {
- return IStorageManager.Stub.asInterface(service);
- } else {
- Log.e(TAG, "Can't get storagemanager service");
- }
- return null;
- }
-
- boolean checkMediaState(String desired) {
- String actual = Environment.getExternalStorageState();
- if (desired.equals(actual)) {
- return true;
- } else {
- return false;
- }
- }
-
- boolean mountMedia() {
- // We can't mount emulated storage.
- if (Environment.isExternalStorageEmulated()) {
- return true;
- }
-
- if (checkMediaState(Environment.MEDIA_MOUNTED)) {
- return true;
- }
-
- final String path = Environment.getExternalStorageDirectory().toString();
- StorageListener observer = new StorageListener(Environment.MEDIA_MOUNTED);
- StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
- sm.registerListener(observer);
- try {
- // Wait on observer
- synchronized (observer) {
- int ret = getSm().mountVolume(path);
- if (ret != StorageResultCode.OperationSucceeded) {
- throw new Exception("Could not mount the media");
- }
- long waitTime = 0;
- while ((!observer.isDone()) && (waitTime < MAX_WAIT_TIME)) {
- observer.wait(WAIT_TIME_INCR);
- waitTime += WAIT_TIME_INCR;
- }
- if (!observer.isDone()) {
- throw new Exception("Timed out waiting for unmount media notification");
- }
- return true;
- }
- } catch (Exception e) {
- Log.e(TAG, "Exception : " + e);
- return false;
- } finally {
- sm.unregisterListener(observer);
- }
- }
-
- private boolean unmountMedia() {
- // We can't unmount emulated storage.
- if (Environment.isExternalStorageEmulated()) {
- return true;
- }
-
- if (checkMediaState(Environment.MEDIA_UNMOUNTED)) {
- return true;
- }
-
- final String path = Environment.getExternalStorageDirectory().getPath();
- StorageListener observer = new StorageListener(Environment.MEDIA_UNMOUNTED);
- StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
- sm.registerListener(observer);
- try {
- // Wait on observer
- synchronized (observer) {
- getSm().unmountVolume(path, true, false);
- long waitTime = 0;
- while ((!observer.isDone()) && (waitTime < MAX_WAIT_TIME)) {
- observer.wait(WAIT_TIME_INCR);
- waitTime += WAIT_TIME_INCR;
- }
- if (!observer.isDone()) {
- throw new Exception("Timed out waiting for unmount media notification");
- }
- return true;
- }
- } catch (Exception e) {
- Log.e(TAG, "Exception : " + e);
- return false;
- } finally {
- sm.unregisterListener(observer);
- }
- }
-
- private boolean mountFromRawResource() throws Exception {
- // Install pkg on sdcard
- InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, false);
- if (localLOGV) Log.i(TAG, "Installed pkg on sdcard");
- boolean origState = checkMediaState(Environment.MEDIA_MOUNTED);
- boolean registeredReceiver = false;
- SdMountReceiver receiver = new SdMountReceiver(new String[]{ip.pkg.packageName});
- try {
- if (localLOGV) Log.i(TAG, "Unmounting media");
- // Unmount media
- assertTrue(unmountMedia());
- if (localLOGV) Log.i(TAG, "Unmounted media");
- // Register receiver here
- PackageManager pm = getPm();
- mContext.registerReceiver(receiver, receiver.filter);
- registeredReceiver = true;
-
- // Wait on receiver
- synchronized (receiver) {
- if (localLOGV) Log.i(TAG, "Mounting media");
- // Mount media again
- assertTrue(mountMedia());
- if (localLOGV) Log.i(TAG, "Mounted media");
- if (localLOGV) Log.i(TAG, "Waiting for notification");
- long waitTime = 0;
- // Verify we received the broadcast
- waitTime = 0;
- while ((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME)) {
- receiver.wait(WAIT_TIME_INCR);
- waitTime += WAIT_TIME_INCR;
- }
- if(!receiver.isDone()) {
- failStr("Timed out waiting for EXTERNAL_APPLICATIONS notification");
- }
- return receiver.received;
- }
- } catch (InterruptedException e) {
- failStr(e);
- return false;
- } finally {
- if (registeredReceiver) {
- mContext.unregisterReceiver(receiver);
- }
- // Restore original media state
- if (origState) {
- mountMedia();
- } else {
- unmountMedia();
- }
- if (localLOGV) Log.i(TAG, "Cleaning up install");
- cleanUpInstall(ip);
- }
- }
-
- /*
- * Install package on sdcard. Unmount and then mount the media.
- * (Use PackageManagerService private api for now)
- * Make sure the installed package is available.
- */
- @LargeTest
- public void testMountSdNormalInternal() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertTrue(mountFromRawResource());
- }
-
void cleanUpInstall(InstallParams ip) throws Exception {
if (ip == null) {
return;
@@ -1713,64 +1438,6 @@
}
}
- /*
- * Test that an install error code is returned when media is unmounted
- * and package installed on sdcard via package manager flag.
- */
- @LargeTest
- public void testInstallSdcardUnmount() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- boolean origState = checkMediaState(Environment.MEDIA_MOUNTED);
- try {
- // Unmount sdcard
- assertTrue(unmountMedia());
- // Try to install and make sure an error code is returned.
- installFromRawResource("install.apk", R.raw.install,
- PackageManager.INSTALL_EXTERNAL, false,
- true, PackageInstaller.STATUS_FAILURE_STORAGE,
- PackageInfo.INSTALL_LOCATION_AUTO);
- } finally {
- // Restore original media state
- if (origState) {
- mountMedia();
- } else {
- unmountMedia();
- }
- }
- }
-
- /*
- * Unmount sdcard. Try installing an app with manifest option to install
- * on sdcard. Make sure it gets installed on internal flash.
- */
- @LargeTest
- public void testInstallManifestSdcardUnmount() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- boolean origState = checkMediaState(Environment.MEDIA_MOUNTED);
- try {
- // Unmount sdcard
- assertTrue(unmountMedia());
- InstallParams ip = new InstallParams("install.apk", R.raw.install_loc_sdcard);
- installFromRawResource(ip, 0, true, false, -1,
- PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
- } finally {
- // Restore original media state
- if (origState) {
- mountMedia();
- } else {
- unmountMedia();
- }
- }
- }
-
/*---------- Recommended install location tests ----*/
/*
* PrecedenceSuffixes:
@@ -2527,133 +2194,6 @@
}
/*
- * Ensure that permissions are properly declared.
- */
- @LargeTest
- public void testInstallOnSdPermissionsUnmount() throws Exception {
- InstallParams ip = null;
- boolean origMediaState = checkMediaState(Environment.MEDIA_MOUNTED);
- try {
- // **: Upon installing a package, are its declared permissions published?
- int iFlags = PackageManager.INSTALL_INTERNAL;
- int iApk = R.raw.install_decl_perm;
- ip = installFromRawResource("install.apk", iApk,
- iFlags, false,
- false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
- assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
- assertPermissions(BASE_PERMISSIONS_DEFINED);
- // Unmount media here
- assertTrue(unmountMedia());
- // Mount media again
- mountMedia();
- //Check permissions now
- assertPermissions(BASE_PERMISSIONS_DEFINED);
- } finally {
- if (ip != null) {
- cleanUpInstall(ip);
- }
- }
- }
-
- /* This test creates a stale container via StorageManagerService and then installs
- * a package and verifies that the stale container is cleaned up and install
- * is successful.
- * Please note that this test is very closely tied to the framework's
- * naming convention for secure containers.
- */
- @LargeTest
- public void testInstallSdcardStaleContainer() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- boolean origMediaState = checkMediaState(Environment.MEDIA_MOUNTED);
- try {
- // Mount media first
- mountMedia();
- String outFileName = "install.apk";
- int rawResId = R.raw.install;
- PackageManager pm = mContext.getPackageManager();
- File filesDir = mContext.getFilesDir();
- File outFile = new File(filesDir, outFileName);
- Uri packageURI = getInstallablePackage(rawResId, outFile);
- PackageParser.Package pkg = parsePackage(packageURI);
- assertNotNull(pkg);
- // Install an app on sdcard.
- installFromRawResource(outFileName, rawResId,
- PackageManager.INSTALL_EXTERNAL, false,
- false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
- // Unmount sdcard
- unmountMedia();
- // Delete the app on sdcard to leave a stale container on sdcard.
- GenericReceiver receiver = new DeleteReceiver(pkg.packageName);
- assertTrue(invokeDeletePackage(pkg.packageName, 0, receiver));
- mountMedia();
- // Reinstall the app and make sure it gets installed.
- installFromRawResource(outFileName, rawResId,
- PackageManager.INSTALL_EXTERNAL, true,
- false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
- } catch (Exception e) {
- failStr(e.getMessage());
- } finally {
- if (origMediaState) {
- mountMedia();
- } else {
- unmountMedia();
- }
-
- }
- }
-
- /* This test installs an application on sdcard and unmounts media.
- * The app is then re-installed on internal storage. The sdcard is mounted
- * and verified that the re-installation on internal storage takes precedence.
- */
- @LargeTest
- public void testInstallSdcardStaleContainerReinstall() throws Exception {
- // Do not run on devices with emulated external storage.
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- boolean origMediaState = checkMediaState(Environment.MEDIA_MOUNTED);
- try {
- // Mount media first
- mountMedia();
- String outFileName = "install.apk";
- int rawResId = R.raw.install;
- PackageManager pm = mContext.getPackageManager();
- File filesDir = mContext.getFilesDir();
- File outFile = new File(filesDir, outFileName);
- Uri packageURI = getInstallablePackage(rawResId, outFile);
- PackageParser.Package pkg = parsePackage(packageURI);
- assertNotNull(pkg);
- // Install an app on sdcard.
- installFromRawResource(outFileName, rawResId,
- PackageManager.INSTALL_EXTERNAL, false,
- false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
- // Unmount sdcard
- unmountMedia();
- // Reinstall the app and make sure it gets installed on internal storage.
- installFromRawResource(outFileName, rawResId,
- PackageManager.INSTALL_REPLACE_EXISTING, false,
- false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
- mountMedia();
- // Verify that the app installed is on internal storage.
- assertInstall(pkg, 0, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
- } catch (Exception e) {
- failStr(e.getMessage());
- } finally {
- if (origMediaState) {
- mountMedia();
- } else {
- unmountMedia();
- }
- }
- }
-
- /*
* The following series of tests are related to upgrading apps with
* different certificates.
*/
diff --git a/core/tests/coretests/src/android/os/storage/AsecTests.java b/core/tests/coretests/src/android/os/storage/AsecTests.java
deleted file mode 100644
index e9a810d..0000000
--- a/core/tests/coretests/src/android/os/storage/AsecTests.java
+++ /dev/null
@@ -1,756 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os.storage;
-
-import android.content.Context;
-import android.os.Environment;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileOutputStream;
-
-public class AsecTests extends AndroidTestCase {
- private static final String SECURE_CONTAINER_PREFIX = "com.android.unittests.AsecTests.";
- private static final boolean localLOGV = true;
- public static final String TAG="AsecTests";
-
- private static final String FS_FAT = "fat";
- private static final String FS_EXT4 = "ext4";
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- if (localLOGV) Log.i(TAG, "Cleaning out old test containers");
- cleanupContainers();
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- if (localLOGV) Log.i(TAG, "Cleaning out old test containers");
- cleanupContainers();
- }
-
- private void cleanupContainers() throws RemoteException {
- IStorageManager sm = getSm();
- String[] containers = sm.getSecureContainerList();
-
- for (int i = 0; i < containers.length; i++) {
- if (containers[i].startsWith(SECURE_CONTAINER_PREFIX)) {
- if (localLOGV)
- Log.i(TAG, "Cleaning: " + containers[i]);
- sm.destroySecureContainer(containers[i], true);
- }
- }
- }
-
- private boolean containerExists(String localId) throws RemoteException {
- IStorageManager sm = getSm();
- String[] containers = sm.getSecureContainerList();
- String fullId = SECURE_CONTAINER_PREFIX + localId;
-
- for (int i = 0; i < containers.length; i++) {
- if (containers[i].equals(fullId)) {
- return true;
- }
- }
- return false;
- }
-
- private int createContainer(String localId, int size, String key, String filesystem,
- boolean isExternal) throws Exception {
- assertTrue("Media should be mounted", isMediaMounted());
- String fullId = SECURE_CONTAINER_PREFIX + localId;
-
- IStorageManager sm = getSm();
- return sm.createSecureContainer(fullId, size, filesystem, key, android.os.Process.myUid(),
- isExternal);
- }
-
- private int mountContainer(String localId, String key) throws Exception {
- assertTrue("Media should be mounted", isMediaMounted());
- String fullId = SECURE_CONTAINER_PREFIX + localId;
-
- IStorageManager sm = getSm();
- return sm.mountSecureContainer(fullId, key, android.os.Process.myUid(), true);
- }
-
- private int renameContainer(String localId1, String localId2) throws Exception {
- assertTrue("Media should be mounted", isMediaMounted());
- String fullId1 = SECURE_CONTAINER_PREFIX + localId1;
- String fullId2 = SECURE_CONTAINER_PREFIX + localId2;
-
- IStorageManager sm = getSm();
- return sm.renameSecureContainer(fullId1, fullId2);
- }
-
- private int unmountContainer(String localId, boolean force) throws Exception {
- assertTrue("Media should be mounted", isMediaMounted());
- String fullId = SECURE_CONTAINER_PREFIX + localId;
-
- IStorageManager sm = getSm();
- return sm.unmountSecureContainer(fullId, force);
- }
-
- private int destroyContainer(String localId, boolean force) throws Exception {
- assertTrue("Media should be mounted", isMediaMounted());
- String fullId = SECURE_CONTAINER_PREFIX + localId;
-
- IStorageManager sm = getSm();
- return sm.destroySecureContainer(fullId, force);
- }
-
- private boolean isContainerMounted(String localId) throws Exception {
- assertTrue("Media should be mounted", isMediaMounted());
- String fullId = SECURE_CONTAINER_PREFIX + localId;
-
- IStorageManager sm = getSm();
- return sm.isSecureContainerMounted(fullId);
- }
-
- private IStorageManager getSm() {
- IBinder service = ServiceManager.getService("mount");
- if (service != null) {
- return IStorageManager.Stub.asInterface(service);
- } else {
- Log.e(TAG, "Can't get storagemanager service");
- }
- return null;
- }
-
- private boolean isMediaMounted() throws Exception {
- String mPath = Environment.getExternalStorageDirectory().toString();
- String state = getSm().getVolumeState(mPath);
- return Environment.MEDIA_MOUNTED.equals(state);
- }
-
-
- /*
- * CREATE
- */
-
- public void test_Fat_External_Create_Success() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testCreateContainer", 4, "none", FS_FAT, true));
- assertTrue(containerExists("testCreateContainer"));
- }
-
- public void test_Ext4_External_Create_Success() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testCreateContainer", 4, "none", FS_EXT4, true));
- assertTrue(containerExists("testCreateContainer"));
- }
-
- public void test_Fat_Internal_Create_Success() throws Exception {
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testCreateContainer", 4, "none", FS_FAT, false));
- assertTrue(containerExists("testCreateContainer"));
- }
-
- public void test_Ext4_Internal_Create_Success() throws Exception {
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testCreateContainer", 4, "none", FS_EXT4, false));
- assertTrue(containerExists("testCreateContainer"));
- }
-
-
- /*
- * CREATE MIN SIZE
- */
-
- public void test_Fat_External_CreateMinSize_Success() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testCreateContainer", 1, "none", FS_FAT, true));
- assertTrue(containerExists("testCreateContainer"));
- }
-
- public void test_Ext4_External_CreateMinSize_Success() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testCreateContainer", 1, "none", FS_EXT4, true));
- assertTrue(containerExists("testCreateContainer"));
- }
-
- public void test_Fat_Internal_CreateMinSize_Success() throws Exception {
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testCreateContainer", 1, "none", FS_FAT, false));
- assertTrue(containerExists("testCreateContainer"));
- }
-
- public void test_Ext4_Internal_CreateMinSize_Success() throws Exception {
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testCreateContainer", 1, "none", FS_EXT4, false));
- assertTrue(containerExists("testCreateContainer"));
- }
-
-
- /*
- * CREATE ZERO SIZE - FAIL CASE
- */
-
- public void test_Fat_External_CreateZeroSize_Failure() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationFailedInternalError,
- createContainer("testCreateZeroContainer", 0, "none", FS_FAT, true));
- }
-
- public void test_Ext4_External_CreateZeroSize_Failure() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationFailedInternalError,
- createContainer("testCreateZeroContainer", 0, "none", FS_EXT4, true));
- }
-
- public void test_Fat_Internal_CreateZeroSize_Failure() throws Exception {
- assertEquals(StorageResultCode.OperationFailedInternalError,
- createContainer("testCreateZeroContainer", 0, "none", FS_FAT, false));
- }
-
- public void test_Ext4_Internal_CreateZeroSize_Failure() throws Exception {
- assertEquals(StorageResultCode.OperationFailedInternalError,
- createContainer("testCreateZeroContainer", 0, "none", FS_EXT4, false));
- }
-
-
- /*
- * CREATE DUPLICATE - FAIL CASE
- */
-
- public void test_Fat_External_CreateDuplicate_Failure() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testCreateDupContainer", 4, "none", FS_FAT, true));
-
- assertEquals(StorageResultCode.OperationFailedInternalError,
- createContainer("testCreateDupContainer", 4, "none", FS_FAT, true));
- }
-
- public void test_Ext4_External_CreateDuplicate_Failure() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testCreateDupContainer", 4, "none", FS_EXT4, true));
-
- assertEquals(StorageResultCode.OperationFailedInternalError,
- createContainer("testCreateDupContainer", 4, "none", FS_EXT4, true));
- }
-
- public void test_Fat_Internal_CreateDuplicate_Failure() throws Exception {
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testCreateDupContainer", 4, "none", FS_FAT, false));
-
- assertEquals(StorageResultCode.OperationFailedInternalError,
- createContainer("testCreateDupContainer", 4, "none", FS_FAT, false));
- }
-
- public void test_Ext4_Internal_CreateDuplicate_Failure() throws Exception {
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testCreateDupContainer", 4, "none", FS_EXT4, false));
-
- assertEquals(StorageResultCode.OperationFailedInternalError,
- createContainer("testCreateDupContainer", 4, "none", FS_EXT4, false));
- }
-
-
- /*
- * DESTROY
- */
-
- public void test_Fat_External_Destroy_Success() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testDestroyContainer", 4, "none", FS_FAT, true));
- assertEquals(StorageResultCode.OperationSucceeded,
- destroyContainer("testDestroyContainer", false));
- }
-
- public void test_Ext4_External_Destroy_Success() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testDestroyContainer", 4, "none", FS_EXT4, true));
- assertEquals(StorageResultCode.OperationSucceeded,
- destroyContainer("testDestroyContainer", false));
- }
-
- public void test_Fat_Internal_Destroy_Success() throws Exception {
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testDestroyContainer", 4, "none", FS_FAT, false));
- assertEquals(StorageResultCode.OperationSucceeded,
- destroyContainer("testDestroyContainer", false));
- }
-
- public void test_Ext4_Internal_Destroy_Success() throws Exception {
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testDestroyContainer", 4, "none", FS_EXT4, false));
- assertEquals(StorageResultCode.OperationSucceeded,
- destroyContainer("testDestroyContainer", false));
- }
-
-
- /*
- * MOUNT
- */
-
- public void test_Fat_External_Mount() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testMountContainer", 4, "none", FS_FAT, true));
-
- assertEquals(StorageResultCode.OperationSucceeded,
- unmountContainer("testMountContainer", false));
-
- assertEquals(StorageResultCode.OperationSucceeded,
- mountContainer("testMountContainer", "none"));
- }
-
-
- /*
- * MOUNT BAD KEY - FAIL CASE
- */
-
- public void test_Fat_External_MountBadKey_Failure() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testMountBadKey", 4, "00000000000000000000000000000000", FS_FAT,
- true));
-
- assertEquals(StorageResultCode.OperationSucceeded,
- unmountContainer("testMountBadKey", false));
-
- assertEquals(StorageResultCode.OperationFailedInternalError,
- mountContainer("testMountContainer", "000000000000000000000000000000001"));
-
- assertEquals(StorageResultCode.OperationFailedInternalError,
- mountContainer("testMountContainer", "none"));
- }
-
-
- public void test_Fat_External_UnmountBusy_Success() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- IStorageManager sm = getSm();
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testUnmountBusyContainer", 4, "none", FS_FAT, true));
-
- String path = sm.getSecureContainerPath(SECURE_CONTAINER_PREFIX
- + "testUnmountBusyContainer");
-
- File f = new File(path, "reference");
- FileOutputStream fos = new FileOutputStream(f);
-
- assertEquals(StorageResultCode.OperationFailedStorageBusy,
- unmountContainer("testUnmountBusyContainer", false));
-
- fos.close();
- assertEquals(StorageResultCode.OperationSucceeded,
- unmountContainer("testUnmountBusyContainer", false));
- }
-
- public void test_Fat_External_DestroyBusy() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- IStorageManager sm = getSm();
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testDestroyBusyContainer", 4, "none", FS_FAT, true));
-
- String path = sm.getSecureContainerPath(SECURE_CONTAINER_PREFIX
- + "testDestroyBusyContainer");
-
- File f = new File(path, "reference");
- FileOutputStream fos = new FileOutputStream(f);
-
- assertEquals(StorageResultCode.OperationFailedStorageBusy,
- destroyContainer("testDestroyBusyContainer", false));
-
- fos.close();
- assertEquals(StorageResultCode.OperationSucceeded,
- destroyContainer("testDestroyBusyContainer", false));
- }
-
- public void test_Fat_External_Rename_Success() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testRenameContainer.1", 4, "none", FS_FAT, true));
-
- assertEquals(StorageResultCode.OperationSucceeded,
- unmountContainer("testRenameContainer.1", false));
-
- assertEquals(StorageResultCode.OperationSucceeded,
- renameContainer("testRenameContainer.1", "testRenameContainer.2"));
-
- assertFalse(containerExists("testRenameContainer.1"));
- assertTrue(containerExists("testRenameContainer.2"));
- }
-
- public void test_Fat_External_RenameSrcMounted_Failure() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testRenameContainer.1", 4, "none", FS_FAT, true));
-
- assertEquals(StorageResultCode.OperationFailedStorageMounted,
- renameContainer("testRenameContainer.1", "testRenameContainer.2"));
- }
-
- public void test_Fat_External_RenameDstMounted_Failure() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testRenameContainer.1", 4, "none", FS_FAT, true));
-
- assertEquals(StorageResultCode.OperationSucceeded,
- unmountContainer("testRenameContainer.1", false));
-
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testRenameContainer.2", 4, "none", FS_FAT, true));
-
- assertEquals(StorageResultCode.OperationFailedStorageMounted,
- renameContainer("testRenameContainer.1", "testRenameContainer.2"));
- }
-
- public void test_Fat_External_Size_Success() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- IStorageManager sm = getSm();
- assertEquals(StorageResultCode.OperationSucceeded,
- createContainer("testContainerSize", 1, "none", FS_FAT, true));
- String path = sm.getSecureContainerPath(SECURE_CONTAINER_PREFIX + "testContainerSize");
-
- byte[] buf = new byte[4096];
- File f = new File(path, "reference");
- FileOutputStream fos = new FileOutputStream(f);
- for (int i = 0; i < (1024 * 1024); i += buf.length) {
- fos.write(buf);
- }
- fos.close();
- }
-
- public void testGetSecureContainerPath_NonExistPath_Failure() throws Exception {
- IStorageManager sm = getSm();
- assertNull("Getting the path for an invalid container should return null",
- sm.getSecureContainerPath("jparks.broke.it"));
- }
-
- /*------------ Tests for unmounting volume ---*/
- public final long MAX_WAIT_TIME=120*1000;
- public final long WAIT_TIME_INCR=20*1000;
-
- boolean getMediaState() throws Exception {
- String mPath = Environment.getExternalStorageDirectory().toString();
- String state = getSm().getVolumeState(mPath);
- return Environment.MEDIA_MOUNTED.equals(state);
- }
-
- boolean mountMedia() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return true;
- }
-
- if (getMediaState()) {
- return true;
- }
-
- String mPath = Environment.getExternalStorageDirectory().toString();
- int ret = getSm().mountVolume(mPath);
- return ret == StorageResultCode.OperationSucceeded;
- }
-
- class StorageListener extends StorageEventListener {
- String oldState;
- String newState;
- String path;
- private boolean doneFlag = false;
-
- public void action() {
- synchronized (this) {
- doneFlag = true;
- notifyAll();
- }
- }
-
- public boolean isDone() {
- return doneFlag;
- }
-
- @Override
- public void onStorageStateChanged(String path, String oldState, String newState) {
- if (localLOGV) Log.i(TAG, "Storage state changed from " + oldState + " to " + newState);
- this.oldState = oldState;
- this.newState = newState;
- this.path = path;
- action();
- }
- }
-
- private void unmountMedia() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- if (!getMediaState()) {
- return;
- }
-
- String path = Environment.getExternalStorageDirectory().toString();
- StorageListener observer = new StorageListener();
- StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
- sm.registerListener(observer);
- try {
- // Wait on observer
- synchronized(observer) {
- getSm().unmountVolume(path, false, false);
- long waitTime = 0;
- while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
- observer.wait(WAIT_TIME_INCR);
- waitTime += WAIT_TIME_INCR;
- }
- if(!observer.isDone()) {
- fail("Timed out waiting for packageInstalled callback");
- }
- }
- } finally {
- sm.unregisterListener(observer);
- }
- }
-
- public void testUnmount() throws Exception {
- boolean oldStatus = getMediaState();
- Log.i(TAG, "oldStatus="+oldStatus);
- try {
- // Mount media firsts
- if (!getMediaState()) {
- mountMedia();
- }
- unmountMedia();
- } finally {
- // Restore old status
- boolean currStatus = getMediaState();
- if (oldStatus != currStatus) {
- if (oldStatus) {
- // Mount media
- mountMedia();
- } else {
- unmountMedia();
- }
- }
- }
- }
-
- class MultipleStorageLis extends StorageListener {
- int count = 0;
- public void onStorageStateChanged(String path, String oldState, String newState) {
- count++;
- super.action();
- }
- }
- /*
- * This test invokes unmount multiple time and expects the call back
- * to be invoked just once.
- */
- public void testUnmountMultiple() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- boolean oldStatus = getMediaState();
- StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
- MultipleStorageLis observer = new MultipleStorageLis();
- try {
- // Mount media firsts
- if (!getMediaState()) {
- mountMedia();
- }
- String path = Environment.getExternalStorageDirectory().toString();
- sm.registerListener(observer);
- // Wait on observer
- synchronized(observer) {
- for (int i = 0; i < 5; i++) {
- getSm().unmountVolume(path, false, false);
- }
- long waitTime = 0;
- while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
- observer.wait(WAIT_TIME_INCR);
- waitTime += WAIT_TIME_INCR;
- }
- if(!observer.isDone()) {
- fail("Timed out waiting for packageInstalled callback");
- }
- }
- assertEquals(observer.count, 1);
- } finally {
- sm.unregisterListener(observer);
- // Restore old status
- boolean currStatus = getMediaState();
- if (oldStatus != currStatus) {
- if (oldStatus) {
- // Mount media
- mountMedia();
- } else {
- unmountMedia();
- }
- }
- }
- }
-
- class ShutdownObserver extends IStorageShutdownObserver.Stub{
- private boolean doneFlag = false;
- int statusCode;
-
- public void action() {
- synchronized (this) {
- doneFlag = true;
- notifyAll();
- }
- }
-
- public boolean isDone() {
- return doneFlag;
- }
- public void onShutDownComplete(int statusCode) throws RemoteException {
- this.statusCode = statusCode;
- action();
- }
-
- }
-
- void invokeShutdown() throws Exception {
- IStorageManager sm = getSm();
- ShutdownObserver observer = new ShutdownObserver();
- synchronized (observer) {
- sm.shutdown(observer);
- }
- }
-
- public void testShutdown() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- boolean oldStatus = getMediaState();
- try {
- // Mount media firsts
- if (!getMediaState()) {
- mountMedia();
- }
- invokeShutdown();
- } finally {
- // Restore old status
- boolean currStatus = getMediaState();
- if (oldStatus != currStatus) {
- if (oldStatus) {
- // Mount media
- mountMedia();
- } else {
- unmountMedia();
- }
- }
- }
- }
-
- /*
- * This test invokes unmount multiple time and expects the call back
- * to be invoked just once.
- */
- public void testShutdownMultiple() throws Exception {
- if (Environment.isExternalStorageEmulated()) {
- return;
- }
-
- boolean oldStatus = getMediaState();
- try {
- // Mount media firsts
- if (!getMediaState()) {
- mountMedia();
- }
- IStorageManager sm = getSm();
- ShutdownObserver observer = new ShutdownObserver();
- synchronized (observer) {
- sm.shutdown(observer);
- for (int i = 0; i < 4; i++) {
- sm.shutdown(null);
- }
- }
- } finally {
- // Restore old status
- boolean currStatus = getMediaState();
- if (oldStatus != currStatus) {
- if (oldStatus) {
- // Mount media
- mountMedia();
- } else {
- unmountMedia();
- }
- }
- }
- }
-
-}
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
index 90cb9a5..56629d4 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
@@ -18,24 +18,21 @@
import android.content.Context;
import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.os.Environment;
-import android.os.SystemClock;
import android.test.InstrumentationTestCase;
import android.util.Log;
-import android.os.Environment;
-import android.os.FileUtils;
-import android.os.storage.OnObbStateChangeListener;
-import android.os.storage.StorageManager;
+
+import libcore.io.Streams;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.FileReader;
-import java.io.InputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.io.StringReader;
public class StorageManagerBaseTest extends InstrumentationTestCase {
@@ -219,46 +216,21 @@
}
/**
- * Helper to copy a raw resource file to an actual specified file
- *
- * @param rawResId The raw resource ID of the OBB resource file
- * @param outFile A File representing the file we want to copy the OBB to
- * @throws NotFoundException If the resource file could not be found
- */
- private void copyRawToFile(int rawResId, File outFile) throws NotFoundException {
- Resources res = mContext.getResources();
- InputStream is = null;
- try {
- is = res.openRawResource(rawResId);
- } catch (NotFoundException e) {
- Log.i(LOG_TAG, "Failed to load resource with id: " + rawResId);
- throw e;
- }
- FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
- | FileUtils.S_IRWXO, -1, -1);
- assertTrue(FileUtils.copyToFile(is, outFile));
- FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
- | FileUtils.S_IRWXO, -1, -1);
- }
-
- /**
* Creates an OBB file (with the given name), into the app's standard files directory
*
* @param name The name of the OBB file we want to create/write to
* @param rawResId The raw resource ID of the OBB file in the package
* @return A {@link File} representing the file to write to
*/
- protected File createObbFile(String name, int rawResId) {
- File outFile = null;
- try {
- final File filesDir = mContext.getFilesDir();
- outFile = new File(filesDir, name);
- copyRawToFile(rawResId, outFile);
- } catch (NotFoundException e) {
- if (outFile != null) {
- outFile.delete();
- }
+ protected File createObbFile(String name, int rawResId) throws IOException, Resources.NotFoundException {
+ final File outFile = new File(mContext.getObbDir(), name);
+ outFile.delete();
+
+ try (InputStream in = mContext.getResources().openRawResource(rawResId);
+ OutputStream out = new FileOutputStream(outFile)) {
+ Streams.copy(in, out);
}
+
return outFile;
}
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
index fbba6ff..5f8bd03 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
@@ -16,61 +16,27 @@
package android.os.storage;
-import android.content.Context;
-import android.os.Environment;
-import android.os.ProxyFileDescriptorCallback;
import android.os.ParcelFileDescriptor;
+import android.os.ProxyFileDescriptorCallback;
import android.system.ErrnoException;
-import android.system.Os;
-import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;
import com.android.frameworks.coretests.R;
-import com.android.internal.os.FuseAppLoop;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.util.concurrent.ThreadFactory;
-import java.io.File;
-import java.io.FileInputStream;
-import junit.framework.AssertionFailedError;
+import java.io.File;
+import java.util.concurrent.ThreadFactory;
public class StorageManagerIntegrationTest extends StorageManagerBaseTest {
-
- private static String LOG_TAG = "StorageManagerBaseTest.StorageManagerIntegrationTest";
- protected File mFile = null;
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mContext = getInstrumentation().getContext();
- mFile = null;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void tearDown() throws Exception {
- if (mFile != null) {
- mFile.delete();
- mFile = null;
- }
- super.tearDown();
- }
+ private static String LOG_TAG = "StorageManagerIntegrationTest";
/**
* Tests mounting a single OBB file and verifies its contents.
*/
@LargeTest
- public void testMountSingleObb() {
- mFile = createObbFile(OBB_FILE_1, R.raw.obb_file1);
- String filePath = mFile.getAbsolutePath();
+ public void testMountSingleObb() throws Exception {
+ final File file = createObbFile(OBB_FILE_1, R.raw.obb_file1);
+ String filePath = file.getAbsolutePath();
mountObb(filePath);
verifyObb1Contents(filePath);
unmountObb(filePath, DONT_FORCE);
@@ -80,7 +46,7 @@
* Tests mounting several OBB files and verifies its contents.
*/
@LargeTest
- public void testMountMultipleObb() {
+ public void testMountMultipleObb() throws Exception {
File file1 = null;
File file2 = null;
File file3 = null;
@@ -120,9 +86,9 @@
* Tests mounting a single encrypted OBB file and verifies its contents.
*/
@LargeTest
- public void testMountSingleEncryptedObb() {
- mFile = createObbFile(OBB_FILE_3_ENCRYPTED, R.raw.obb_enc_file100_orig3);
- String filePath = mFile.getAbsolutePath();
+ public void testMountSingleEncryptedObb() throws Exception {
+ final File file = createObbFile(OBB_FILE_3_ENCRYPTED, R.raw.obb_enc_file100_orig3);
+ String filePath = file.getAbsolutePath();
mountObb(filePath, OBB_FILE_3_PASSWORD, OnObbStateChangeListener.MOUNTED);
verifyObb3Contents(filePath);
unmountObb(filePath, DONT_FORCE);
@@ -132,19 +98,17 @@
* Tests mounting a single encrypted OBB file using an invalid password.
*/
@LargeTest
- @Suppress // Failing.
- public void testMountSingleEncryptedObbInvalidPassword() {
- mFile = createObbFile("bad password@$%#@^*(!&)", R.raw.obb_enc_file100_orig3);
- String filePath = mFile.getAbsolutePath();
- mountObb(filePath, OBB_FILE_3_PASSWORD, OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
- unmountObb(filePath, DONT_FORCE);
+ public void testMountSingleEncryptedObbInvalidPassword() throws Exception {
+ final File file = createObbFile("bad password@$%#@^*(!&)", R.raw.obb_enc_file100_orig3);
+ String filePath = file.getAbsolutePath();
+ mountObb(filePath, OBB_FILE_1_PASSWORD, OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
}
/**
* Tests simultaneously mounting 2 encrypted OBBs with different keys and verifies contents.
*/
@LargeTest
- public void testMountTwoEncryptedObb() {
+ public void testMountTwoEncryptedObb() throws Exception {
File file3 = null;
File file1 = null;
try {
@@ -174,9 +138,9 @@
* Tests that we can not force unmount when a file is currently open on the OBB.
*/
@LargeTest
- public void testUnmount_DontForce() {
- mFile = createObbFile(OBB_FILE_1, R.raw.obb_file1);
- String obbFilePath = mFile.getAbsolutePath();
+ public void testUnmount_DontForce() throws Exception {
+ final File file = createObbFile(OBB_FILE_1, R.raw.obb_file1);
+ String obbFilePath = file.getAbsolutePath();
MountingObbThread mountingThread = new MountingObbThread(obbFilePath,
OBB_FILE_1_CONTENTS_1);
@@ -218,9 +182,9 @@
* Tests mounting a single OBB that isn't signed.
*/
@LargeTest
- public void testMountUnsignedObb() {
- mFile = createObbFile(OBB_FILE_2_UNSIGNED, R.raw.obb_file2_nosign);
- String filePath = mFile.getAbsolutePath();
+ public void testMountUnsignedObb() throws Exception {
+ final File file = createObbFile(OBB_FILE_2_UNSIGNED, R.raw.obb_file2_nosign);
+ String filePath = file.getAbsolutePath();
mountObb(filePath, OBB_FILE_2_UNSIGNED, OnObbStateChangeListener.ERROR_INTERNAL);
}
@@ -228,9 +192,9 @@
* Tests mounting a single OBB that is signed with a different package.
*/
@LargeTest
- public void testMountBadPackageNameObb() {
- mFile = createObbFile(OBB_FILE_3_BAD_PACKAGENAME, R.raw.obb_file3_bad_packagename);
- String filePath = mFile.getAbsolutePath();
+ public void testMountBadPackageNameObb() throws Exception {
+ final File file = createObbFile(OBB_FILE_3_BAD_PACKAGENAME, R.raw.obb_file3_bad_packagename);
+ String filePath = file.getAbsolutePath();
mountObb(filePath, OBB_FILE_3_BAD_PACKAGENAME,
OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
}
@@ -239,9 +203,9 @@
* Tests remounting a single OBB that has already been mounted.
*/
@LargeTest
- public void testRemountObb() {
- mFile = createObbFile(OBB_FILE_1, R.raw.obb_file1);
- String filePath = mFile.getAbsolutePath();
+ public void testRemountObb() throws Exception {
+ final File file = createObbFile(OBB_FILE_1, R.raw.obb_file1);
+ String filePath = file.getAbsolutePath();
mountObb(filePath);
verifyObb1Contents(filePath);
mountObb(filePath, null, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
@@ -291,4 +255,4 @@
return thread;
}
}
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 8395638..d9bcc57d 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -277,6 +277,7 @@
Settings.Global.NEW_CONTACT_AGGREGATOR,
Settings.Global.NITZ_UPDATE_DIFF,
Settings.Global.NITZ_UPDATE_SPACING,
+ Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
Settings.Global.NSD_ON,
Settings.Global.NTP_SERVER,
Settings.Global.NTP_TIMEOUT,
diff --git a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
index 5548e48..b0ec55d 100644
--- a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
+++ b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
@@ -21,6 +21,8 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
@@ -36,6 +38,8 @@
private static final String TEST_ID = "id";
private static final String TEST_TITLE = "title";
private static final String TEST_SUMMARY = "summary";
+
+ private Icon mIcon;
private PendingIntent mTestIntent;
@@ -44,6 +48,7 @@
final Context context = InstrumentationRegistry.getContext();
mTestIntent = PendingIntent.getActivity(context, 0 /* requestCode */,
new Intent(), 0 /* flags */);
+ mIcon = Icon.createWithBitmap(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888));
}
@Test
@@ -51,12 +56,15 @@
final Suggestion suggestion = new Suggestion.Builder(TEST_ID)
.setTitle(TEST_TITLE)
.setSummary(TEST_SUMMARY)
+ .setIcon(mIcon)
.setPendingIntent(mTestIntent)
.build();
assertThat(suggestion.getId()).isEqualTo(TEST_ID);
assertThat(suggestion.getTitle()).isEqualTo(TEST_TITLE);
assertThat(suggestion.getSummary()).isEqualTo(TEST_SUMMARY);
+ assertThat(suggestion.getIcon()).isEqualTo(mIcon);
+ assertThat(suggestion.getFlags()).isEqualTo(0);
assertThat(suggestion.getPendingIntent()).isEqualTo(mTestIntent);
}
@@ -66,6 +74,7 @@
.setTitle(TEST_TITLE)
.setSummary(TEST_SUMMARY)
.setPendingIntent(mTestIntent)
+ .setIcon(mIcon)
.build();
}
@@ -75,6 +84,8 @@
final Suggestion oldSuggestion = new Suggestion.Builder(TEST_ID)
.setTitle(TEST_TITLE)
.setSummary(TEST_SUMMARY)
+ .setIcon(mIcon)
+ .setFlags(Suggestion.FLAG_HAS_BUTTON)
.setPendingIntent(mTestIntent)
.build();
@@ -85,6 +96,9 @@
assertThat(newSuggestion.getId()).isEqualTo(TEST_ID);
assertThat(newSuggestion.getTitle()).isEqualTo(TEST_TITLE);
assertThat(newSuggestion.getSummary()).isEqualTo(TEST_SUMMARY);
+ assertThat(newSuggestion.getIcon().toString()).isEqualTo(mIcon.toString());
+ assertThat(newSuggestion.getFlags())
+ .isEqualTo(Suggestion.FLAG_HAS_BUTTON);
assertThat(newSuggestion.getPendingIntent()).isEqualTo(mTestIntent);
}
}
diff --git a/core/tests/coretests/src/android/text/DynamicLayoutTest.java b/core/tests/coretests/src/android/text/DynamicLayoutTest.java
index ed6bfbf..639cefb 100644
--- a/core/tests/coretests/src/android/text/DynamicLayoutTest.java
+++ b/core/tests/coretests/src/android/text/DynamicLayoutTest.java
@@ -259,4 +259,21 @@
assertEquals(2 * textSize, layout.getLineDescent(2));
}
}
+
+ @Test
+ public void testBuilder_defaultTextDirection() {
+ final DynamicLayout.Builder builder = DynamicLayout.Builder
+ .obtain("", new TextPaint(), WIDTH);
+ final DynamicLayout layout = builder.build();
+ assertEquals(TextDirectionHeuristics.FIRSTSTRONG_LTR, layout.getTextDirectionHeuristic());
+ }
+
+ @Test
+ public void testBuilder_setTextDirection() {
+ final DynamicLayout.Builder builder = DynamicLayout.Builder
+ .obtain("", new TextPaint(), WIDTH)
+ .setTextDirection(TextDirectionHeuristics.ANYRTL_LTR);
+ final DynamicLayout layout = builder.build();
+ assertEquals(TextDirectionHeuristics.ANYRTL_LTR, layout.getTextDirectionHeuristic());
+ }
}
diff --git a/core/tests/coretests/src/android/text/LineBreakingOverhangsTest.java b/core/tests/coretests/src/android/text/LineBreakingOverhangsTest.java
new file mode 100644
index 0000000..4f18b0b4
--- /dev/null
+++ b/core/tests/coretests/src/android/text/LineBreakingOverhangsTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2012 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.text;
+
+import static org.junit.Assert.assertEquals;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Typeface;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LineBreakingOverhangsTest {
+ private static final int EM = 100; // Make 1em == 100px.
+ private static final TextPaint sTextPaint = new TextPaint();
+
+ static {
+ // The test font has following coverage and overhangs.
+ // All the characters have a width of 1em.
+ // space: no overhangs
+ // R: 4em overhang on the right
+ // a: no overhang
+ // y: 1.5em overhang on the left
+ sTextPaint.setTypeface(Typeface.createFromAsset(
+ InstrumentationRegistry.getTargetContext().getAssets(),
+ "fonts/LineBreakingOverhangsTestFont.ttf"));
+ sTextPaint.setTextSize(EM);
+ }
+
+ private static void layout(@NonNull CharSequence source, @NonNull int[] breaks, double width,
+ @Nullable int[] leftPadding, @Nullable int[] rightPadding) {
+ layout(source, breaks, width, leftPadding, rightPadding, null /* indents */);
+ }
+
+ private static void layout(@NonNull CharSequence source, @NonNull int[] breaks, double width,
+ @Nullable int[] leftPadding, @Nullable int[] rightPadding, @Nullable int[] indents) {
+ final StaticLayout staticLayout = StaticLayout.Builder
+ .obtain(source, 0, source.length(), sTextPaint, (int) width)
+ .setAvailablePaddings(leftPadding, rightPadding)
+ .setIndents(indents, indents)
+ .build();
+
+ final int lineCount = breaks.length + 1;
+ assertEquals("Number of lines", lineCount, staticLayout.getLineCount());
+
+ for (int line = 0; line < lineCount; line++) {
+ final int lineStart = staticLayout.getLineStart(line);
+ final int lineEnd = staticLayout.getLineEnd(line);
+
+ if (line == 0) {
+ assertEquals("Line start for first line", 0, lineStart);
+ } else {
+ assertEquals("Line start for line " + line, breaks[line - 1], lineStart);
+ }
+
+ if (line == lineCount - 1) {
+ assertEquals("Line end for last line", source.length(), lineEnd);
+ } else {
+ assertEquals("Line end for line " + line, breaks[line], lineEnd);
+ }
+ }
+ }
+
+ private static final int[] NO_BREAK = new int[] {};
+
+ private static final int[] NO_PADDING = null;
+ // Maximum needed for left side of 'y'.
+ private static final int[] FULL_LEFT_PADDING = new int[] {(int) (1.5 * EM)};
+ // Maximum padding needed for right side of 'R'.
+ private static final int[] FULL_RIGHT_PADDING = new int[] {4 * EM};
+
+ private static final int[] ONE_EM_PADDING = new int[] {1 * EM};
+ private static final int[] HALF_EM_PADDING = new int[] {(int) (0.5 * EM)};
+ private static final int[] QUARTER_EM_PADDING = new int[] {(int) (0.25 * EM)};
+
+ @Test
+ public void testRightOverhang() {
+ // The advance of "aaa R" is 5em, but the right-side overhang of 'R' would need 4em more, so
+ // we break the line if there's not enough overhang.
+
+ // Enough right padding, so the whole line fits in 5em.
+ layout("aaa R", NO_BREAK, 5 * EM, NO_PADDING, FULL_RIGHT_PADDING);
+
+ // No right padding, so we'd need 9em to fit the advance and the right padding of 'R'.
+ layout("aaa R", new int[] {4}, 8.9 * EM, NO_PADDING, NO_PADDING);
+ layout("aaa R", NO_BREAK, 9 * EM, NO_PADDING, NO_PADDING);
+
+ // 1em of right padding means we can fit the string in 8em.
+ layout("aaa R", new int[] {4}, 7.9 * EM, NO_PADDING, ONE_EM_PADDING);
+ layout("aaa R", NO_BREAK, 8 * EM, NO_PADDING, ONE_EM_PADDING);
+ }
+
+ @Test
+ public void testLeftOverhang() {
+ // The advance of "y a" is 3em, but the left-side overhang of 'y' would need 1.5em more, so
+ // we break the line if there's not enough overhang.
+
+ // Enough left padding, so the whole line fits in 3em.
+ layout("y a", NO_BREAK, 3 * EM, FULL_LEFT_PADDING, NO_PADDING);
+
+ // No right padding, so we'd need 4.5em to fit the advance and the left padding of 'y'.
+ layout("y a", new int[] {2}, 4.4 * EM, NO_PADDING, NO_PADDING);
+ layout("y a", NO_BREAK, 4.5 * EM, NO_PADDING, NO_PADDING);
+
+ // 1em of left padding means we can fit the string in 3.5em.
+ layout("y a", new int[] {2}, 3.4 * EM, ONE_EM_PADDING, NO_PADDING);
+ layout("y a", NO_BREAK, 3.5 * EM, ONE_EM_PADDING, NO_PADDING);
+ }
+
+ @Test
+ public void testBothSidesOverhang() {
+ // The advance of "y a R" is 5em, but the left-side overhang of 'y' would need 1.5em more,
+ // and the right side overhang or 'R' would need 4em more, so we break the line if there's
+ // not enough overhang.
+
+ // Enough padding, so the whole line fits in 5em.
+ layout("y a R", NO_BREAK, 5 * EM, FULL_LEFT_PADDING, FULL_RIGHT_PADDING);
+
+ // No padding, so we'd need 10.5em to fit the advance and the paddings.
+ layout("y a R", new int[] {4}, 10.4 * EM, NO_PADDING, NO_PADDING);
+ layout("y a R", NO_BREAK, 10.5 * EM, NO_PADDING, NO_PADDING);
+
+ // 1em of padding on each side means we can fit the string in 8.5em.
+ layout("y a R", new int[] {4}, 8.4 * EM, ONE_EM_PADDING, ONE_EM_PADDING);
+ layout("y a R", NO_BREAK, 8.5 * EM, ONE_EM_PADDING, ONE_EM_PADDING);
+ }
+
+ @Test
+ public void testIndentsDontAffectPaddings() {
+ // This is identical to the previous test, except that it applies wide indents of 4em on
+ // each side and thus needs an extra 8em of width. This test makes sure that indents and
+ // paddings are independent.
+ final int[] indents = new int[] {4 * EM};
+ final int indentAdj = 8 * EM;
+
+ // Enough padding, so the whole line fits in 5em.
+ layout("y a R", NO_BREAK, 5 * EM + indentAdj, FULL_LEFT_PADDING, FULL_RIGHT_PADDING,
+ indents);
+
+ // No padding, so we'd need 10.5em to fit the advance and the paddings.
+ layout("y a R", new int[] {4}, 10.4 * EM + indentAdj, NO_PADDING, NO_PADDING, indents);
+ layout("y a R", NO_BREAK, 10.5 * EM + indentAdj, NO_PADDING, NO_PADDING, indents);
+
+ // 1em of padding on each side means we can fit the string in 8.5em.
+ layout("y a R", new int[] {4}, 8.4 * EM + indentAdj, ONE_EM_PADDING, ONE_EM_PADDING,
+ indents);
+ layout("y a R", NO_BREAK, 8.5 * EM + indentAdj, ONE_EM_PADDING, ONE_EM_PADDING, indents);
+ }
+}
diff --git a/core/tests/coretests/src/android/text/StaticLayoutLineBreakingTest.java b/core/tests/coretests/src/android/text/StaticLayoutLineBreakingTest.java
index 2ee855e..f7dbafa 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutLineBreakingTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutLineBreakingTest.java
@@ -18,6 +18,9 @@
import static org.junit.Assert.assertEquals;
+import android.content.Context;;
+import android.graphics.Typeface;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.Layout.Alignment;
@@ -45,57 +48,24 @@
private static final int[] NO_BREAK = new int[] {};
- private static final TextPaint sTextPaint = new MockTextPaint();
+ private static final TextPaint sTextPaint = new TextPaint();
- private static class MockTextPaint extends TextPaint {
-
- @Override
- public float getTextRunAdvances(char[] chars, int index, int count,
- int contextIndex, int contextCount, boolean isRtl, float[] advances,
- int advancesIndex) {
-
- // Conditions copy pasted from Paint
- if (chars == null) {
- throw new IllegalArgumentException("text cannot be null");
- }
-
- if ((index | count | contextIndex | contextCount | advancesIndex
- | (index - contextIndex) | (contextCount - count)
- | ((contextIndex + contextCount) - (index + count))
- | (chars.length - (contextIndex + contextCount))
- | (advances == null ? 0 :
- (advances.length - (advancesIndex + count)))) < 0) {
- throw new IndexOutOfBoundsException();
- }
-
- float res = 0.0f;
-
- if (advances != null) {
- for (int i = 0; i < count; i++) {
- float width = getCharWidth(chars[index + i]);
- advances[advancesIndex + i] = width;
- res += width;
- }
- }
-
- return res;
- }
- }
-
- private static float getCharWidth(char c) {
- switch (Character.toUpperCase(c)) {
- // Roman figures
- case 'I': return 1.0f;
- case 'V': return 5.0f;
- case 'X': return 10.0f;
- case 'L': return 50.0f;
- case 'C': return 100.0f; // equals to WIDTH
- case ' ': return 10.0f;
- case '_': return 0.0f; // 0-width character
- case SURR_FIRST: return 7.0f;
- case SURR_SECOND: return 3.0f; // Sum of SURR_FIRST-SURR_SECOND is 10
- default: return 10.0f;
- }
+ static {
+ // The test font has following coverage and width.
+ // U+0020: 10em
+ // U+002E (.): 10em
+ // U+0043 (C): 100em
+ // U+0049 (I): 1em
+ // U+004C (L): 50em
+ // U+0056 (V): 5em
+ // U+0058 (X): 10em
+ // U+005F (_): 0em
+ // U+FFFD (invalid surrogate will be replaced to this): 7em
+ // U+10331 (\uD800\uDF31): 10em
+ Context context = InstrumentationRegistry.getTargetContext();
+ sTextPaint.setTypeface(Typeface.createFromAsset(context.getAssets(),
+ "fonts/StaticLayoutLineBreakingTestFont.ttf"));
+ sTextPaint.setTextSize(1.0f); // Make 1em == 1px.
}
private static StaticLayout getStaticLayout(CharSequence source, int width) {
@@ -406,13 +376,13 @@
public void testWithSurrogate() {
layout("LX" + SURR_FIRST + SURR_SECOND, NO_BREAK);
layout("LXXXX" + SURR_FIRST + SURR_SECOND, NO_BREAK);
- // LXXXXI (91) + SURR_FIRST (7) fits. But we should not break the surrogate pair
- // Bug: surrogate pair is broken, should be 6 (breaking after the 'V')
- // Maybe not: may be ok if the second character of a pair always has a 0-width
- layout("LXXXXI" + SURR_FIRST + SURR_SECOND, new int[] {7});
+ // LXXXXI (91) + SURR_FIRST + SURR_SECOND (10). Do not break in the middle point of
+ // surrogatge pair.
+ layout("LXXXXI" + SURR_FIRST + SURR_SECOND, new int[] {6});
- // LXXXXI (95) + SURR_SECOND (3) fits, but this is not a valid surrogate pair, breaking it
- layout("LXXXXV" + SURR_SECOND + SURR_FIRST, new int[] {7});
+ // LXXXXI (91) + SURR_SECOND (replaced with REPLACEMENT CHARACTER. width is 7px) fits.
+ // Break just after invalid trailing surrogate.
+ layout("LXXXXI" + SURR_SECOND + SURR_FIRST, new int[] {7});
layout("C" + SURR_FIRST + SURR_SECOND, new int[] {1});
}
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index defb44a..078ed76 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -72,7 +72,7 @@
}
@Test
- public void testBuilder() {
+ public void testBuilder_textDirection() {
{
// Obtain.
final StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
@@ -88,8 +88,7 @@
LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
builder.setTextDirection(TextDirectionHeuristics.RTL);
final StaticLayout layout = builder.build();
- // Always returns TextDirectionHeuristics.FIRSTSTRONG_LTR.
- assertEquals(TextDirectionHeuristics.FIRSTSTRONG_LTR,
+ assertEquals(TextDirectionHeuristics.RTL,
layout.getTextDirectionHeuristic());
}
}
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index d26437e..ddf9876 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -20,7 +20,9 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
@@ -382,4 +384,19 @@
RemoteViews.CREATOR.createFromParcel(p);
p.recycle();
}
+
+ @Test
+ public void copyWithBinders() throws Exception {
+ RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
+ for (int i = 1; i < 10; i++) {
+ PendingIntent pi = PendingIntent.getBroadcast(mContext, 0,
+ new Intent("android.widget.RemoteViewsTest_" + i), PendingIntent.FLAG_ONE_SHOT);
+ views.setOnClickPendingIntent(i, pi);
+ }
+ try {
+ new RemoteViews(views);
+ } catch (Throwable t) {
+ throw new Exception(t);
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java b/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java
index b881053..28a3b67 100644
--- a/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java
+++ b/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java
@@ -18,6 +18,8 @@
import static org.junit.Assert.assertEquals;
+import static java.util.function.Function.identity;
+
import android.graphics.PointF;
import android.graphics.RectF;
@@ -105,7 +107,7 @@
final PointF point = new PointF(pointX, pointY);
final PointF adjustedPoint =
SelectionActionModeHelper.movePointInsideNearestRectangle(point,
- mRectFList);
+ mRectFList, identity());
assertEquals(expectedPointX, adjustedPoint.x, 0.0f);
assertEquals(expectedPointY, adjustedPoint.y, 0.0f);
@@ -254,7 +256,8 @@
final List<RectF> result = new ArrayList<>();
final int size = inputRectangles.length;
for (int index = 0; index < size; ++index) {
- SelectionActionModeHelper.mergeRectangleIntoList(result, inputRectangles[index]);
+ SelectionActionModeHelper.mergeRectangleIntoList(result, inputRectangles[index],
+ identity(), identity());
}
assertEquals(expectedOutput, result);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
index eff6ad9..c611a01 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
@@ -22,6 +22,7 @@
import android.os.WorkSource;
import android.support.test.filters.SmallTest;
import android.util.ArrayMap;
+import android.view.Display;
import junit.framework.TestCase;
@@ -44,7 +45,7 @@
// Off-battery, non-existent
clocks.realtime = clocks.uptime = 10;
cur = clocks.realtime * 1000;
- bi.updateTimeBasesLocked(false, false, cur, cur); // off battery
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, cur, cur); // off battery
assertFalse(bgtb.isRunning());
assertEquals(0, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED));
@@ -65,7 +66,7 @@
// On-battery, background
clocks.realtime = clocks.uptime = 303;
cur = clocks.realtime * 1000;
- bi.updateTimeBasesLocked(true, false, cur, cur); // on battery
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, cur, cur); // on battery
// still in ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
assertTrue(bgtb.isRunning());
assertEquals(0, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED));
@@ -73,7 +74,7 @@
// On-battery, background - but change screen state
clocks.realtime = clocks.uptime = 409;
cur = clocks.realtime * 1000;
- bi.updateTimeBasesLocked(true, true, cur, cur); // on battery (again)
+ bi.updateTimeBasesLocked(true, Display.STATE_OFF, cur, cur); // on battery (again)
assertTrue(bgtb.isRunning());
assertEquals(106_000, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED));
@@ -87,7 +88,7 @@
// Off-battery, foreground
clocks.realtime = clocks.uptime = 530;
cur = clocks.realtime * 1000;
- bi.updateTimeBasesLocked(false, false, cur, cur); // off battery
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, cur, cur); // off battery
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
assertFalse(bgtb.isRunning());
assertEquals(227_000, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED));
@@ -112,17 +113,17 @@
// battery=off, screen=off, background=off
cur = (clocks.realtime = clocks.uptime = 100) * 1000;
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
- bi.updateTimeBasesLocked(false, false, cur, cur);
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, cur, cur);
assertFalse(bgtb.isRunning());
// battery=on, screen=off, background=off
cur = (clocks.realtime = clocks.uptime = 200) * 1000;
- bi.updateTimeBasesLocked(true, false, cur, cur);
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, cur, cur);
assertFalse(bgtb.isRunning());
// battery=on, screen=on, background=off
cur = (clocks.realtime = clocks.uptime = 300) * 1000;
- bi.updateTimeBasesLocked(true, true, cur, cur);
+ bi.updateTimeBasesLocked(true, Display.STATE_OFF, cur, cur);
assertFalse(bgtb.isRunning());
// battery=on, screen=on, background=on
@@ -133,12 +134,12 @@
// battery=on, screen=off, background=on
cur = (clocks.realtime = clocks.uptime = 550) * 1000;
- bi.updateTimeBasesLocked(true, false, cur, cur);
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, cur, cur);
assertFalse(bgtb.isRunning());
// battery=off, screen=off, background=on
cur = (clocks.realtime = clocks.uptime = 660) * 1000;
- bi.updateTimeBasesLocked(false, false, cur, cur);
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, cur, cur);
assertFalse(bgtb.isRunning());
// battery=off, screen=off, background=off
@@ -157,7 +158,7 @@
// On battery
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
// App in foreground
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
@@ -171,7 +172,7 @@
// Off battery
curr = 1000 * (clocks.realtime = clocks.uptime = 305);
- bi.updateTimeBasesLocked(false, false, curr, curr); // off battery
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr); // off battery
// Stop timer
curr = 1000 * (clocks.realtime = clocks.uptime = 409);
@@ -200,7 +201,7 @@
// On battery
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
// App in foreground
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
@@ -215,7 +216,7 @@
// Off battery
curr = 1000 * (clocks.realtime = clocks.uptime = 305);
- bi.updateTimeBasesLocked(false, false, curr, curr); // off battery
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr); // off battery
// Start timer (unoptimized)
curr = 1000 * (clocks.realtime = clocks.uptime = 1000);
@@ -223,7 +224,7 @@
// On battery
curr = 1000 * (clocks.realtime = clocks.uptime = 2001);
- bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
// Move to foreground
curr = 1000 * (clocks.realtime = clocks.uptime = 3004);
@@ -270,7 +271,7 @@
// On battery
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
// App in foreground
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
@@ -292,7 +293,7 @@
// Off battery
curr = 1000 * (clocks.realtime = clocks.uptime = 305);
- bi.updateTimeBasesLocked(false, false, curr, curr); // off battery
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr); // off battery
// Stop timer
curr = 1000 * (clocks.realtime = clocks.uptime = 409);
@@ -331,7 +332,7 @@
// On battery
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
// App in foreground
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
@@ -353,7 +354,7 @@
// Off battery
curr = 1000 * (clocks.realtime = clocks.uptime = 305);
- bi.updateTimeBasesLocked(false, false, curr, curr); // off battery
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr); // off battery
// Stop timer
curr = 1000 * (clocks.realtime = clocks.uptime = 409);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index 97b54b1..6ff0ab2 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -39,6 +39,7 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.SparseLongArray;
+import android.view.Display;
import com.android.internal.util.ArrayUtils;
@@ -179,7 +180,7 @@
@Test
public void testUpdateClusterSpeedTimes() {
// PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final long[][] clusterSpeedTimesMs = {{20, 30}, {40, 50, 60}};
initKernelCpuSpeedReaders(clusterSpeedTimesMs.length);
for (int i = 0; i < clusterSpeedTimesMs.length; ++i) {
@@ -224,7 +225,7 @@
@Test
public void testReadKernelUidCpuTimesLocked() {
//PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int[] testUids = getUids(testUserId, new int[] {
@@ -295,7 +296,7 @@
@Test
public void testReadKernelUidCpuTimesLocked_isolatedUid() {
//PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int isolatedAppId = FIRST_ISOLATED_UID + 27;
@@ -382,7 +383,7 @@
@Test
public void testReadKernelUidCpuTimesLocked_invalidUid() {
//PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
final int invalidUserId = 15;
final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
@@ -427,7 +428,7 @@
@Test
public void testReadKernelUidCpuTimesLocked_withPartialTimers() {
//PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int[] testUids = getUids(testUserId, new int[] {
@@ -509,7 +510,7 @@
@Test
public void testReadKernelUidCpuFreqTimesLocked() {
// PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -550,7 +551,7 @@
// Repeat the test when the screen is off.
// PRECONDITIONS
- updateTimeBasesLocked(true, true, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
final long[][] deltasMs = {
{3, 12, 55, 100, 32},
{3248327490475l, 232349349845043l, 123, 2398, 0},
@@ -584,7 +585,7 @@
@Test
public void testReadKernelUidCpuFreqTimesLocked_perClusterTimesAvailable() {
// PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -644,7 +645,7 @@
// Repeat the test when the screen is off.
// PRECONDITIONS
- updateTimeBasesLocked(true, true, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
final long[][] deltasMs = {
{3, 12, 55, 100, 32},
{3248327490475l, 232349349845043l, 123, 2398, 0},
@@ -688,7 +689,7 @@
@Test
public void testReadKernelUidCpuFreqTimesLocked_partialTimers() {
// PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -792,7 +793,7 @@
@Test
public void testReadKernelUidCpuFreqTimesLocked_freqsChanged() {
// PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -833,7 +834,7 @@
// Repeat the test with the freqs from proc file changed.
// PRECONDITIONS
- updateTimeBasesLocked(true, true, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
final long[][] deltasMs = {
{3, 12, 55, 100, 32, 34984, 27983},
{3248327490475l, 232349349845043l, 123, 2398, 0, 398, 0},
@@ -867,7 +868,7 @@
@Test
public void testReadKernelUidCpuFreqTimesLocked_isolatedUid() {
// PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -961,7 +962,7 @@
@Test
public void testReadKernelUiidCpuFreqTimesLocked_invalidUid() {
// PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
final int invalidUserId = 15;
@@ -1008,12 +1009,12 @@
verify(mKernelUidCpuFreqTimeReader).removeUid(invalidUid);
}
- private void updateTimeBasesLocked(boolean unplugged, boolean screenOff,
+ private void updateTimeBasesLocked(boolean unplugged, int screenState,
long upTime, long realTime) {
// Set PowerProfile=null before calling updateTimeBasesLocked to avoid execution of
// BatteryStatsImpl.updateCpuTimeLocked
mBatteryStatsImpl.setPowerProfile(null);
- mBatteryStatsImpl.updateTimeBasesLocked(unplugged, screenOff, upTime, realTime);
+ mBatteryStatsImpl.updateTimeBasesLocked(unplugged, screenState, upTime, realTime);
mBatteryStatsImpl.setPowerProfile(mPowerProfile);
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 7769e69..461d537 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -19,9 +19,11 @@
import static android.os.BatteryStats.WAKE_TYPE_PARTIAL;
import android.app.ActivityManager;
+import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.WorkSource;
import android.support.test.filters.SmallTest;
+import android.view.Display;
import junit.framework.TestCase;
@@ -50,7 +52,7 @@
@SmallTest
public void testNoteBluetoothScanResultLocked() throws Exception {
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(new MockClocks());
- bi.updateTimeBasesLocked(true, true, 0, 0);
+ bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
bi.noteBluetoothScanResultsFromSourceLocked(WS, 1);
@@ -82,7 +84,7 @@
int pid = 10;
String name = "name";
- bi.updateTimeBasesLocked(true, true, 0, 0);
+ bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
bi.getUidStatsLocked(UID).noteStartWakeLocked(pid, name, WAKE_TYPE_PARTIAL, clocks.realtime);
@@ -124,7 +126,7 @@
stateRuntimeMap.put(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT, 5309);
stateRuntimeMap.put(ActivityManager.PROCESS_STATE_CACHED_EMPTY, 42);
- bi.updateTimeBasesLocked(true, false, 0, 0);
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
for (Map.Entry<Integer, Integer> entry : stateRuntimeMap.entrySet()) {
bi.noteUidProcessStateLocked(UID, entry.getKey());
@@ -189,4 +191,46 @@
expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
}
+
+ /** Test BatteryStatsImpl.updateTimeBasesLocked. */
+ @SmallTest
+ public void testUpdateTimeBasesLocked() throws Exception {
+ final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+ bi.updateTimeBasesLocked(false, Display.STATE_OFF, 0, 0);
+ assertFalse(bi.getOnBatteryTimeBase().isRunning());
+ bi.updateTimeBasesLocked(false, Display.STATE_DOZE, 10, 10);
+ assertFalse(bi.getOnBatteryTimeBase().isRunning());
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, 20, 20);
+ assertFalse(bi.getOnBatteryTimeBase().isRunning());
+
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, 30, 30);
+ assertTrue(bi.getOnBatteryTimeBase().isRunning());
+ assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning());
+ bi.updateTimeBasesLocked(true, Display.STATE_DOZE, 40, 40);
+ assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning());
+ bi.updateTimeBasesLocked(true, Display.STATE_OFF, 40, 40);
+ assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning());
+ }
+
+ /** Test BatteryStatsImpl.noteScreenStateLocked. */
+ @SmallTest
+ public void testNoteScreenStateLocked() throws Exception {
+ final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+ bi.noteScreenStateLocked(Display.STATE_ON);
+ bi.noteScreenStateLocked(Display.STATE_DOZE);
+ assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning());
+ assertEquals(bi.getScreenState(), Display.STATE_DOZE);
+ bi.noteScreenStateLocked(Display.STATE_ON);
+ assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning());
+ assertEquals(bi.getScreenState(), Display.STATE_ON);
+ bi.noteScreenStateLocked(Display.STATE_OFF);
+ assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning());
+ assertEquals(bi.getScreenState(), Display.STATE_OFF);
+ }
+
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
index 0294095..a751f90 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.os.BatteryStats;
import android.support.test.filters.SmallTest;
+import android.view.Display;
import junit.framework.TestCase;
@@ -74,7 +75,7 @@
// Plugged-in (battery=off, sensor=off)
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(false, false, curr, curr);
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr);
// Start sensor (battery=off, sensor=on)
@@ -110,7 +111,7 @@
// Unplugged (battery=on, sensor=off)
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(true, false, curr, curr);
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr);
// Start sensor (battery=on, sensor=on)
curr = 1000 * (clocks.realtime = clocks.uptime = 200);
@@ -145,7 +146,7 @@
// On battery (battery=on, sensor=off)
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(true, false, curr, curr);
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
// Start sensor (battery=on, sensor=on)
@@ -154,7 +155,7 @@
// Off battery (battery=off, sensor=on)
curr = 1000 * (clocks.realtime = clocks.uptime = 305);
- bi.updateTimeBasesLocked(false, false, curr, curr);
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr);
// Stop sensor while off battery (battery=off, sensor=off)
curr = 1000 * (clocks.realtime = clocks.uptime = 409);
@@ -191,7 +192,7 @@
// Plugged-in (battery=off, sensor=off)
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(false, false, curr, curr);
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr);
// Start sensor (battery=off, sensor=on)
curr = 1000 * (clocks.realtime = clocks.uptime = 200);
@@ -209,7 +210,7 @@
// Unplug (battery=on, sensor=on)
curr = 1000 * (clocks.realtime = clocks.uptime = 305);
- bi.updateTimeBasesLocked(true, false, curr, curr);
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr);
//Test situation
curr = 1000 * (clocks.realtime = clocks.uptime = 410);
@@ -243,7 +244,7 @@
long curr = 0; // realtime in us
// Entire test is on-battery
curr = 1000 * (clocks.realtime = clocks.uptime = 1000);
- bi.updateTimeBasesLocked(true, false, curr, curr);
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr);
// See below for a diagram of events.
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 73ee3e5..63d1e5a 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -28,6 +28,10 @@
MockBatteryStatsImpl(Clocks clocks) {
super(clocks);
this.clocks = mClocks;
+ mScreenOnTimer = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null,
+ mOnBatteryTimeBase);
+ mScreenDozeTimer = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null,
+ mOnBatteryTimeBase);
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
}
@@ -39,6 +43,14 @@
return mOnBatteryTimeBase;
}
+ public TimeBase getOnBatteryScreenOffTimeBase() {
+ return mOnBatteryScreenOffTimeBase;
+ }
+
+ public int getScreenState() {
+ return mScreenState;
+ }
+
public boolean isOnBattery() {
return mForceOnBattery ? true : super.isOnBattery();
}
diff --git a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
index 8fee1d1..ebfa45c 100644
--- a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
+++ b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
@@ -16,6 +16,7 @@
package android.util;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -65,6 +66,18 @@
}
@Test
+ public void testSetEnabled_shouldSetOverrideFlag() {
+ assertFalse(FeatureFlagUtils.isEnabled(TEST_FEATURE_NAME));
+
+ FeatureFlagUtils.setEnabled(TEST_FEATURE_NAME, true);
+
+ assertEquals(SystemProperties.get(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, null),
+ "");
+ assertTrue(Boolean.parseBoolean(SystemProperties.get(
+ FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + TEST_FEATURE_NAME, "")));
+ }
+
+ @Test
public void testGetFlag_notSet_shouldReturnFalse() {
assertFalse(FeatureFlagUtils.isEnabled(TEST_FEATURE_NAME));
}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
index 2784193..c5e112b 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
@@ -33,29 +33,17 @@
$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
- -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-
-#################################
-include $(BUILD_SYSTEM)/configure_local_jack.mk
-#################################
-
-ifdef LOCAL_JACK_ENABLED
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
-endif
LOCAL_MIN_SDK_VERSION := 8
include $(BUILD_PACKAGE)
-ifndef LOCAL_JACK_ENABLED
$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
$(hide) mkdir -p $(dir $@)
$(MAINDEXCLASSES) $< 1>$@
echo "com/android/multidexlegacytestapp/Test.class" >> $@
$(built_dex_intermediate): $(mainDexList)
-endif
## The application with a full main dex
include $(CLEAR_VARS)
@@ -76,26 +64,14 @@
$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList2)
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=multidex -D jack.preprocessor=true\
- -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-
-#################################
-include $(BUILD_SYSTEM)/configure_local_jack.mk
-#################################
-
-ifdef LOCAL_JACK_ENABLED
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
-endif
LOCAL_MIN_SDK_VERSION := 8
include $(BUILD_PACKAGE)
-ifndef LOCAL_JACK_ENABLED
$(mainDexList2): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
$(hide) mkdir -p $(dir $@)
$(MAINDEXCLASSES) $< 1>$@
echo "com/android/multidexlegacytestapp/Test.class" >> $@
$(built_dex_intermediate): $(mainDexList2)
-endif
\ No newline at end of file
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/test.jpp b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/test.jpp
deleted file mode 100644
index a1f5656..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/test.jpp
+++ /dev/null
@@ -1,3 +0,0 @@
-test:
- @@com.android.jack.annotations.ForceInMainDex
- class com.android.multidexlegacytestapp.Test
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
index 1c7d807..da48df9 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
@@ -32,24 +32,12 @@
$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
- -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-
-#################################
-include $(BUILD_SYSTEM)/configure_local_jack.mk
-#################################
-
-ifdef LOCAL_JACK_ENABLED
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
-endif
include $(BUILD_PACKAGE)
-ifndef LOCAL_JACK_ENABLED
$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
$(hide) mkdir -p $(dir $@)
$(MAINDEXCLASSES) $< 1>$@
echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
$(built_dex_intermediate): $(mainDexList)
-endif
\ No newline at end of file
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/test.jpp b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/test.jpp
deleted file mode 100644
index 6d384e3..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/test.jpp
+++ /dev/null
@@ -1,3 +0,0 @@
-test:
- @@com.android.jack.annotations.ForceInMainDex
- class com.android.framework.multidexlegacyversionedtestapp.MultiDexUpdateTest
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
index b77cf31..02b3f53 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
@@ -32,24 +32,12 @@
$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
- -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-
-#################################
-include $(BUILD_SYSTEM)/configure_local_jack.mk
-#################################
-
-ifdef LOCAL_JACK_ENABLED
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
-endif
include $(BUILD_PACKAGE)
-ifndef LOCAL_JACK_ENABLED
$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
$(hide) mkdir -p $(dir $@)
$(MAINDEXCLASSES) $< 1>$@
echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
$(built_dex_intermediate): $(mainDexList)
-endif
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/test.jpp b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/test.jpp
deleted file mode 100644
index 6d384e3..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/test.jpp
+++ /dev/null
@@ -1,3 +0,0 @@
-test:
- @@com.android.jack.annotations.ForceInMainDex
- class com.android.framework.multidexlegacyversionedtestapp.MultiDexUpdateTest
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
index 3631626..4808684 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
@@ -32,25 +32,13 @@
LOCAL_DEX_PREOPT := false
LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
- -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-
-#################################
-include $(BUILD_SYSTEM)/configure_local_jack.mk
-#################################
-
-ifdef LOCAL_JACK_ENABLED
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
-endif
include $(BUILD_PACKAGE)
-ifndef LOCAL_JACK_ENABLED
$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
$(hide) mkdir -p $(dir $@)
$(MAINDEXCLASSES) $< 1>$@
echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
$(built_dex_intermediate): $(mainDexList)
-endif
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/test.jpp b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/test.jpp
deleted file mode 100644
index 6d384e3..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/test.jpp
+++ /dev/null
@@ -1,3 +0,0 @@
-test:
- @@com.android.jack.annotations.ForceInMainDex
- class com.android.framework.multidexlegacyversionedtestapp.MultiDexUpdateTest
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 7a2df85..9948f7e 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -64,7 +64,7 @@
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
</privapp-permissions>
- <privapp-permissions package="com.android.launcher">
+ <privapp-permissions package="com.android.launcher3">
<permission name="android.permission.BIND_APPWIDGET"/>
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
</privapp-permissions>
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index e3527e3..43fd270 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -658,6 +658,14 @@
* float confidence = floatDepthBuffer.get();
* </pre>
*
+ * For camera devices that support the
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT}
+ * capability, DEPTH_POINT_CLOUD coordinates have units of meters, and the coordinate system is
+ * defined by the camera's pose transforms:
+ * {@link android.hardware.camera2.CameraCharacteristics#LENS_POSE_TRANSLATION} and
+ * {@link android.hardware.camera2.CameraCharacteristics#LENS_POSE_ROTATION}. That means the origin is
+ * the optical center of the camera device, and the positive Z axis points along the camera's optical axis,
+ * toward the scene.
*/
public static final int DEPTH_POINT_CLOUD = 0x101;
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index f93886d..96d6eee 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -185,4 +185,52 @@
return false;
}
+
+ /**
+ * @hide
+ */
+ public static String formatToString(@Format int format) {
+ switch (format) {
+ case UNKNOWN:
+ return "UNKNOWN";
+ case TRANSLUCENT:
+ return "TRANSLUCENT";
+ case TRANSPARENT:
+ return "TRANSPARENT";
+ case RGBA_8888:
+ return "RGBA_8888";
+ case RGBX_8888:
+ return "RGBX_8888";
+ case RGB_888:
+ return "RGB_888";
+ case RGB_565:
+ return "RGB_565";
+ case RGBA_5551:
+ return "RGBA_5551";
+ case RGBA_4444:
+ return "RGBA_4444";
+ case A_8:
+ return "A_8";
+ case L_8:
+ return "L_8";
+ case LA_88:
+ return "LA_88";
+ case RGB_332:
+ return "RGB_332";
+ case YCbCr_422_SP:
+ return "YCbCr_422_SP";
+ case YCbCr_420_SP:
+ return "YCbCr_420_SP";
+ case YCbCr_422_I:
+ return "YCbCr_422_I";
+ case RGBA_F16:
+ return "RGBA_F16";
+ case RGBA_1010102:
+ return "RGBA_1010102";
+ case JPEG:
+ return "JPEG";
+ default:
+ return Integer.toString(format);
+ }
+ }
}
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 0209cea..40288f5 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -159,8 +159,10 @@
if (mNativeInstance == 0) {
mNativeInstance = createNativeInstance(mLocalMatrix == null
? 0 : mLocalMatrix.native_instance);
- mCleaner = NoImagePreloadHolder.sRegistry.registerNativeAllocation(
- this, mNativeInstance);
+ if (mNativeInstance != 0) {
+ mCleaner = NoImagePreloadHolder.sRegistry.registerNativeAllocation(
+ this, mNativeInstance);
+ }
}
return mNativeInstance;
}
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index 6bd2646..3bf4f90 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -104,7 +104,7 @@
final AnimatorSet set = new AnimatorSet();
// Linear exit after enter is completed.
- final ObjectAnimator exit = ObjectAnimator.ofFloat(this, RippleBackground.OPACITY, 0);
+ final ObjectAnimator exit = ObjectAnimator.ofFloat(this, OPACITY, 0);
exit.setInterpolator(LINEAR_INTERPOLATOR);
exit.setDuration(OPACITY_EXIT_DURATION);
exit.setAutoCancel(true);
@@ -115,7 +115,7 @@
final int fastEnterDuration = mIsBounded ?
(int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0;
if (fastEnterDuration > 0) {
- final ObjectAnimator enter = ObjectAnimator.ofFloat(this, RippleBackground.OPACITY, 1);
+ final ObjectAnimator enter = ObjectAnimator.ofFloat(this, OPACITY, 1);
enter.setInterpolator(LINEAR_INTERPOLATOR);
enter.setDuration(fastEnterDuration);
enter.setAutoCancel(true);
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 8f314c9..b090681 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -266,8 +266,9 @@
}
}
- setRippleActive(enabled && pressed);
- setBackgroundActive(hovered || focused || (enabled && pressed), focused || hovered);
+ setRippleActive(focused || (enabled && pressed));
+
+ setBackgroundActive(hovered, hovered);
return changed;
}
@@ -693,7 +694,9 @@
// have a mask or content and the ripple bounds if we're projecting.
final Rect bounds = getDirtyBounds();
final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
- canvas.clipRect(bounds);
+ if (isBounded()) {
+ canvas.clipRect(bounds);
+ }
drawContent(canvas);
drawBackgroundAndRipples(canvas);
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index 829733e..0de3f11 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -80,9 +80,6 @@
private float mTweenX = 0;
private float mTweenY = 0;
- /** Whether this ripple is bounded. */
- private boolean mIsBounded;
-
/** Whether this ripple has finished its exit animation. */
private boolean mHasFinishedExit;
@@ -90,7 +87,6 @@
boolean isBounded, boolean forceSoftware) {
super(owner, bounds, forceSoftware);
- mIsBounded = isBounded;
mStartingX = startingX;
mStartingY = startingY;
@@ -162,11 +158,6 @@
@Override
protected Animator createSoftwareEnter(boolean fast) {
- // Bounded ripples don't have enter animations.
- if (mIsBounded) {
- return null;
- }
-
final int duration = (int)
(1000 * Math.sqrt(mTargetRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensityScale) + 0.5);
@@ -215,31 +206,15 @@
return (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
}
- /**
- * Compute target values that are dependent on bounding.
- */
- private void computeBoundedTargetValues() {
- mTargetX = (mClampedStartingX - mBounds.exactCenterX()) * .7f;
- mTargetY = (mClampedStartingY - mBounds.exactCenterY()) * .7f;
- mTargetRadius = mBoundedRadius;
- }
-
@Override
protected Animator createSoftwareExit() {
final int radiusDuration;
final int originDuration;
final int opacityDuration;
- if (mIsBounded) {
- computeBoundedTargetValues();
- radiusDuration = BOUNDED_RADIUS_EXIT_DURATION;
- originDuration = BOUNDED_ORIGIN_EXIT_DURATION;
- opacityDuration = BOUNDED_OPACITY_EXIT_DURATION;
- } else {
- radiusDuration = getRadiusExitDuration();
- originDuration = radiusDuration;
- opacityDuration = getOpacityExitDuration();
- }
+ radiusDuration = getRadiusExitDuration();
+ originDuration = radiusDuration;
+ opacityDuration = getOpacityExitDuration();
final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
tweenRadius.setAutoCancel(true);
@@ -268,17 +243,10 @@
final int radiusDuration;
final int originDuration;
final int opacityDuration;
- if (mIsBounded) {
- computeBoundedTargetValues();
- radiusDuration = BOUNDED_RADIUS_EXIT_DURATION;
- originDuration = BOUNDED_ORIGIN_EXIT_DURATION;
- opacityDuration = BOUNDED_OPACITY_EXIT_DURATION;
- } else {
- radiusDuration = getRadiusExitDuration();
- originDuration = radiusDuration;
- opacityDuration = getOpacityExitDuration();
- }
+ radiusDuration = getRadiusExitDuration();
+ originDuration = radiusDuration;
+ opacityDuration = getOpacityExitDuration();
final float startX = getCurrentX();
final float startY = getCurrentY();
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index e764034..c4c14c9e 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -79,7 +79,7 @@
"libutils",
],
shared_libs: [
- "libz-host",
+ "libz",
],
},
windows: {
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index a5b1d29..0e577d1 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -30,6 +30,8 @@
namespace android {
+ApkAssets::ApkAssets() : zip_handle_(nullptr, ::CloseArchive) {}
+
std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) {
return ApkAssets::LoadImpl(path, system, false /*load_as_shared_library*/);
}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index ab7e14d..f1f2e2d 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -902,26 +902,32 @@
return true;
}
- if (asset_manager_ != o.asset_manager_) {
- return false;
- }
-
type_spec_flags_ = o.type_spec_flags_;
+ const bool copy_only_system = asset_manager_ != o.asset_manager_;
+
for (size_t p = 0; p < packages_.size(); p++) {
const Package* package = o.packages_[p].get();
- if (package == nullptr) {
+ if (package == nullptr || (copy_only_system && p != 0x01)) {
+ // The other theme doesn't have this package, clear ours.
packages_[p].reset();
continue;
}
+ if (packages_[p] == nullptr) {
+ // The other theme has this package, but we don't. Make one.
+ packages_[p].reset(new Package());
+ }
+
for (size_t t = 0; t < package->types.size(); t++) {
const Type* type = package->types[t].get();
if (type == nullptr) {
+ // The other theme doesn't have this type, clear ours.
packages_[p]->types[t].reset();
continue;
}
+ // Create a new type and update it to theirs.
const size_t type_alloc_size = sizeof(Type) + (type->entry_capacity * sizeof(Entry));
void* copied_data = malloc(type_alloc_size);
memcpy(copied_data, type, type_alloc_size);
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index b7e66fb..2e392d5 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -21,7 +21,6 @@
#include <string>
#include "android-base/macros.h"
-#include "ziparchive/zip_archive.h"
#include "androidfw/Asset.h"
#include "androidfw/LoadedArsc.h"
@@ -52,14 +51,9 @@
static std::unique_ptr<const ApkAssets> LoadImpl(const std::string& path, bool system,
bool load_as_shared_library);
- ApkAssets() = default;
+ ApkAssets();
- struct ZipArchivePtrCloser {
- void operator()(::ZipArchiveHandle handle) { ::CloseArchive(handle); }
- };
-
- using ZipArchivePtr =
- std::unique_ptr<typename std::remove_pointer<::ZipArchiveHandle>::type, ZipArchivePtrCloser>;
+ using ZipArchivePtr = std::unique_ptr<void, void(*)(void*)>;
ZipArchivePtr zip_handle_;
std::string path_;
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index d2bc6ee..fd94144 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -70,9 +70,8 @@
};
// AssetManager2 is the main entry point for accessing assets and resources.
-// AssetManager2 provides caching of resources retrieved via the underlying
-// ApkAssets.
-class AssetManager2 : public ::AAssetManager {
+// AssetManager2 provides caching of resources retrieved via the underlying ApkAssets.
+class AssetManager2 {
public:
struct ResourceName {
const char* package = nullptr;
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index dfff9c0..feb454e 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -23,6 +23,7 @@
#include "data/lib_one/R.h"
#include "data/libclient/R.h"
#include "data/styles/R.h"
+#include "data/system/R.h"
namespace app = com::android::app;
namespace lib_one = com::android::lib_one;
@@ -33,6 +34,9 @@
class ThemeTest : public ::testing::Test {
public:
void SetUp() override {
+ system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/);
+ ASSERT_NE(nullptr, system_assets_);
+
style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
ASSERT_NE(nullptr, style_assets_);
@@ -47,6 +51,7 @@
}
protected:
+ std::unique_ptr<const ApkAssets> system_assets_;
std::unique_ptr<const ApkAssets> style_assets_;
std::unique_ptr<const ApkAssets> libclient_assets_;
std::unique_ptr<const ApkAssets> lib_one_assets_;
@@ -262,20 +267,30 @@
EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
}
-TEST_F(ThemeTest, FailToCopyThemeWithDifferentAssetManager) {
+TEST_F(ThemeTest, OnlyCopySystemThemeWhenAssetManagersDiffer) {
AssetManager2 assetmanager_one;
- assetmanager_one.SetApkAssets({style_assets_.get()});
+ assetmanager_one.SetApkAssets({system_assets_.get(), style_assets_.get()});
AssetManager2 assetmanager_two;
- assetmanager_two.SetApkAssets({style_assets_.get()});
+ assetmanager_two.SetApkAssets({system_assets_.get(), style_assets_.get()});
auto theme_one = assetmanager_one.NewTheme();
ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne));
auto theme_two = assetmanager_two.NewTheme();
+ ASSERT_TRUE(theme_two->ApplyStyle(R::style::Theme_One));
ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleTwo));
- EXPECT_FALSE(theme_one->SetTo(*theme_two));
+ EXPECT_TRUE(theme_one->SetTo(*theme_two));
+
+ Res_value value;
+ uint32_t flags;
+
+ // No app resources.
+ EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
+
+ // Only system.
+ EXPECT_NE(kInvalidCookie, theme_one->GetAttribute(R::attr::foreground, &value, &flags));
}
} // namespace android
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 770a57a..124182f 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -2,7 +2,7 @@
name: "hwui_defaults",
defaults: [
"hwui_static_deps",
-
+ "skia_deps"
//"hwui_bugreport_font_cache_usage",
//"hwui_compile_for_perf",
],
@@ -55,7 +55,6 @@
"libEGL",
"libGLESv2",
"libvulkan",
- "libskia",
"libui",
"libgui",
"libprotobuf-cpp-lite",
@@ -114,6 +113,9 @@
cc_defaults {
name: "libhwui_defaults",
defaults: ["hwui_defaults"],
+
+ whole_static_libs: ["libskia"],
+
srcs: [
"hwui/Bitmap.cpp",
"font/CacheTexture.cpp",
diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp
index f5bb821..69ead58 100644
--- a/libs/hwui/AnimatorManager.cpp
+++ b/libs/hwui/AnimatorManager.cpp
@@ -71,9 +71,11 @@
void AnimatorManager::pushStaging() {
if (mNewAnimators.size()) {
- LOG_ALWAYS_FATAL_IF(!mAnimationHandle,
- "Trying to start new animators on %p (%s) without an animation handle!",
- &mParent, mParent.getName());
+ if (CC_UNLIKELY(!mAnimationHandle)) {
+ ALOGW("Trying to start new animators on %p (%s) without an animation handle!",
+ &mParent, mParent.getName());
+ return;
+ }
// Only add new animators that are not already in the mAnimators list
for (auto& anim : mNewAnimators) {
diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp
index f9a1cc5..2687410 100644
--- a/libs/hwui/OpenGLReadback.cpp
+++ b/libs/hwui/OpenGLReadback.cpp
@@ -280,6 +280,11 @@
bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread,
GlLayer& layer, SkBitmap* bitmap) {
+ if (!layer.isRenderable()) {
+ // layer has never been updated by DeferredLayerUpdater, abort copy
+ return false;
+ }
+
return CopyResult::Success == copyTextureInto(Caches::getInstance(),
renderThread.renderState(), layer.getTexture(), layer.getTexTransform(),
Rect(), bitmap);
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 2b29542..a5b4c42 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -104,14 +104,14 @@
std::shared_ptr<minikin::MinikinFont> MinikinFontSkia::createFontWithVariation(
const std::vector<minikin::FontVariation>& variations) const {
- SkFontMgr::FontParameters params;
+ SkFontArguments params;
int ttcIndex;
- SkStreamAsset* stream = mTypeface->openStream(&ttcIndex);
+ std::unique_ptr<SkStreamAsset> stream(mTypeface->openStream(&ttcIndex));
LOG_ALWAYS_FATAL_IF(stream == nullptr, "openStream failed");
params.setCollectionIndex(ttcIndex);
- std::vector<SkFontMgr::FontParameters::Axis> skAxes;
+ std::vector<SkFontArguments::Axis> skAxes;
skAxes.resize(variations.size());
for (size_t i = 0; i < variations.size(); i++) {
skAxes[i].fTag = variations[i].axisTag;
@@ -119,7 +119,7 @@
}
params.setAxes(skAxes.data(), skAxes.size());
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
- sk_sp<SkTypeface> face(fm->createFromStream(stream, params));
+ sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), params));
return std::make_shared<MinikinFontSkia>(std::move(face), mFontData, mFontSize, ttcIndex,
variations);
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 17438e5..3b72a8b 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -36,22 +36,18 @@
bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer) {
// transform the matrix based on the layer
- int saveCount = -1;
- if (!layer->getTransform().isIdentity()) {
- saveCount = canvas->save();
- SkMatrix transform;
- layer->getTransform().copyTo(transform);
- canvas->concat(transform);
- }
-
+ SkMatrix layerTransform;
+ layer->getTransform().copyTo(layerTransform);
sk_sp<SkImage> layerImage;
+ int layerWidth = layer->getWidth();
+ int layerHeight = layer->getHeight();
if (layer->getApi() == Layer::Api::OpenGL) {
GlLayer* glLayer = static_cast<GlLayer*>(layer);
GrGLTextureInfo externalTexture;
externalTexture.fTarget = glLayer->getRenderTarget();
externalTexture.fID = glLayer->getTextureId();
- GrBackendTexture backendTexture(glLayer->getWidth(), glLayer->getHeight(),
- kRGBA_8888_GrPixelConfig, externalTexture);
+ GrBackendTexture backendTexture(layerWidth, layerHeight, kRGBA_8888_GrPixelConfig,
+ externalTexture);
layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
kPremul_SkAlphaType, nullptr);
} else {
@@ -62,15 +58,37 @@
}
if (layerImage) {
+ SkMatrix textureMatrix;
+ layer->getTexTransform().copyTo(textureMatrix);
+ //TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
+ // use bottom left origin and remove flipV and invert transformations.
+ SkMatrix flipV;
+ flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
+ textureMatrix.preConcat(flipV);
+ textureMatrix.preScale(1.0f/layerWidth, 1.0f/layerHeight);
+ textureMatrix.postScale(layerWidth, layerHeight);
+ SkMatrix textureMatrixInv;
+ if (!textureMatrix.invert(&textureMatrixInv)) {
+ textureMatrixInv = textureMatrix;
+ }
+
+ SkMatrix matrix = SkMatrix::Concat(layerTransform, textureMatrixInv);
+
SkPaint paint;
paint.setAlpha(layer->getAlpha());
paint.setBlendMode(layer->getMode());
paint.setColorFilter(sk_ref_sp(layer->getColorFilter()));
- canvas->drawImage(layerImage, 0, 0, &paint);
- }
- // restore the original matrix
- if (saveCount >= 0) {
- canvas->restoreToCount(saveCount);
+
+ const bool nonIdentityMatrix = !matrix.isIdentity();
+ if (nonIdentityMatrix) {
+ canvas->save();
+ canvas->concat(matrix);
+ }
+ canvas->drawImage(layerImage.get(), 0, 0, &paint);
+ // restore the original matrix
+ if (nonIdentityMatrix) {
+ canvas->restore();
+ }
}
return layerImage;
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 4ee47af..47dee9d 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -19,6 +19,7 @@
#include "SkiaDisplayList.h"
#include "SkiaPipeline.h"
#include "utils/TraceUtils.h"
+#include <SkPaintFilterCanvas.h>
namespace android {
namespace uirenderer {
@@ -151,6 +152,27 @@
return false;
}
+class AlphaFilterCanvas : public SkPaintFilterCanvas {
+public:
+ AlphaFilterCanvas(SkCanvas* canvas, float alpha) : SkPaintFilterCanvas(canvas), mAlpha(alpha) {}
+protected:
+ bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type t) const override {
+ SkTLazy<SkPaint> defaultPaint;
+ if (!*paint) {
+ paint->init(*defaultPaint.init());
+ }
+ paint->writable()->setAlpha((uint8_t)(*paint)->getAlpha()*mAlpha);
+ return true;
+ }
+ void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
+ // We unroll the drawable using "this" canvas, so that draw calls contained inside will
+ // get their alpha applied. THe default SkPaintFilterCanvas::onDrawDrawable does not unroll.
+ drawable->draw(this, matrix);
+ }
+private:
+ float mAlpha;
+};
+
void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
RenderNode* renderNode = mRenderNode.get();
float alphaMultiplier = 1.0f;
@@ -211,7 +233,14 @@
canvas->restore();
}
} else {
- displayList->draw(canvas);
+ if (alphaMultiplier < 1.0f) {
+ // Non-layer draw for a view with getHasOverlappingRendering=false, will apply
+ // the alpha to the paint of each nested draw.
+ AlphaFilterCanvas alphaCanvas(canvas, alphaMultiplier);
+ displayList->draw(&alphaCanvas);
+ } else {
+ displayList->draw(canvas);
+ }
}
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 6f117de..c4bd1e1 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -45,6 +45,10 @@
mVectorDrawables.reserve(30);
}
+SkiaPipeline::~SkiaPipeline() {
+ unpinImages();
+}
+
TaskManager* SkiaPipeline::getTaskManager() {
return mRenderThread.cacheManager().getTaskManager();
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 2b0c419..3e6ae30 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -28,7 +28,7 @@
class SkiaPipeline : public renderthread::IRenderPipeline {
public:
SkiaPipeline(renderthread::RenderThread& thread);
- virtual ~SkiaPipeline() {}
+ virtual ~SkiaPipeline();
TaskManager* getTaskManager() override;
diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp
new file mode 100644
index 0000000..04fc2d4
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/TvApp.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "TestSceneBase.h"
+#include "tests/common/BitmapAllocationTestUtils.h"
+#include "SkBlendMode.h"
+
+class TvApp;
+class TvAppNoRoundedCorner;
+class TvAppColorFilter;
+class TvAppNoRoundedCornerColorFilter;
+
+static bool _TvApp(
+ BitmapAllocationTestUtils::registerBitmapAllocationScene<TvApp>(
+ "tvapp", "A dense grid of cards:"
+ "with rounded corner, using overlay RenderNode for dimming."));
+
+static bool _TvAppNoRoundedCorner(
+ BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppNoRoundedCorner>(
+ "tvapp_norc", "A dense grid of cards:"
+ "no rounded corner, using overlay RenderNode for dimming"));
+
+static bool _TvAppColorFilter(
+ BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppColorFilter>(
+ "tvapp_cf", "A dense grid of cards:"
+ "with rounded corner, using ColorFilter for dimming"));
+
+static bool _TvAppNoRoundedCornerColorFilter(
+ BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppNoRoundedCornerColorFilter>(
+ "tvapp_norc_cf", "A dense grid of cards:"
+ "no rounded corner, using ColorFilter for dimming"));
+
+class TvApp : public TestScene {
+public:
+ TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator)
+ : TestScene()
+ , mAllocator(allocator) { }
+
+ sp<RenderNode> mBg;
+ std::vector<sp<RenderNode>> mCards;
+ std::vector<sp<RenderNode>> mInfoAreas;
+ std::vector<sp<RenderNode>> mImages;
+ std::vector<sp<RenderNode>> mOverlays;
+ std::vector<sk_sp<Bitmap>> mCachedBitmaps;
+ BitmapAllocationTestUtils::BitmapAllocator mAllocator;
+ sk_sp<Bitmap> mSingleBitmap;
+ int mSeed = 0;
+ int mSeed2 = 0;
+
+ void createContent(int width, int height, Canvas& canvas) override {
+ mBg = createBitmapNode(canvas, 0xFF9C27B0, 0, 0, width, height);
+ canvas.drawRenderNode(mBg.get());
+
+ canvas.insertReorderBarrier(true);
+ mSingleBitmap = mAllocator(dp(160), dp(120), kRGBA_8888_SkColorType,
+ [](SkBitmap& skBitmap) {
+ skBitmap.eraseColor(0xFF0000FF);
+ });
+
+ for (int y = dp(18) - dp(178); y < height - dp(18); y += dp(178)) {
+ bool isFirstCard = true;
+ for (int x = dp(18); x < width - dp(18); x += dp(178)) {
+ sp<RenderNode> card = createCard(x, y, dp(160), dp(160), isFirstCard);
+ isFirstCard = false;
+ canvas.drawRenderNode(card.get());
+ mCards.push_back(card);
+ }
+ }
+ canvas.insertReorderBarrier(false);
+ }
+
+ void doFrame(int frameNr) override {
+ size_t numCards = mCards.size();
+ for (size_t ci = 0; ci < numCards; ci++) {
+ updateCard(ci, frameNr);
+ }
+ }
+
+private:
+ sp<RenderNode> createBitmapNode(Canvas& canvas, SkColor color, int left, int top,
+ int width, int height) {
+ return TestUtils::createNode(left, top, left + width , top + height,
+ [this, width, height, color](RenderProperties& props, Canvas& canvas) {
+ sk_sp<Bitmap> bitmap = mAllocator(width, height, kRGBA_8888_SkColorType,
+ [color](SkBitmap& skBitmap) {
+ skBitmap.eraseColor(color);
+ });
+ canvas.drawBitmap(*bitmap, 0, 0, nullptr);
+ });
+ }
+
+ sp<RenderNode> createSharedBitmapNode(Canvas& canvas, int left, int top,
+ int width, int height, sk_sp<Bitmap>bitmap) {
+ return TestUtils::createNode(left, top, left + width , top + height,
+ [bitmap](RenderProperties& props, Canvas& canvas) {
+ canvas.drawBitmap(*bitmap, 0, 0, nullptr);
+ });
+ }
+
+ sp<RenderNode> createInfoNode(Canvas& canvas, int left, int top,
+ int width, int height, const char* text, const char* text2) {
+ return TestUtils::createNode(left, top, left + width , top + height,
+ [text, text2](RenderProperties& props, Canvas& canvas) {
+ canvas.drawColor(0xFFFFEEEE, SkBlendMode::kSrcOver);
+
+ SkPaint paint;
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ paint.setAntiAlias(true);
+ paint.setTextSize(24);
+
+ paint.setColor(Color::Black);
+ TestUtils::drawUtf8ToCanvas(&canvas, text, paint, 10, 30);
+ paint.setTextSize(20);
+ TestUtils::drawUtf8ToCanvas(&canvas, text2, paint, 10, 54);
+
+ });
+ }
+
+ sp<RenderNode> createColorNode(Canvas& canvas, int left, int top,
+ int width, int height, SkColor color) {
+ return TestUtils::createNode(left, top, left + width , top + height,
+ [color](RenderProperties& props, Canvas& canvas) {
+ canvas.drawColor(color, SkBlendMode::kSrcOver);
+ });
+ }
+
+ virtual bool useSingleBitmap() {
+ return false;
+ }
+
+ virtual float roundedCornerRadius() {
+ return dp(2);
+ }
+
+ // when true, use overlay RenderNode for dimming, otherwise apply a ColorFilter to dim image
+ virtual bool useOverlay() {
+ return true;
+ }
+
+ sp<RenderNode> createCard(int x, int y, int width, int height, bool selected) {
+ return TestUtils::createNode(x, y, x + width, y + height,
+ [width, height, selected, this](RenderProperties& props, Canvas& canvas) {
+ if (selected) {
+ props.setElevation(dp(16));
+ props.setScaleX(1.2);
+ props.setScaleY(1.2);
+ }
+ props.mutableOutline().setRoundRect(0, 0, width, height, roundedCornerRadius(), 1);
+ props.mutableOutline().setShouldClip(true);
+
+ sk_sp<Bitmap> bitmap = useSingleBitmap() ? mSingleBitmap
+ : mAllocator(width, dp(120), kRGBA_8888_SkColorType, [this](SkBitmap& skBitmap) {
+ skBitmap.eraseColor(0xFF000000 | ((mSeed << 3) & 0xFF));
+ });
+ sp<RenderNode> cardImage = createSharedBitmapNode(canvas, 0, 0, width, dp(120),
+ bitmap);
+ canvas.drawRenderNode(cardImage.get());
+ mCachedBitmaps.push_back(bitmap);
+ mImages.push_back(cardImage);
+
+ char buffer[128];
+ sprintf(buffer, "Video %d-%d", mSeed, mSeed + 1);
+ mSeed++;
+ char buffer2[128];
+ sprintf(buffer2, "Studio %d", mSeed2++);
+ sp<RenderNode> infoArea = createInfoNode(canvas, 0, dp(120), width, height, buffer, buffer2);
+ canvas.drawRenderNode(infoArea.get());
+ mInfoAreas.push_back(infoArea);
+
+ if (useOverlay()) {
+ sp<RenderNode> overlayColor = createColorNode(canvas, 0, 0, width, height, 0x00000000);
+ canvas.drawRenderNode(overlayColor.get());
+ mOverlays.push_back(overlayColor);
+ }
+ });
+ }
+
+ void updateCard(int ci, int curFrame) {
+ // updating card's translation Y
+ sp<RenderNode> card = mCards[ci];
+ card->setPropertyFieldsDirty(RenderNode::Y);
+ card->mutateStagingProperties().setTranslationY(curFrame % 150);
+
+ // re-recording card's canvas, not necessary but to add some burden to CPU
+ std::unique_ptr<Canvas> cardcanvas(Canvas::create_recording_canvas(
+ card->stagingProperties().getWidth(),
+ card->stagingProperties().getHeight()));
+ sp<RenderNode> image = mImages[ci];
+ sp<RenderNode> infoArea = mInfoAreas[ci];
+ cardcanvas->drawRenderNode(infoArea.get());
+
+ if (useOverlay()) {
+ cardcanvas->drawRenderNode(image.get());
+ // re-recording card overlay's canvas, animating overlay color alpha
+ sp<RenderNode> overlay = mOverlays[ci];
+ std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(
+ overlay->stagingProperties().getWidth(),
+ overlay->stagingProperties().getHeight()));
+ canvas->drawColor((curFrame % 150) << 24, SkBlendMode::kSrcOver);
+ overlay->setStagingDisplayList(canvas->finishRecording());
+ cardcanvas->drawRenderNode(overlay.get());
+ } else {
+ // re-recording image node's canvas, animating ColorFilter
+ std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(
+ image->stagingProperties().getWidth(),
+ image->stagingProperties().getHeight()));
+ SkPaint paint;
+ sk_sp<SkColorFilter> filter(SkColorFilter::MakeModeFilter((curFrame % 150) << 24,
+ SkBlendMode::kSrcATop));
+ paint.setColorFilter(filter);
+ sk_sp<Bitmap> bitmap = mCachedBitmaps[ci];
+ canvas->drawBitmap(*bitmap, 0, 0, &paint);
+ image->setStagingDisplayList(canvas->finishRecording());
+ cardcanvas->drawRenderNode(image.get());
+ }
+
+ card->setStagingDisplayList(cardcanvas->finishRecording());
+ }
+};
+
+class TvAppNoRoundedCorner : public TvApp {
+public:
+ TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator)
+ : TvApp(allocator) { }
+
+private:
+
+ virtual float roundedCornerRadius() override {
+ return dp(0);
+ }
+};
+
+class TvAppColorFilter : public TvApp {
+public:
+ TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator)
+ : TvApp(allocator) { }
+
+private:
+
+ virtual bool useOverlay() override {
+ return false;
+ }
+};
+
+class TvAppNoRoundedCornerColorFilter : public TvApp {
+public:
+ TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator)
+ : TvApp(allocator) { }
+
+private:
+
+ virtual float roundedCornerRadius() override {
+ return dp(0);
+ }
+
+ virtual bool useOverlay() override {
+ return false;
+ }
+};
+
+
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 345cfd6..439d690 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -52,7 +52,7 @@
SkData::MakeWithProc(data, st.st_size, unmap, reinterpret_cast<void*>(st.st_size));
std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(skData));
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
- sk_sp<SkTypeface> typeface(fm->createFromStream(fontData.release()));
+ sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData)));
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 4310706..67682a0 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -24,7 +24,7 @@
"libcutils",
"liblog",
"libutils",
- "libskia",
+ "libhwui",
"libgui",
"libui",
"libinput",
diff --git a/libs/protoutil/Android.mk b/libs/protoutil/Android.mk
new file mode 100644
index 0000000..a534816
--- /dev/null
+++ b/libs/protoutil/Android.mk
@@ -0,0 +1,39 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libprotoutil
+
+LOCAL_CFLAGS := \
+ -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ liblog \
+ libutils
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include
+
+LOCAL_SRC_FILES := \
+ src/EncodedBuffer.cpp \
+ src/protobuf.cpp \
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/libs/protoutil/include/android/util/EncodedBuffer.h b/libs/protoutil/include/android/util/EncodedBuffer.h
new file mode 100644
index 0000000..cf09609
--- /dev/null
+++ b/libs/protoutil/include/android/util/EncodedBuffer.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ANDROID_UTIL_ENCODED_BUFFER_H
+#define ANDROID_UTIL_ENCODED_BUFFER_H
+
+#include <stdint.h>
+#include <vector>
+
+namespace android {
+namespace util {
+
+using namespace std;
+
+/**
+ * A stream of bytes containing a read pointer and a write pointer,
+ * backed by a set of fixed-size buffers. There are write functions for the
+ * primitive types stored by protocol buffers, but none of the logic
+ * for tags, inner objects, or any of that.
+ *
+ * Terminology:
+ * *Pos: Position in the whole data set (as if it were a single buffer).
+ * *Index: Index of a buffer within the mBuffers list.
+ * *Offset: Position within a buffer.
+ */
+class EncodedBuffer
+{
+public:
+ EncodedBuffer();
+ EncodedBuffer(size_t chunkSize);
+ ~EncodedBuffer();
+
+ class Pointer {
+ public:
+ Pointer();
+ Pointer(size_t chunkSize);
+
+ size_t pos() const;
+ size_t index() const;
+ size_t offset() const;
+
+ void move(size_t amt);
+ inline void move() { move(1); };
+
+ void rewind();
+ Pointer copy() const;
+
+ private:
+ size_t mChunkSize;
+ size_t mIndex;
+ size_t mOffset;
+ };
+
+ /******************************** Write APIs ************************************************/
+
+ /**
+ * Returns the number of bytes written in the buffer
+ */
+ size_t size() const;
+
+ /**
+ * Returns the write pointer.
+ */
+ Pointer* wp();
+
+ /**
+ * Returns the current position of write pointer, if the write buffer is full, it will automatically
+ * rotate to a new buffer with given chunkSize. If NULL is returned, it means NO_MEMORY
+ */
+ uint8_t* writeBuffer();
+
+ /**
+ * Returns the writeable size in the current write buffer .
+ */
+ size_t currentToWrite();
+
+ /**
+ * Write a varint into a vector. Return the size of the varint.
+ */
+ size_t writeRawVarint(uint32_t val);
+
+ /**
+ * Write a protobuf header. Return the size of the header.
+ */
+ size_t writeHeader(uint32_t fieldId, uint8_t wireType);
+
+ /********************************* Read APIs ************************************************/
+ class iterator;
+ friend class iterator;
+ class iterator {
+ public:
+ iterator(const EncodedBuffer& buffer);
+
+ /**
+ * Returns the number of bytes written in the buffer
+ */
+ size_t size() const;
+
+ /**
+ * Returns the size of total bytes read.
+ */
+ size_t bytesRead() const;
+
+ /**
+ * Returns the read pointer.
+ */
+ Pointer* rp();
+
+ /**
+ * Returns the current position of read pointer, if NULL is returned, it reaches end of buffer.
+ */
+ uint8_t const* readBuffer();
+
+ /**
+ * Returns the readable size in the current read buffer.
+ */
+ size_t currentToRead();
+
+ /**
+ * Returns true if next bytes is available for read.
+ */
+ bool hasNext();
+
+ /**
+ * Reads the current byte and moves pointer 1 bit.
+ */
+ uint8_t next();
+
+ /**
+ * Read varint from iterator, the iterator will point to next available byte.
+ * Return the number of bytes of the varint.
+ */
+ uint32_t readRawVarint();
+
+ private:
+ const EncodedBuffer& mData;
+ Pointer mRp;
+ };
+
+ /**
+ * Returns the iterator of EncodedBuffer so it guarantees consumers won't be able to modified the buffer.
+ */
+ iterator begin() const;
+
+private:
+ size_t mChunkSize;
+ vector<uint8_t*> mBuffers;
+
+ Pointer mWp;
+
+ inline uint8_t* at(const Pointer& p) const; // helper function to get value
+};
+
+} // util
+} // android
+
+#endif // ANDROID_UTIL_ENCODED_BUFFER_H
\ No newline at end of file
diff --git a/cmds/incidentd/src/protobuf.h b/libs/protoutil/include/android/util/protobuf.h
similarity index 74%
rename from cmds/incidentd/src/protobuf.h
rename to libs/protoutil/include/android/util/protobuf.h
index 263c864..f4e8d09 100644
--- a/cmds/incidentd/src/protobuf.h
+++ b/libs/protoutil/include/android/util/protobuf.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-#ifndef PROTOBUF_H
-#define PROTOBUF_H
+#ifndef ANDROID_UTIL_PROTOBUF_H
+#define ANDROID_UTIL_PROTOBUF_H
#include <stdint.h>
-#include <vector>
+
+namespace android {
+namespace util {
using namespace std;
@@ -50,19 +52,7 @@
*/
uint8_t* write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size);
-/**
- * Write a varint into a vector. Return the size of the varint.
- */
-size_t write_raw_varint(vector<uint8_t>* buf, uint32_t val);
+} // util
+} // android
-/**
- * Write a protobuf header. Return the size of the header.
- */
-size_t write_header(vector<uint8_t>* buf, uint32_t fieldId, uint8_t wireType);
-
-enum {
- // IncidentProto.header
- FIELD_ID_INCIDENT_HEADER = 1
-};
-
-#endif // PROTOBUF_H
+#endif // ANDROID_UTIL_PROTOUBUF_H
diff --git a/libs/protoutil/src/EncodedBuffer.cpp b/libs/protoutil/src/EncodedBuffer.cpp
new file mode 100644
index 0000000..84dc5b6
--- /dev/null
+++ b/libs/protoutil/src/EncodedBuffer.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <android/util/EncodedBuffer.h>
+
+#include <stdlib.h>
+
+namespace android {
+namespace util {
+
+const size_t BUFFER_SIZE = 8 * 1024; // 8 KB
+
+EncodedBuffer::Pointer::Pointer() : Pointer(BUFFER_SIZE)
+{
+}
+
+EncodedBuffer::Pointer::Pointer(size_t chunkSize)
+ :mIndex(0),
+ mOffset(0)
+{
+ mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize;
+}
+
+size_t
+EncodedBuffer::Pointer::pos() const
+{
+ return mIndex * mChunkSize + mOffset;
+}
+
+size_t
+EncodedBuffer::Pointer::index() const
+{
+ return mIndex;
+}
+
+size_t
+EncodedBuffer::Pointer::offset() const
+{
+ return mOffset;
+}
+
+void
+EncodedBuffer::Pointer::move(size_t amt)
+{
+ size_t newOffset = mOffset + amt;
+ mIndex += newOffset / mChunkSize;
+ mOffset = newOffset % mChunkSize;
+}
+
+void
+EncodedBuffer::Pointer::rewind()
+{
+ mIndex = 0;
+ mOffset = 0;
+}
+
+EncodedBuffer::Pointer
+EncodedBuffer::Pointer::copy() const
+{
+ Pointer p = Pointer(mChunkSize);
+ p.mIndex = mIndex;
+ p.mOffset = mOffset;
+ return p;
+}
+
+// ===========================================================
+EncodedBuffer::EncodedBuffer() : EncodedBuffer(0)
+{
+}
+
+EncodedBuffer::EncodedBuffer(size_t chunkSize)
+ :mBuffers()
+{
+ mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize;
+ mWp = Pointer(mChunkSize);
+}
+
+EncodedBuffer::~EncodedBuffer()
+{
+ for (size_t i=0; i<mBuffers.size(); i++) {
+ uint8_t* buf = mBuffers[i];
+ free(buf);
+ }
+}
+
+inline uint8_t*
+EncodedBuffer::at(const Pointer& p) const
+{
+ return mBuffers[p.index()] + p.offset();
+}
+
+/******************************** Write APIs ************************************************/
+size_t
+EncodedBuffer::size() const
+{
+ return mWp.pos();
+}
+
+EncodedBuffer::Pointer*
+EncodedBuffer::wp()
+{
+ return &mWp;
+}
+
+uint8_t*
+EncodedBuffer::writeBuffer()
+{
+ // This prevents write pointer move too fast than allocating the buffer.
+ if (mWp.index() > mBuffers.size()) return NULL;
+ uint8_t* buf = NULL;
+ if (mWp.index() == mBuffers.size()) {
+ buf = (uint8_t*)malloc(mChunkSize);
+
+ if (buf == NULL) return NULL; // This indicates NO_MEMORY
+
+ mBuffers.push_back(buf);
+ }
+ return at(mWp);
+}
+
+size_t
+EncodedBuffer::currentToWrite()
+{
+ return mChunkSize - mWp.offset();
+}
+
+size_t
+EncodedBuffer::writeRawVarint(uint32_t val)
+{
+ size_t size = 0;
+ while (true) {
+ size++;
+ if ((val & ~0x7F) == 0) {
+ *writeBuffer() = (uint8_t) val;
+ mWp.move();
+ return size;
+ } else {
+ *writeBuffer() = (uint8_t)((val & 0x7F) | 0x80);
+ mWp.move();
+ val >>= 7;
+ }
+ }
+}
+
+size_t
+EncodedBuffer::writeHeader(uint32_t fieldId, uint8_t wireType)
+{
+ return writeRawVarint((fieldId << 3) | wireType);
+}
+
+/********************************* Read APIs ************************************************/
+EncodedBuffer::iterator
+EncodedBuffer::begin() const
+{
+ return EncodedBuffer::iterator(*this);
+}
+
+EncodedBuffer::iterator::iterator(const EncodedBuffer& buffer)
+ :mData(buffer),
+ mRp(buffer.mChunkSize)
+{
+}
+
+size_t
+EncodedBuffer::iterator::size() const
+{
+ return mData.size();
+}
+
+size_t
+EncodedBuffer::iterator::bytesRead() const
+{
+ return mRp.pos();
+}
+
+EncodedBuffer::Pointer*
+EncodedBuffer::iterator::rp()
+{
+ return &mRp;
+}
+
+uint8_t const*
+EncodedBuffer::iterator::readBuffer()
+{
+ return hasNext() ? const_cast<uint8_t const*>(mData.at(mRp)) : NULL;
+}
+
+size_t
+EncodedBuffer::iterator::currentToRead()
+{
+ return (mData.mWp.index() > mRp.index()) ?
+ mData.mChunkSize - mRp.offset() :
+ mData.mWp.offset() - mRp.offset();
+}
+
+bool
+EncodedBuffer::iterator::hasNext()
+{
+ return mRp.pos() < mData.mWp.pos();
+}
+
+uint8_t
+EncodedBuffer::iterator::next()
+{
+ uint8_t res = *(mData.at(mRp));
+ mRp.move();
+ return res;
+}
+
+uint32_t
+EncodedBuffer::iterator::readRawVarint()
+{
+ uint32_t val = 0, shift = 0;
+ while (true) {
+ uint8_t byte = next();
+ val += (byte & 0x7F) << shift;
+ if ((byte & 0x80) == 0) break;
+ shift += 7;
+ }
+ return val;
+}
+
+} // util
+} // android
diff --git a/cmds/incidentd/src/protobuf.cpp b/libs/protoutil/src/protobuf.cpp
similarity index 67%
rename from cmds/incidentd/src/protobuf.cpp
rename to libs/protoutil/src/protobuf.cpp
index 4fffec1..ec5325c 100644
--- a/cmds/incidentd/src/protobuf.cpp
+++ b/libs/protoutil/src/protobuf.cpp
@@ -14,14 +14,19 @@
* limitations under the License.
*/
-#include "protobuf.h"
+#include <android/util/protobuf.h>
-uint8_t read_wire_type(uint32_t varint)
+namespace android {
+namespace util {
+
+uint8_t
+read_wire_type(uint32_t varint)
{
return (uint8_t) (varint & 0x07);
}
-uint32_t read_field_id(uint32_t varint)
+uint32_t
+read_field_id(uint32_t varint)
{
return varint >> 3;
}
@@ -49,24 +54,5 @@
return buf;
}
-size_t
-write_raw_varint(vector<uint8_t>* buf, uint32_t val)
-{
- size_t size = 0;
- while (true) {
- size++;
- if ((val & ~0x7F) == 0) {
- buf->push_back((uint8_t) val);
- return size;
- } else {
- buf->push_back((uint8_t)((val & 0x7F) | 0x80));
- val >>= 7;
- }
- }
-}
-
-size_t
-write_header(vector<uint8_t>* buf, uint32_t fieldId, uint8_t wireType)
-{
- return write_raw_varint(buf, (fieldId << 3) | wireType);
-}
\ No newline at end of file
+} // util
+} // android
diff --git a/media/java/android/media/AmrInputStream.java b/media/java/android/media/AmrInputStream.java
index fb91bbb..efaf224 100644
--- a/media/java/android/media/AmrInputStream.java
+++ b/media/java/android/media/AmrInputStream.java
@@ -25,12 +25,12 @@
/**
- * AmrInputStream
+ * DO NOT USE
* @hide
*/
public final class AmrInputStream extends InputStream {
private final static String TAG = "AmrInputStream";
-
+
// frame is 20 msec at 8.000 khz
private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000;
@@ -51,10 +51,10 @@
private byte[] mOneByte = new byte[1];
/**
- * Create a new AmrInputStream, which converts 16 bit PCM to AMR
- * @param inputStream InputStream containing 16 bit PCM.
+ * DO NOT USE - use MediaCodec instead
*/
public AmrInputStream(InputStream inputStream) {
+ Log.w(TAG, "@@@@ AmrInputStream is not a public API @@@@");
mInputStream = inputStream;
MediaFormat format = new MediaFormat();
@@ -83,17 +83,26 @@
mInfo = new BufferInfo();
}
+ /**
+ * DO NOT USE
+ */
@Override
public int read() throws IOException {
int rtn = read(mOneByte, 0, 1);
return rtn == 1 ? (0xff & mOneByte[0]) : -1;
}
+ /**
+ * DO NOT USE
+ */
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
+ /**
+ * DO NOT USE
+ */
@Override
public int read(byte[] b, int offset, int length) throws IOException {
if (mCodec == null) {
@@ -131,19 +140,15 @@
}
}
- // now read encoded data from the encoder (blocking, since we just filled up the
- // encoder's input with data it should be able to output at least one buffer)
- while (true) {
- int index = mCodec.dequeueOutputBuffer(mInfo, -1);
- if (index >= 0) {
- mBufIn = mInfo.size;
- ByteBuffer out = mCodec.getOutputBuffer(index);
- out.get(mBuf, 0 /* offset */, mBufIn /* length */);
- mCodec.releaseOutputBuffer(index, false /* render */);
- if ((mInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
- mSawOutputEOS = true;
- }
- break;
+ // now read encoded data from the encoder
+ int index = mCodec.dequeueOutputBuffer(mInfo, 0);
+ if (index >= 0) {
+ mBufIn = mInfo.size;
+ ByteBuffer out = mCodec.getOutputBuffer(index);
+ out.get(mBuf, 0 /* offset */, mBufIn /* length */);
+ mCodec.releaseOutputBuffer(index, false /* render */);
+ if ((mInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ mSawOutputEOS = true;
}
}
}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 3b9a5de..26ead3d 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -19,12 +19,14 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.media.AudioAttributesProto;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -177,7 +179,7 @@
/**
* IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES
- * if applicable.
+ * if applicable, as well as audioattributes.proto.
*/
/**
@@ -850,6 +852,21 @@
}
/** @hide */
+ public void toProto(ProtoOutputStream proto) {
+ proto.write(AudioAttributesProto.USAGE, mUsage);
+ proto.write(AudioAttributesProto.CONTENT_TYPE, mContentType);
+ proto.write(AudioAttributesProto.FLAGS, mFlags);
+ // mFormattedTags is never null due to assignment in Builder or unmarshalling.
+ for (String t : mFormattedTags.split(";")) {
+ t = t.trim();
+ if (t != "") {
+ proto.write(AudioAttributesProto.TAGS, t);
+ }
+ }
+ // TODO: is the data in mBundle useful for debugging?
+ }
+
+ /** @hide */
public String usageToString() {
return usageToString(mUsage);
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 186b265..dab7632a 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4119,7 +4119,15 @@
Log.w(TAG, "updateAudioPortCache: listAudioPatches failed");
return status;
}
- } while (patchGeneration[0] != portGeneration[0]);
+ // Loop until patch generation is the same as port generation unless audio ports
+ // and audio patches are not null.
+ } while (patchGeneration[0] != portGeneration[0]
+ && (ports == null || patches == null));
+ // If the patch generation doesn't equal port generation, return ERROR here in case
+ // of mismatch between audio ports and audio patches.
+ if (patchGeneration[0] != portGeneration[0]) {
+ return ERROR;
+ }
for (int i = 0; i < newPatches.size(); i++) {
for (int j = 0; j < newPatches.get(i).sources().length; j++) {
diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java
index c152245..ac3904a 100644
--- a/media/java/android/media/AudioPortEventHandler.java
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -17,6 +17,7 @@
package android.media;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import java.util.ArrayList;
@@ -30,6 +31,7 @@
class AudioPortEventHandler {
private Handler mHandler;
+ private HandlerThread mHandlerThread;
private final ArrayList<AudioManager.OnAudioPortUpdateListener> mListeners =
new ArrayList<AudioManager.OnAudioPortUpdateListener>();
@@ -40,6 +42,8 @@
private static final int AUDIOPORT_EVENT_SERVICE_DIED = 3;
private static final int AUDIOPORT_EVENT_NEW_LISTENER = 4;
+ private static final long RESCHEDULE_MESSAGE_DELAY_MS = 100;
+
/**
* Accessed by native methods: JNI Callback context.
*/
@@ -51,11 +55,12 @@
if (mHandler != null) {
return;
}
- // find the looper for our new event handler
- Looper looper = Looper.getMainLooper();
+ // create a new thread for our new event handler
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
- if (looper != null) {
- mHandler = new Handler(looper) {
+ if (mHandlerThread.getLooper() != null) {
+ mHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
ArrayList<AudioManager.OnAudioPortUpdateListener> listeners;
@@ -86,6 +91,12 @@
if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) {
int status = AudioManager.updateAudioPortCache(ports, patches, null);
if (status != AudioManager.SUCCESS) {
+ // Since audio ports and audio patches are not null, the return
+ // value could be ERROR due to inconsistency between port generation
+ // and patch generation. In this case, we need to reschedule the
+ // message to make sure the native callback is done.
+ sendMessageDelayed(obtainMessage(msg.what, msg.obj),
+ RESCHEDULE_MESSAGE_DELAY_MS);
return;
}
}
@@ -132,6 +143,9 @@
@Override
protected void finalize() {
native_finalize();
+ if (mHandlerThread.isAlive()) {
+ mHandlerThread.quit();
+ }
}
private native void native_finalize();
@@ -168,6 +182,10 @@
Handler handler = eventHandler.handler();
if (handler != null) {
Message m = handler.obtainMessage(what, arg1, arg2, obj);
+ if (what != AUDIOPORT_EVENT_NEW_LISTENER) {
+ // Except AUDIOPORT_EVENT_NEW_LISTENER, we can only respect the last message.
+ handler.removeMessages(what);
+ }
handler.sendMessage(m);
}
}
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 1f5edfa..ba41a7b 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -2584,22 +2584,21 @@
ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder));
}
- // Note that the rotation angle from MediaMetadataRetriever for heif images
- // are CCW, while rotation in ExifInterface orientations are CW.
String rotation = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
if (rotation != null) {
int orientation = ExifInterface.ORIENTATION_NORMAL;
+ // all rotation angles in CW
switch (Integer.parseInt(rotation)) {
case 90:
- orientation = ExifInterface.ORIENTATION_ROTATE_270;
+ orientation = ExifInterface.ORIENTATION_ROTATE_90;
break;
case 180:
orientation = ExifInterface.ORIENTATION_ROTATE_180;
break;
case 270:
- orientation = ExifInterface.ORIENTATION_ROTATE_90;
+ orientation = ExifInterface.ORIENTATION_ROTATE_270;
break;
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index dc7fa8c..3308fc9 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -28,7 +28,6 @@
MediaRouterClientState getState(IMediaRouterClient client);
boolean isPlaybackActive(IMediaRouterClient client);
- boolean isGlobalBluetoothA2doOn();
void setDiscoveryRequest(IMediaRouterClient client, int routeTypes, boolean activeScan);
void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit);
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 8707ad0..70ab863 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -184,13 +184,15 @@
void updateAudioRoutes(AudioRoutesInfo newRoutes) {
boolean audioRoutesChanged = false;
+ boolean forceUseDefaultRoute = false;
+
if (newRoutes.mainType != mCurAudioRoutesInfo.mainType) {
mCurAudioRoutesInfo.mainType = newRoutes.mainType;
int name;
- if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HEADPHONES) != 0
- || (newRoutes.mainType&AudioRoutesInfo.MAIN_HEADSET) != 0) {
+ if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0
+ || (newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
name = com.android.internal.R.string.default_audio_route_name_headphones;
- } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
+ } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
} else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HDMI) != 0) {
name = com.android.internal.R.string.default_audio_route_name_hdmi;
@@ -201,10 +203,16 @@
}
mDefaultAudioVideo.mNameResId = name;
dispatchRouteChanged(mDefaultAudioVideo);
+
+ if ((newRoutes.mainType & (AudioRoutesInfo.MAIN_HEADSET
+ | AudioRoutesInfo.MAIN_HEADPHONES | AudioRoutesInfo.MAIN_USB)) != 0) {
+ forceUseDefaultRoute = true;
+ }
audioRoutesChanged = true;
}
if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
+ forceUseDefaultRoute = false;
mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
if (mCurAudioRoutesInfo.bluetoothName != null) {
if (mBluetoothA2dpRoute == null) {
@@ -233,30 +241,18 @@
Log.v(TAG, "Audio routes updated: " + newRoutes + ", a2dp=" + isBluetoothA2dpOn());
if (mSelectedRoute == null || mSelectedRoute == mDefaultAudioVideo
|| mSelectedRoute == mBluetoothA2dpRoute) {
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, getDefaultSystemAudioRoute(), false);
+ if (forceUseDefaultRoute || mBluetoothA2dpRoute == null) {
+ selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo, false);
+ } else {
+ selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute, false);
+ }
}
}
}
- RouteInfo getDefaultSystemAudioRoute() {
- boolean globalBluetoothA2doOn = false;
- try {
- globalBluetoothA2doOn = mMediaRouterService.isGlobalBluetoothA2doOn();
- } catch (RemoteException ex) {
- Log.e(TAG, "Unable to call isSystemBluetoothA2doOn.", ex);
- }
- return (globalBluetoothA2doOn && mBluetoothA2dpRoute != null)
- ? mBluetoothA2dpRoute : mDefaultAudioVideo;
- }
-
- RouteInfo getCurrentSystemAudioRoute() {
- return (isBluetoothA2dpOn() && mBluetoothA2dpRoute != null)
- ? mBluetoothA2dpRoute : mDefaultAudioVideo;
- }
-
boolean isBluetoothA2dpOn() {
try {
- return mAudioService.isBluetoothA2dpOn();
+ return mBluetoothA2dpRoute != null && mAudioService.isBluetoothA2dpOn();
} catch (RemoteException e) {
Log.e(TAG, "Error querying Bluetooth A2DP state", e);
return false;
@@ -604,13 +600,20 @@
@Override
public void onRestoreRoute() {
- // Skip restoring route if the selected route is not a system audio route, or
- // MediaRouter is initializing.
- if ((mSelectedRoute != mDefaultAudioVideo && mSelectedRoute != mBluetoothA2dpRoute)
- || mSelectedRoute == null) {
- return;
- }
- mSelectedRoute.select();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // Skip restoring route if the selected route is not a system audio route,
+ // MediaRouter is initializing, or mClient was changed.
+ if (Client.this != mClient || mSelectedRoute == null
+ || (mSelectedRoute != mDefaultAudioVideo
+ && mSelectedRoute != mBluetoothA2dpRoute)) {
+ return;
+ }
+ Log.v(TAG, "onRestoreRoute() : route=" + mSelectedRoute);
+ mSelectedRoute.select();
+ }
+ });
}
}
}
@@ -942,10 +945,12 @@
Log.v(TAG, "Selecting route: " + route);
assert(route != null);
final RouteInfo oldRoute = sStatic.mSelectedRoute;
+ final RouteInfo currentSystemRoute = sStatic.isBluetoothA2dpOn()
+ ? sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo;
boolean wasDefaultOrBluetoothRoute = (oldRoute == sStatic.mDefaultAudioVideo
|| oldRoute == sStatic.mBluetoothA2dpRoute);
if (oldRoute == route
- && (!wasDefaultOrBluetoothRoute || route == sStatic.getCurrentSystemAudioRoute())) {
+ && (!wasDefaultOrBluetoothRoute || route == currentSystemRoute)) {
return;
}
if (!route.matchesTypes(types)) {
@@ -1016,8 +1021,7 @@
static void selectDefaultRouteStatic() {
// TODO: Be smarter about the route types here; this selects for all valid.
- if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute
- && sStatic.mBluetoothA2dpRoute != null && sStatic.isBluetoothA2dpOn()) {
+ if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute && sStatic.isBluetoothA2dpOn()) {
selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mBluetoothA2dpRoute, false);
} else {
selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mDefaultAudioVideo, false);
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 4808d7a..09449a1 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -127,8 +127,9 @@
Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e);
}
synchronized (mLock) {
+ boolean attributesChanged = (mAttributes != attr);
mAttributes = attr;
- updateAppOpsPlayAudio_sync();
+ updateAppOpsPlayAudio_sync(attributesChanged);
}
}
@@ -200,16 +201,13 @@
}
void baseSetVolume(float leftVolume, float rightVolume) {
- final boolean hasAppOpsPlayAudio;
+ final boolean isRestricted;
synchronized (mLock) {
mLeftVolume = leftVolume;
mRightVolume = rightVolume;
- hasAppOpsPlayAudio = mHasAppOpsPlayAudio;
- if (isRestricted_sync()) {
- return;
- }
+ isRestricted = isRestricted_sync();
}
- playerSetVolume(!hasAppOpsPlayAudio/*muting*/,
+ playerSetVolume(isRestricted/*muting*/,
leftVolume * mPanMultiplierL, rightVolume * mPanMultiplierR);
}
@@ -250,7 +248,7 @@
private void updateAppOpsPlayAudio() {
synchronized (mLock) {
- updateAppOpsPlayAudio_sync();
+ updateAppOpsPlayAudio_sync(false);
}
}
@@ -258,7 +256,7 @@
* To be called whenever a condition that might affect audibility of this player is updated.
* Must be called synchronized on mLock.
*/
- void updateAppOpsPlayAudio_sync() {
+ void updateAppOpsPlayAudio_sync(boolean attributesChanged) {
boolean oldHasAppOpsPlayAudio = mHasAppOpsPlayAudio;
try {
int mode = AppOpsManager.MODE_IGNORED;
@@ -275,9 +273,10 @@
// AppsOps alters a player's volume; when the restriction changes, reflect it on the actual
// volume used by the player
try {
- if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio) {
+ if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio ||
+ attributesChanged) {
getService().playerHasOpPlayAudio(mPlayerIId, mHasAppOpsPlayAudio);
- if (mHasAppOpsPlayAudio) {
+ if (!isRestricted_sync()) {
if (DEBUG_APP_OPS) {
Log.v(TAG, "updateAppOpsPlayAudio: unmuting player, vol=" + mLeftVolume
+ "/" + mRightVolume);
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 80fd5c0..17b2326 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -845,6 +845,33 @@
return MtpConstants.RESPONSE_OK;
}
+ private int moveObject(int handle, int newParent, String newPath) {
+ String[] whereArgs = new String[] { Integer.toString(handle) };
+
+ // do not allow renaming any of the special subdirectories
+ if (isStorageSubDirectory(newPath)) {
+ return MtpConstants.RESPONSE_OBJECT_WRITE_PROTECTED;
+ }
+
+ // update database
+ ContentValues values = new ContentValues();
+ values.put(Files.FileColumns.DATA, newPath);
+ values.put(Files.FileColumns.PARENT, newParent);
+ int updated = 0;
+ try {
+ // note - we are relying on a special case in MediaProvider.update() to update
+ // the paths for all children in the case where this is a directory.
+ updated = mMediaProvider.update(mObjectsUri, values, ID_WHERE, whereArgs);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in mMediaProvider.update", e);
+ }
+ if (updated == 0) {
+ Log.e(TAG, "Unable to update path for " + handle + " to " + newPath);
+ return MtpConstants.RESPONSE_GENERAL_ERROR;
+ }
+ return MtpConstants.RESPONSE_OK;
+ }
+
private int setObjectProperty(int handle, int property,
long intValue, String stringValue) {
switch (property) {
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 02667ca..1d85c972 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -38,7 +38,7 @@
"libmediametrics",
"libmediadrm",
"libmidi",
- "libskia",
+ "libhwui",
"libui",
"liblog",
"libcutils",
diff --git a/media/jni/android_media_MediaDescrambler.cpp b/media/jni/android_media_MediaDescrambler.cpp
index bee5218..e77e855 100644
--- a/media/jni/android_media_MediaDescrambler.cpp
+++ b/media/jni/android_media_MediaDescrambler.cpp
@@ -21,7 +21,7 @@
#include "android_media_MediaDescrambler.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_os_HwRemoteBinder.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include <android/hardware/cas/native/1.0/BpHwDescrambler.h>
#include <android/hardware/cas/native/1.0/BnHwDescrambler.h>
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index c9657b1..d2fa8f5 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -27,7 +27,7 @@
#include "android_runtime/Log.h"
#include "android_util_Binder.h"
#include "jni.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include <android/hardware/cas/1.0/BpHwCas.h>
#include <android/hardware/cas/1.0/BnHwCas.h>
diff --git a/media/jni/android_media_MediaMetricsJNI.cpp b/media/jni/android_media_MediaMetricsJNI.cpp
index 8979cec..38f7a7e 100644
--- a/media/jni/android_media_MediaMetricsJNI.cpp
+++ b/media/jni/android_media_MediaMetricsJNI.cpp
@@ -16,7 +16,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <jni.h>
-#include <JNIHelp.h>
+#include <nativehelper/JNIHelp.h>
#include "android_media_MediaMetricsJNI.h"
#include <media/MediaAnalyticsItem.h>
diff --git a/media/jni/android_media_MediaMetricsJNI.h b/media/jni/android_media_MediaMetricsJNI.h
index d174212..16081b4 100644
--- a/media/jni/android_media_MediaMetricsJNI.h
+++ b/media/jni/android_media_MediaMetricsJNI.h
@@ -19,7 +19,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <jni.h>
-#include <JNIHelp.h>
+#include <nativehelper/JNIHelp.h>
#include <media/MediaAnalyticsItem.h>
namespace android {
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index ceab478..497684c 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -35,7 +35,7 @@
#include <nativehelper/ScopedUtfChars.h>
#include "jni.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include "android_media_MediaMetricsJNI.h"
#include "android_runtime/AndroidRuntime.h"
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 5b874cd..7225f10 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -68,6 +68,7 @@
static jmethodID method_getObjectInfo;
static jmethodID method_getObjectFilePath;
static jmethodID method_deleteFile;
+static jmethodID method_moveObject;
static jmethodID method_getObjectReferences;
static jmethodID method_setObjectReferences;
static jmethodID method_sessionStarted;
@@ -178,6 +179,9 @@
virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty property);
+ virtual MtpResponseCode moveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+ MtpString& newPath);
+
virtual void sessionStarted();
virtual void sessionEnded();
@@ -995,6 +999,18 @@
return result;
}
+MtpResponseCode MyMtpDatabase::moveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+ MtpString &newPath) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jstring stringValue = env->NewStringUTF((const char *) newPath);
+ MtpResponseCode result = env->CallIntMethod(mDatabase, method_moveObject,
+ (jint)handle, (jint)newParent, stringValue);
+
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ env->DeleteLocalRef(stringValue);
+ return result;
+}
+
struct PropertyTableEntry {
MtpObjectProperty property;
int type;
@@ -1360,6 +1376,11 @@
ALOGE("Can't find deleteFile");
return -1;
}
+ method_moveObject = env->GetMethodID(clazz, "moveObject", "(IILjava/lang/String;)I");
+ if (method_moveObject == NULL) {
+ ALOGE("Can't find moveObject");
+ return -1;
+ }
method_getObjectReferences = env->GetMethodID(clazz, "getObjectReferences", "(I)[I");
if (method_getObjectReferences == NULL) {
ALOGE("Can't find getObjectReferences");
diff --git a/native/graphics/jni/Android.mk b/native/graphics/jni/Android.mk
index ec4b35a..7a40e62 100644
--- a/native/graphics/jni/Android.mk
+++ b/native/graphics/jni/Android.mk
@@ -20,14 +20,11 @@
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
- libskia \
libui \
libandroidfw
LOCAL_C_INCLUDES += \
- frameworks/base/native/include \
- frameworks/base/core/jni/android/graphics \
- frameworks/base/libs/hwui
+ frameworks/base/core/jni/android/graphics
LOCAL_MODULE:= libjnigraphics
diff --git a/native/webview/loader/loader.cpp b/native/webview/loader/loader.cpp
index 376dbb8..adb371d 100644
--- a/native/webview/loader/loader.cpp
+++ b/native/webview/loader/loader.cpp
@@ -143,17 +143,7 @@
return DoReserveAddressSpace(size);
}
-jboolean CreateRelroFile(JNIEnv* env, jclass, jstring lib32, jstring lib64,
- jstring relro32, jstring relro64) {
-#ifdef __LP64__
- jstring lib = lib64;
- jstring relro = relro64;
- (void)lib32; (void)relro32;
-#else
- jstring lib = lib32;
- jstring relro = relro32;
- (void)lib64; (void)relro64;
-#endif
+jboolean CreateRelroFile(JNIEnv* env, jclass, jstring lib, jstring relro) {
jboolean ret = JNI_FALSE;
const char* lib_utf8 = env->GetStringUTFChars(lib, NULL);
if (lib_utf8 != NULL) {
@@ -167,15 +157,8 @@
return ret;
}
-jint LoadWithRelroFile(JNIEnv* env, jclass, jstring lib, jstring relro32,
- jstring relro64, jobject clazzLoader) {
-#ifdef __LP64__
- jstring relro = relro64;
- (void)relro32;
-#else
- jstring relro = relro32;
- (void)relro64;
-#endif
+jint LoadWithRelroFile(JNIEnv* env, jclass, jstring lib, jstring relro,
+ jobject clazzLoader) {
jint ret = LIBLOAD_FAILED_JNI_CALL;
const char* lib_utf8 = env->GetStringUTFChars(lib, NULL);
if (lib_utf8 != NULL) {
@@ -196,10 +179,10 @@
{ "nativeReserveAddressSpace", "(J)Z",
reinterpret_cast<void*>(ReserveAddressSpace) },
{ "nativeCreateRelroFile",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
+ "(Ljava/lang/String;Ljava/lang/String;)Z",
reinterpret_cast<void*>(CreateRelroFile) },
{ "nativeLoadWithRelroFile",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)I",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)I",
reinterpret_cast<void*>(LoadWithRelroFile) },
};
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 3800e6f..4a771eb 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -16,8 +16,6 @@
package com.android.defcontainer;
-import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
-
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
@@ -31,21 +29,15 @@
import android.content.res.ObbInfo;
import android.content.res.ObbScanner;
import android.os.Binder;
-import android.os.Environment;
import android.os.Environment.UserEnvironment;
-import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.StructStatVfs;
import android.util.Slog;
import com.android.internal.app.IMediaContainerService;
-import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.os.IParcelFileDescriptorFactory;
import com.android.internal.util.ArrayUtils;
@@ -72,51 +64,6 @@
private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
/**
- * Creates a new container and copies package there.
- *
- * @param packagePath absolute path to the package to be copied. Can be
- * a single monolithic APK file or a cluster directory
- * containing one or more APKs.
- * @param containerId the id of the secure container that should be used
- * for creating a secure container into which the resource
- * will be copied.
- * @param key Refers to key used for encrypting the secure container
- * @return Returns the new cache path where the resource has been copied
- * into
- */
- @Override
- public String copyPackageToContainer(String packagePath, String containerId, String key,
- boolean isExternal, boolean isForwardLocked, String abiOverride) {
- if (packagePath == null || containerId == null) {
- return null;
- }
-
- if (isExternal) {
- // Make sure the sdcard is mounted.
- String status = Environment.getExternalStorageState();
- if (!status.equals(Environment.MEDIA_MOUNTED)) {
- Slog.w(TAG, "Make sure sdcard is mounted.");
- return null;
- }
- }
-
- PackageLite pkg = null;
- NativeLibraryHelper.Handle handle = null;
- try {
- final File packageFile = new File(packagePath);
- pkg = PackageParser.parsePackageLite(packageFile, 0);
- handle = NativeLibraryHelper.Handle.create(pkg);
- return copyPackageToContainerInner(pkg, handle, containerId, key, isExternal,
- isForwardLocked, abiOverride);
- } catch (PackageParserException | IOException e) {
- Slog.w(TAG, "Failed to copy package at " + packagePath, e);
- return null;
- } finally {
- IoUtils.closeQuietly(handle);
- }
- }
-
- /**
* Copy package to the target location.
*
* @param packagePath absolute path to the package to be copied. Can be
@@ -153,7 +100,6 @@
public PackageInfoLite getMinimalPackageInfo(String packagePath, int flags,
String abiOverride) {
final Context context = DefaultContainerService.this;
- final boolean isForwardLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
PackageInfoLite ret = new PackageInfoLite();
if (packagePath == null) {
@@ -167,7 +113,7 @@
final long sizeBytes;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
- sizeBytes = PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
+ sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride);
} catch (PackageParserException | IOException e) {
Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);
@@ -230,13 +176,13 @@
* containing one or more APKs.
*/
@Override
- public long calculateInstalledSize(String packagePath, boolean isForwardLocked,
- String abiOverride) throws RemoteException {
+ public long calculateInstalledSize(String packagePath, String abiOverride)
+ throws RemoteException {
final File packageFile = new File(packagePath);
final PackageParser.PackageLite pkg;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
- return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
+ return PackageHelper.calculateInstalledSize(pkg, abiOverride);
} catch (PackageParserException | IOException e) {
Slog.w(TAG, "Failed to calculate installed size: " + e);
return Long.MAX_VALUE;
@@ -292,60 +238,6 @@
return mBinder;
}
- private String copyPackageToContainerInner(PackageLite pkg, NativeLibraryHelper.Handle handle,
- String newCid, String key, boolean isExternal, boolean isForwardLocked,
- String abiOverride) throws IOException {
-
- // Calculate container size, rounding up to nearest MB and adding an
- // extra MB for filesystem overhead
- final long sizeBytes = PackageHelper.calculateInstalledSize(pkg, handle,
- isForwardLocked, abiOverride);
-
- // Create new container
- final String newMountPath = PackageHelper.createSdDir(sizeBytes, newCid, key,
- Process.myUid(), isExternal);
- if (newMountPath == null) {
- throw new IOException("Failed to create container " + newCid);
- }
- final File targetDir = new File(newMountPath);
-
- try {
- // Copy all APKs
- copyFile(pkg.baseCodePath, targetDir, "base.apk", isForwardLocked);
- if (!ArrayUtils.isEmpty(pkg.splitNames)) {
- for (int i = 0; i < pkg.splitNames.length; i++) {
- copyFile(pkg.splitCodePaths[i], targetDir,
- "split_" + pkg.splitNames[i] + ".apk", isForwardLocked);
- }
- }
-
- // Extract native code
- final File libraryRoot = new File(targetDir, LIB_DIR_NAME);
- final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
- abiOverride);
- if (res != PackageManager.INSTALL_SUCCEEDED) {
- throw new IOException("Failed to extract native code, res=" + res);
- }
-
- if (!PackageHelper.finalizeSdDir(newCid)) {
- throw new IOException("Failed to finalize " + newCid);
- }
-
- if (PackageHelper.isContainerMounted(newCid)) {
- PackageHelper.unMountSdDir(newCid);
- }
-
- } catch (ErrnoException e) {
- PackageHelper.destroySdDir(newCid);
- throw e.rethrowAsIOException();
- } catch (IOException e) {
- PackageHelper.destroySdDir(newCid);
- throw e;
- }
-
- return newMountPath;
- }
-
private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target)
throws IOException, RemoteException {
copyFile(pkg.baseCodePath, target, "base.apk");
@@ -373,28 +265,4 @@
IoUtils.closeQuietly(in);
}
}
-
- private void copyFile(String sourcePath, File targetDir, String targetName,
- boolean isForwardLocked) throws IOException, ErrnoException {
- final File sourceFile = new File(sourcePath);
- final File targetFile = new File(targetDir, targetName);
-
- Slog.d(TAG, "Copying " + sourceFile + " to " + targetFile);
- if (!FileUtils.copyFile(sourceFile, targetFile)) {
- throw new IOException("Failed to copy " + sourceFile + " to " + targetFile);
- }
-
- if (isForwardLocked) {
- final String publicTargetName = PackageHelper.replaceEnd(targetName,
- ".apk", ".zip");
- final File publicTargetFile = new File(targetDir, publicTargetName);
-
- PackageHelper.extractPublicFiles(sourceFile, publicTargetFile);
-
- Os.chmod(targetFile.getAbsolutePath(), 0640);
- Os.chmod(publicTargetFile.getAbsolutePath(), 0644);
- } else {
- Os.chmod(targetFile.getAbsolutePath(), 0644);
- }
- }
}
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index 334952c..08ff4ca 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -3,7 +3,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="8016145283189546017">"Inputenheder"</string>
<string name="keyboard_layouts_label" msgid="6688773268302087545">"Android-tastatur"</string>
- <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"Engelsk (UK)"</string>
+ <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"Engelsk (Storbritannien)"</string>
<string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"engelsk (USA)"</string>
<string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Engelsk (USA), international stil"</string>
<string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Engelsk (USA), Colemak-stil"</string>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 0e0d9ae..cf9515e 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Niks"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Wag vir ontfouter"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Ontfoutde program wag vir ontfouter om te heg voordat dit uitgevoer word"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telefonie-monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Telefonie-monitor sal loglêers insamel wanneer dit \'n probleem met telefonie-/modemfunksionaliteit bespeur en \'n kennisgewing aan die gebruiker stuur om \'n fout in te dien"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Invoer"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Skets"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardeware-versnelde lewering"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 017722f..a84ee81 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"ምንም"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"ስህተት ማስወገጃውን ጠብቅ"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"ስህተቱ የተወገደለት መተግበሪያ ከመፈጸሙ በፊት የስህተት ማስወገጃው እስኪያያዝ ድረስ እየጠበቀው ነው"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"የቴሌፎኒ መከታተያ"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor በቴሌፎኒ/ሞደም ተግባር ላይ ችግር እንዳለ ሲያገኝ የምዝግብ ማስታወሻዎችን ይሰበስብና ተጠቃሚው ሳንካ እንዲያስመዘግቡ በማሳወቂያ ይጠይቃቸዋል"</string>
<string name="debug_input_category" msgid="1811069939601180246">"ግብዓት"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"ስዕል"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"የተፋጠነ የሃርድዌር አሰጣጥ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 2c39566..b9a16b96 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"لا شيء"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"انتظار برنامج التصحيح"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"ينتظر التطبيق قيد التصحيح انضمام برنامج التصحيح قبل التنفيذ"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"خدمة مراقبة الاتصالات الهاتفية"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"ستجمع خدمة مراقبة الاتصالات الهاتفية سجلات عند اكتشاف مشكلة متعلقة بوظائف الاتصالات الهاتفية أو المودم، وإرسال إشعار إلى المستخدم لإرسال تقرير بالخطأ"</string>
<string name="debug_input_category" msgid="1811069939601180246">"الإدخال"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"رسم"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"عرض تسارع الأجهزة"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 33e9e85..0a86706 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Heç nə"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Sazlamanı gözləyin"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Sazlanmış tətbiq icradan əvvəl qoşulmaq üçün sazlayıcı gözləyir"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telefoniya Monitoru"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Telefoniya Monitoru telefoniya/modem funksionallığı ilə bağlı problem aşkar etdikdə qeydiyyatları toplayır və baq haqqında istifadəçiyə bildiriş göndərir"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Daxiletmə"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Təsvir"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Avadanlıq qaldırma renderi"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 48e8e6d..1e84d3d 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nijedna"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Sačekaj program za otklanjanje grešaka"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Aplikacija čeka program za otklanjanje grešaka da priloži pre izvršavanja"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"TelephonyMonitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor će prikupljati evidenciju kada otkrije problem sa funkcionisanjem telefonije/modema i zatražiće od korisnika da prijavi grešku"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Unos"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Crtanje"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardverski ubrzano prikazivanje"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 551a86d..e47bc43 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Нічога"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Пачакайце адладчык"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Адладжанае прыкладанне чакае далучэння да iнструмента для адладкi перад працай"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Сродак адсочвання тэлефаніі"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Праграма TelephonyMonitor будзе заносіць у журналы выпадкі выяўлення праблем, што датычацца фукнцый тэлефаніі/мадэма, а таксама паказваць карыстальніку адпаведнае апавяшчэнне з прапановай адпраўкі паведамлення пра памылку"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Увод"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Чарцёж"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Апаратнае паскарэнне рэндэрынгу"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 802e66e..77beb4c 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Няма"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Изчакване на инструмента за отстраняване на грешки"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Прил. изчаква инстр. за отстраняване на грешки да се прикачи преди изпълнение"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Наблюдение на телефонията"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Функцията за наблюдение на телефонията ще събира регистрационни файлове, когато установи проблем с функционалността на телефонията/модема, и ще изпрати на потребителя известие с подкана да подаде сигнал за програмна грешка"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Въвеждане"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Начертаване"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Хардуерно ускорено изобразяване"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 9e2ef59..424e7b9 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"কিছুই না"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"ডিবাগারের জন্য অপেক্ষা করুন"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"চালানোর আগে সংযুক্ত করতে জন্য ডিবাগ করা অ্যাপ্লিকেশনটি ডিবাগারের জন্য অপেক্ষা করছে"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"টেলিফোনি মনিটর"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"টেলিফোনি মনিটর টেলিফোনি/মোডেমের কার্যকারিতায় কোনও সমস্যা শনাক্ত করলে সমস্যাটি লগ করবে এবং সমস্যাটি জানাতে একটি বাগ ফাইল করার জন্য ব্যবহারকারিকে বিজ্ঞপ্তি পাঠাবে"</string>
<string name="debug_input_category" msgid="1811069939601180246">"ইনপুট"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"অঙ্কন"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"হার্ডওয়্যার দ্বারা চালিত রেন্ডারিং"</string>
@@ -289,7 +287,7 @@
<string name="transition_animation_scale_title" msgid="387527540523595875">"ট্র্যানজিশন অ্যানিমেশন স্কেল"</string>
<string name="animator_duration_scale_title" msgid="3406722410819934083">"অ্যানিমেটর সময়কাল স্কেল"</string>
<string name="overlay_display_devices_title" msgid="5364176287998398539">"গৌণ প্রদর্শনগুলি নকল করুন"</string>
- <string name="debug_applications_category" msgid="4206913653849771549">"অ্যাপ্লিকেশানগুলি"</string>
+ <string name="debug_applications_category" msgid="4206913653849771549">"অ্যাপ"</string>
<string name="immediately_destroy_activities" msgid="1579659389568133959">"কার্যকলাপ রাখবেন না"</string>
<string name="immediately_destroy_activities_summary" msgid="3592221124808773368">"ব্যবহারকারী এটি ছেড়ে যাওয়ার পরে যত তাড়াতাড়ি সম্ভব প্রতিটি কার্যকলাপ ধ্বংস করুন"</string>
<string name="app_process_limit_title" msgid="4280600650253107163">"পশ্চাদপট প্রক্রিয়ার সীমা"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index d16ec3c..97cbfbf 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -71,7 +71,7 @@
<string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Dijeljenje kontakta"</string>
<string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Koristi za dijeljenje kontakta"</string>
<string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Dijeljenje internet veze"</string>
- <string name="bluetooth_profile_map" msgid="1019763341565580450">"Tekstualne poruke"</string>
+ <string name="bluetooth_profile_map" msgid="1019763341565580450">"SMS-ovi"</string>
<string name="bluetooth_profile_sap" msgid="5764222021851283125">"Pristup SIM-u"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD audio"</string>
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Ništa"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Pričekajte na program za otklanjanje grešaka"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Aplikacija u kojoj se otklanjaju greške čeka da se priloži program za otklanjanje grešaka prije izvršavanja"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Nadzor telefonije"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Nadzor telefonije će prikupiti zapisnike kada otkrije problem u funkcioniranju telefona/modema i obavijestiti korisnika da prijavi grešku"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Ulaz"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Crtanje"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Prikaz s hardverskom akceleracijom"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index ded9352..479cf38 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Cap"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Espera el depurador"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Abans d\'executar-se, l\'aplicació de depuració espera que es connecti el depurador"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Monitor de telefonia"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Quan el monitor de telefonia detecta un problema amb la funció de telefonia/mòdem, recopila registres i mostra una notificació a l\'usuari perquè informi de l\'error"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Entrada"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Dibuix"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Renderització accelerada per maquinari"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 5afa00c..97a76f7 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nic"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Počkat na ladicí program"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Aplikace čeká na připojení ladicího programu"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Služba Telephony Monitor bude zaznamenávat protokoly problémů s funkcemi telefonu či modemu a zobrazí uživateli oznámení, že je třeba vyplnit hlášení o chybě"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Vstup"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Vykreslování"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardwarově urychlené vykreslování"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 275822c..6a006cc 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Ingen"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Vent på fejlfinder"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Fejlrettet app venter på tilknytning af fejlfinder før udførelse"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Overvågning af telefoni"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Overvågning af telefoni indsamler logfiler, når der registreres et problem med telefoni-/modemfunktioner, og sender brugeren en underretning, der beder vedkommende om at indsende en fejlrapport"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Input"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Tegning"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardware-accelereret gengivelse"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 5f11b51..2203bc6 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Keine"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Auf Debugger warten"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"App wartet vor der Ausführung auf den Start des Debuggers"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Mit Telephony Monitor werden Protokolle erfasst, wenn ein Problem mit der Telefon- oder der Modemfunktion entdeckt wird. Nutzer werden dazu aufgefordert, den Programmfehler zu melden."</string>
<string name="debug_input_category" msgid="1811069939601180246">"Eingabe"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Zeichnung"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardwarebeschleunigtes Rendering"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index cdb9518..514cfd8 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Καμία"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Περιμένετε το εργαλείο εντοπισμού σφαλμάτων"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Αναμονή εφαρμογής για να συνδεθεί ο εντοπισμός σφαλμάτων"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Παρακολούθηση τηλεφωνίας"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Η υπηρεσία TelephonyMonitor θα συλλέξει αρχεία καταγραφής μόλις εντοπίσει κάποιο πρόβλημα στη λειτουργία του τηλεφώνου/μόντεμ και θα εμφανίσει μια ειδοποίηση στον χρήστη για να υποβάλει αναφορά σφάλματος"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Εισαγωγή"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Σχέδιο"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Απόδοση με επιτάχυνση από υλικό εξοπλισμό"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index c964367..aa5af8e 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nothing"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Wait for debugger"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Debugged application waits for debugger to attach before executing"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor will collect logs when it detects a problem with telephony/modem functionality and prompt notification to user to file a bug"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Input"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Drawing"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardware accelerated rendering"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index c964367..aa5af8e 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nothing"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Wait for debugger"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Debugged application waits for debugger to attach before executing"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor will collect logs when it detects a problem with telephony/modem functionality and prompt notification to user to file a bug"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Input"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Drawing"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardware accelerated rendering"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index c964367..aa5af8e 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nothing"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Wait for debugger"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Debugged application waits for debugger to attach before executing"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor will collect logs when it detects a problem with telephony/modem functionality and prompt notification to user to file a bug"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Input"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Drawing"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardware accelerated rendering"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index c964367..aa5af8e 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nothing"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Wait for debugger"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Debugged application waits for debugger to attach before executing"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor will collect logs when it detects a problem with telephony/modem functionality and prompt notification to user to file a bug"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Input"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Drawing"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardware accelerated rendering"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 9c313ee..66d8fe0 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nothing"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Wait for debugger"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Debugged application waits for debugger to attach before executing"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor will collect logs when it detects a problem with telephony/modem functionality and prompt notification to user to file a bug"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Input"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Drawing"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardware accelerated rendering"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index fdf39c8..743700e 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Ninguna"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Esperar al depurador"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Esperar que se conecte el depurador para iniciar la aplicación"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Telephony Monitor recopilará registros cuando se detecte un problema con la funcionalidad de telefonía/módem y además enviará al usuario una notificación para que reporte el error"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Entrada"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Dibujo"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Representación acelerada mediante hardware"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 6065bb0..d964728 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Ninguna"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Esperar al depurador"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"La aplicación depurada espera a que se active el depurador para ejecutarse"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Monitor de telefonía"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor recopilará los registros al detectar un problema con la función de módem o telefonía y mostrará una notificación al usuario para registrar un error"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Entrada"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Dibujo"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Renderización acelerada por hardware"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 48b7719..cfcc4c6 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Mitte ühtegi"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Oodake silurit"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Silutud rakendus ootab toimimiseks siluri lisamist"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Teenus Telephony Monitor kogub telefoni/modemi funktsioonide probleemide korral logisid ja esitab kasutajale märguande veaaruande esitamiseks"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Sisend"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Joonis"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Tarkvarakiirendusega renderdamine"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index fc60954..d7595e8 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Ezer ez"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Itxaron araztaileari"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Araztutako aplikazioa araztailea erantsi arte itxaroten ari da exekutatu aurretik"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telefono-gainbegiratzailea"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Telefono-gainbegiratzaileak erregistroak bilduko ditu telefono/modem funtzioarekin arazoren bat dagoela hautematen duenean, eta akatsaren berri emateko eskatuko dio erabiltzaileari"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Sarrera"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Marrazkia"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardware bidez bizkortutako errendatzea"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 2500d5d..f582486 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"هیچ چیز"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"انتظار برای اشکالزدا"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"برنامه اشکالزدایی شده منتظر پیوست شدن اشکالزدا قبل از اجرا است"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor وقتی مشکلی در ارتباط با عملکرد تلفن/مودم تشخیص میدهد، گزارش جمعآوری میکند و با اعلانی از کاربر میخواهد گزارش اشکال تهیه کند"</string>
<string name="debug_input_category" msgid="1811069939601180246">"ورودی"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"طراحی"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"پردازش سختافزاری سریع"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 414a4c7f..83073d1 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Ei mitään"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Odota vianetsintää"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Sovellus odottaa vianetsinnän lisäämistä, ja käynnistyy sen jälkeen."</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Puhelinpalvelujen seuranta"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Puhelinpalvelujen seuranta kerää tietoja virheistä puhelinpalvelujen tai modeemin toiminnassa ja kehottaa käyttäjää tekemään virheilmoituksen."</string>
<string name="debug_input_category" msgid="1811069939601180246">"Syöte"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Piirustus"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Laitteistokiihdytetty hahmonnus"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 6306306..54d6864 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Aucune"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Attendre l\'intervention du débogueur"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Avant de s\'exécuter, l\'application déboguée doit attendre que le débogueur soit attaché."</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"TelephonyMonitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor recueille des journaux lorsqu\'il détecte un problème lié à la fonctionnalité de téléphonie ou de modem, puis invite l\'utilisateur à soumettre un bogue."</string>
<string name="debug_input_category" msgid="1811069939601180246">"Entrée"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Dessin"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Accélération matérielle"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index f8cf107..e35d330 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Aucune"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Attendre l\'intervention du débogueur"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Avant de s\'exécuter, l\'application déboguée doit attendre que le débogueur soit attaché."</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Telephony Monitor recueille des journaux lorsqu\'il détecte un problème lié à la fonctionnalité de téléphonie ou de modem, puis invite l\'utilisateur à créer un bug"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Saisie"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Tracé"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Accélération matérielle"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 39fd72c..8291cb1 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nada"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Agardar polo depurador"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"A aplicación depurada agarda a que o depurador se conecte antes de executarse"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Monitor de telefonía"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"O monitor de telefonía recompilará rexistros cando detecte un problema coa función da telefonía ou do módem e enviará unha notificación ao usuario para solicitarlle que informe dun erro"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Entrada"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Debuxo"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Procesamento acelerado mediante hardware"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index fccac47..ca14118 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"કંઈ નહીં"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"ડીબગર માટે રાહ જુઓ"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"ડીબગ કરેલ ઍપ્લિકેશનો ક્રિયાન્વિત થતા પહેલાં ડીબગર જોડાઈ તેની રાહ જુએ છે"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor ને જ્યારે ટેલિફોની/મૉડેમની કાર્યક્ષમતામાં કોઈ સમસ્યા મળે ત્યારે તે લૉગ એકત્રિત કરશે અને વપરાશકર્તાને બગની જાણ કરવાની સૂચનાનો સંકેત આપશે"</string>
<string name="debug_input_category" msgid="1811069939601180246">"ઇનપુટ"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"રેખાંકન"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"હાર્ડવેર પ્રવેગક રેન્ડરિંગ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 7b01903..bde80f2 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -45,7 +45,7 @@
<string name="available_via_carrier" msgid="1469036129740799053">"%1$s के ज़रिए उपलब्ध"</string>
<string name="speed_label_very_slow" msgid="1867055264243608530">"अत्यधिक धीमी"</string>
<string name="speed_label_slow" msgid="813109590815810235">"धीमी"</string>
- <string name="speed_label_okay" msgid="2331665440671174858">"ठीक"</string>
+ <string name="speed_label_okay" msgid="2331665440671174858">"ठीक है"</string>
<string name="speed_label_medium" msgid="3175763313268941953">"मध्यम"</string>
<string name="speed_label_fast" msgid="7715732164050975057">"तेज़"</string>
<string name="speed_label_very_fast" msgid="2265363430784523409">"अत्यधिक तेज़"</string>
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"कुछ भी नहीं"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"डीबगर की प्रतीक्षा करें"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"डीबग किया गया ऐप्स निष्पादन के पहले अनुलग्न करने के लिए डीबगर की प्रतीक्षा करता है"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"टेलीफ़ोनी मॉनिटर"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"टेलीफ़ोनी मॉनिटर को जब टेलीफ़ोनी/मॉडेम के फंक्शन में कोई समस्या मिलती है, तो वह लॉग इकट्ठा करता है और उपयोगकर्ता को एक गड़बड़ी दर्ज करने के लिए सूचना देता है"</string>
<string name="debug_input_category" msgid="1811069939601180246">"हिंदी में लिखें"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"ड्रॉइंग"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"हार्डवेयर त्वरित रेंडरिंग"</string>
@@ -289,7 +287,7 @@
<string name="transition_animation_scale_title" msgid="387527540523595875">"संक्रमण एनिमेशन स्केल"</string>
<string name="animator_duration_scale_title" msgid="3406722410819934083">"एनिमेटर अवधि स्केल"</string>
<string name="overlay_display_devices_title" msgid="5364176287998398539">"द्वितीयक डिस्प्ले अनुरूपित करें"</string>
- <string name="debug_applications_category" msgid="4206913653849771549">"ऐप्स"</string>
+ <string name="debug_applications_category" msgid="4206913653849771549">"ऐप"</string>
<string name="immediately_destroy_activities" msgid="1579659389568133959">"गतिविधियों को न रखें"</string>
<string name="immediately_destroy_activities_summary" msgid="3592221124808773368">"उपयोगकर्ता के छोड़ते ही हर गतिविधि को खत्म करें"</string>
<string name="app_process_limit_title" msgid="4280600650253107163">"पृष्ठभूमि प्रक्रिया सीमा"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index a58561a..ec2b9cf 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Ništa"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Čeka se program za otklanjanje pogrešaka"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Aplikacija čeka priključivanje programa za otklanjanje pogrešaka"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"TelephonyMonitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor prikupljat će zapisnike kada otkrije problem s funkcioniranjem telefona/modema i obavijestiti korisnika da prijavi programsku pogrešku"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Ulaz"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Crtež"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardverski ubrzano renderiranje"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 82e7432..3fe6a78 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Semmi"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Várjon a hibakeresőre."</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"A javított alkalmazás a hibakeresőre vár."</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"A TelephonyMonitor begyűjti a naplókat, ha problémát észlel a telefonos szolgáltatások vagy a modem működésében, és javasolja a felhasználónak a hiba jelentését"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Bevitel"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Rajz"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardveres gyorsítású megjelenítés"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 9e6e101..119e24c 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Ոչինչ"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Սպասել վրիպազերծիչին"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Վրիպազերծված ծրագրիը սպասում է վրիպազերծիչի կցմանը մինչ կատարումը"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Հեռախոսակապի/մոդեմի հետ կապված խնդիրներ հայտնաբերելու դեպքում TelephonyMonitor-ը կհավաքի մատյանները և օգտվողին կհուշի վրիպակ գրանցելու անհրաժեշտության մասին"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Մուտքագրում"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Պատկերում"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Սարքաշարի արագացված նյութավորում"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 5dcc045..c2db508 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Tidak ada"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Tunggu debugger"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Aplikasi yang di-debug menunggu debugger menempel sebelum berjalan"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Monitor Telefoni"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Monitor Telefoni akan mengumpulkan log jika mendeteksi masalah pada fungsi telefoni/modem dan mengirimkan notifikasi ke pengguna untuk melaporkan bug"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Masukan"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Gambar"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Render yang dipercepat hardware"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index c4d6777..883e909 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Ekkert"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Bíða eftir villuleit"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Villuleituð forrit bíða eftir að villuleit tengist fyrir keyrslu"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Fjarskiptaumsjón"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Fjarskiptaumsjón mun safna annálum þegar það skynjar vandamál með fjarskipti/virkni mótalds og veita notanda beiðni um að senda inn villutilkynningu"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Inntak"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Teikning"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Myndþýðing með vélbúnaðarhröðun"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 85993aa..e53e4da 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nessuna"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Attendi debugger"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"L\'app in debug attende il debugger prima dell\'esecuzione"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"TelephonyMonitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor raccoglierà log quando rileverà un problema con la funzionalità di telefonia/del modem e avviserà tempestivamente l\'utente per segnalare il bug"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Input"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Disegno"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Rendering con accelerazione hardware"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index df690ef..363b064 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"אף אחת"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"המתן למנקה באגים"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"אפליקציה שנוקו בה הבאגים ממתינה למנקה הבאגים לצירוף לפני ביצוע"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor יאסוף מידע ביומנים כשיזהה בעיה בפונקציונליות של טלפוניה/מודם. הוא ישלח הודעה למשתמש כדי שיוכל להגיש דוח על באג"</string>
<string name="debug_input_category" msgid="1811069939601180246">"קלט"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"שרטוט"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"עיבוד מואץ של חומרה"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 46b51d1..dfd199a 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"なし"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"デバッガを待機"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"アプリは実行前にデバッガのアタッチを待機します"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Telephony Monitor は、電話やモデムの機能で問題が検出されたときにログを収集し、バグを報告するようユーザーに通知を表示します"</string>
<string name="debug_input_category" msgid="1811069939601180246">"入力"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"描画"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"ハードウェアアクセラレーテッドレンダリング"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 49a6902..7fac639 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"არაფერი"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"დაილოდეთ, სანამ ჩაირთვება გამმართველი"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"გამართული აპლიკაცია ელოდება გამმართველის ჩართვას გაშვებამდე"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"ტელეფონიის კონტროლიორი"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"ტელეფონიის/მოდემის გამართულ მუშაობასთან დაკავშირებული პრობლემის გამოვლენისას, ტელეფონიის კონტროლიორი შეაგროვებს ჟურნალების ჩანაწერებს, ხოლო მომხმარებელს შეცდომის შესახებ მოხსენებას შეთავაზებს"</string>
<string name="debug_input_category" msgid="1811069939601180246">"ტექსტის შეყვანა"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"ნახაზი"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"აპარატურით დაჩქარებული გამოსახულება"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 7dea44a..3b806a5 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Ешнәрсе"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Жөндеушіні күту"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Орындау алдында бекіту үшін жөнделген қолданба жөндеушіні күтеді."</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Telephony Monitor функциясы телефон не модем жұмысында ақау байқаған жағдайда деректерді жинайды да, пайдаланушыға қате туралы ақпаратты жіберуді ұсынады"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Кіріс"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Сызу"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Бейнелеуді жабдықпен жылдамдату"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 4dd298d..2160bae 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"គ្មានអ្វីទេ"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"រង់ចាំកម្មវិធីកែកំហុស"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"កម្មវិធីបានកែកំហុសរង់ចាំឲ្យភ្ជាប់កម្មវិធីកែកំហុសមុនពេលអនុវត្ត"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor នឹងប្រមូលកំណត់ហេតុ នៅពេលវារកឃើញបញ្ហាទាក់ទងនឹងមុខងារទូរសព្ទ/ម៉ូដឹម និងបញ្ជូនការជូនដំណឹងទៅកាន់អ្នកប្រើប្រាស់ដើម្បីរាយការណ៍ពីបញ្ហា"</string>
<string name="debug_input_category" msgid="1811069939601180246">"បញ្ចូល"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"គំនូរ"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"បង្ហាញផ្នែករឹងបានបង្កើនល្បឿន"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index bbfef4b..a47a92c 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"ಏನೂ ಇಲ್ಲ"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"ಡೀಬಗರ್ಗಾಗಿ ನಿರೀಕ್ಷಿಸಿ"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"ಲಗತ್ತನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸುವ ಮೊದಲು ಡೀಬಗರ್ಗಾಗಿ ಡೀಬಗ್ ಮಾಡಿದ ಅಪ್ಲಿಕೇಶನ್ ಕಾಯುತ್ತದೆ"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"ದೂರವಾಣಿ ಮಾನಿಟರ್"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"ದೂರವಾಣಿ/ಮೊಡೆಮ್ ಕಾರ್ಯಾಚರಣೆಯಲ್ಲಿ ಸಮಸ್ಯೆಗಳು ಕಂಡುಬಂದಾಗ, ದೂರವಾಣಿ ಮಾನಿಟರ್ ಲಾಗ್ಗಳನ್ನು ಸಂಗ್ರಹಿಸುತ್ತದೆ ಮತ್ತು ದೋಷದ ಕುರಿತು ವರದಿ ಸಲ್ಲಿಸಲು ಬಳಕೆದಾರನಿಗೆ ಸೂಚನೆ ನೀಡುತ್ತದೆ"</string>
<string name="debug_input_category" msgid="1811069939601180246">"ಇನ್ಪುಟ್"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"ಚಿತ್ರಣ"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"ಹಾರ್ಡ್ವೇರ್ ವೇಗವರ್ಧಿತ ರೆಂಡರಿಂಗ್"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 6633558..852c78e 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"없음"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"디버거 연결을 위해 대기"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"디버깅된 애플리케이션이 실행되기 전에 디버거 연결을 위해 대기"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"통신 모니터"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"통신 모니터는 통신/모뎀 기능에서 문제가 감지될 경우 로그를 수집하며 사용자에게 버그를 신고하라는 알림을 표시합니다."</string>
<string name="debug_input_category" msgid="1811069939601180246">"입력"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"그림"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"하드웨어 가속 렌더링"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index e59d3f8..380d0ff 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Эч бирөө"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Жөндөөчү күтүлүүдө"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Жөндөлүүчү колдонмо аткаруудан мурун жөндөөчүнүнүн тиркелишин күтүп жатат"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor телефондун/модемдин функцияларында көйгөй тапса, анын таржымалын аныктайт жана мүчүлүштүк тууралуу кабарлоо үчүн колдонуучуга эскертме жөнөтөт"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Киргизүү"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Тартуу"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Визуалдаштырууну аппарат менен ылдамдатуу"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 20869a7..eb10dbb 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"ບໍ່ມີຫຍັງ"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"ລໍຖ້າໂຕດີບັ໊ກ"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"ແອັບພລິເຄຊັນທີ່ດີບັ໊ກແລ້ວ ຈະຖ້າໂຕດີບັ໊ກກ່ອນການເຮັດວຽກ"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor ຈະເກັບກຳບັນທຶກການເຮັດວຽກເມື່ອມັນກວດພົບບັນຫາກັບການເຮັດວຽກຂອງລະບົບໂທລະສັບ/ໂມເດັມ ແລະ ແຈ້ງເຕືອນໃຫ້ຜູ້ໃຊ້ໃຫ້ລາຍງານບັກ"</string>
<string name="debug_input_category" msgid="1811069939601180246">"ການປ້ອນຂໍ້ມູນ"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"ການແຕ້ມ"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"ການສະແດງຜົນໂດຍໃຊ້ຮາດແວຊ່ວຍ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 8d17a3a..adf6f8a 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nieko"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Laukti derintuvės"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Derinta progr. laukia derint., kad galėtų tęsti."</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telefonijos stebėjimo priemonė"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Telefonijos stebėjimo priemonė rinks žurnalus, kai aptiks problemą dėl telefonijos / modemo funkcijų, ir naudotojui pateiks raginimą pranešti apie riktą"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Įvestis"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Atvaizdavimas"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Aparatinės įrangos paspartintas pateikimas"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 101b30b..280900a 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nekas"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Gaidīt atkļūdotāju"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Gaida atkļūdotāju, ko pirms izp. piev. atkļ. liet."</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telefonijas pārraugs"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Ja tiks konstatēta problēma saistībā ar telefonijas/modema funkcionalitāti, telefonijas pārraugs apkopos žurnālus un paziņojumā aicinās lietotāju reģistrēt kļūdu."</string>
<string name="debug_input_category" msgid="1811069939601180246">"Ievade"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Zīmējums"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Aparatūras paātrinātā atveidošana"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 40feed0..51697bd 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Ништо"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Почекај ја програмата за отстранување грешки"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Пред да се изврши, апликација за отстранување грешки чека програмата за отстранување грешки да се закачи"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Монитор за телефонија"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Мониторот за телефонија ќе води евиденција кога ќе открие проблем со функционалноста на телефонијата/модемот и ќе го извести корисникот да пријави проблем"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Внес"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Цртање"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Прикажување забрзување на хардвер"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 40525ed..d96607e 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"ഒന്നുമില്ല"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"ഡീബഗ്ഗറിനായി കാത്തിരിക്കുക"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"ഡീബഗ്ഗുചെയ്ത അപ്ലിക്കേഷൻ നിർവ്വഹണത്തിനുമുമ്പായി അറ്റാച്ചുചെയ്യുന്നതിന് ഡീബഗ്ഗറിനായി കാത്തിരിക്കുന്നു."</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"ടെലിഫോണി മോണിറ്റർ"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"ടെലിഫോണി മോണിറ്റർ, ടെലിഫോണി/മോഡവുമായി ബന്ധപ്പെട്ട് എന്തെങ്കിലും പ്രശ്നം കണ്ടെത്തുമ്പോൾ, അതിന്റെ ലോഗുകൾ ശേഖരിക്കുകയും ഒരു ബഗ് ഫയൽ ചെയ്യാൻ ഉപയോക്താവിന് അറിയിപ്പ് നൽകുകയും ചെയ്യും"</string>
<string name="debug_input_category" msgid="1811069939601180246">"ഇൻപുട്ട്"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"ഡ്രോയിംഗ്"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"ഹാർഡ്വെയർ ത്വരിതപ്പെടുത്തിയ റെൻഡർ ചെയ്യൽ"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index f6abd54..e13886d 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Юуг ч биш"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Согог засагчийг хүлээх"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Согог засагдсан аппликейшн ажиллахын өмнө согог засагчийг хавсаргагдахыг хүлээнэ"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Утасны хяналт"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Утасны хяналт нь утас/модемын ажиллагаанд асуудал илрүүлсэн тохиолдолд лог цуглуулж, хэрэглэгчид алдааг засах мэдэгдэл илгээнэ"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Оруулах"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Зураг"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Техник хангамжийн хурдатгалтай үзүүлэлт"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 5e9ecdb..ae61916 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"काहीही नाही"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"डीबगरची प्रतीक्षा करा"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"डीबग केलेले अॅप्लिकेशन अंमलात आणण्यापूर्वी डीबगर संलग्न करण्याची प्रतीक्षा करतो"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"टेलिफोनी मॉनिटर"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"टेलिफोनी/मोडेमच्या कार्यक्षमतेत समस्या आढळल्यावर टेलिफोनी मॉनिटर लॉग्ज गोळा करेल आणि दोष फाइल करण्यासाठी वापरकर्त्याला सूचनेचे संकेत देईल"</string>
<string name="debug_input_category" msgid="1811069939601180246">"इनपुट"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"रेखांकन"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"हार्डवेअर प्रवेगक प्रस्तुती"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index f252c2a..a44e5b1 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Tiada apa-apa"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Nantikan penyahpepijat"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Menanti penyahpepijat sebelum aplikasi melaksana"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor akan mengumpulkan log apabila apl ini mengesan masalah berhubung kefungsian telefoni/modem dan memaparkan pemberitahuan kepada pengguna supaya memfailkan pepijat"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Input"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Lukisan"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Pemaparan dipercepat perkakasan"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 74302a0..af95eac 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"တခုမှမရှိ"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"အပြစ်ရှာဖွေ ဖယ်ရှားချက်ကိုစောင့်ရန်"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"အမှားပြင်ဆင်ရှာဖွေသည့် အပလီကေးရှင်းသည် လုပ်ငန်းမစမီ တွဲဖက်ရန် အမှားရှာဖွေမည့်သူကို စောင့်နေသည်။"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"တယ်လီဖုန်းဆက်သွယ်မှု မော်နီတာ"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"တယ်လီဖုန်းဆက်သွယ်မှု မော်နီတာသည် တယ်လီဖုန်းဆက်သွယ်မှု/မိုဒမ် လုပ်ဆောင်ချက်တို့တွင် ပြဿနာရှိနေလျှင် မှတ်တမ်းပြုစုပြီး ချွတ်ယွင်းချက် အစီရင်ခံရန် အသုံးပြုသူကို အကြောင်းကြားပေးမည် ဖြစ်သည်။"</string>
<string name="debug_input_category" msgid="1811069939601180246">"ထည့်သွင်းရန်"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"ရေးဆွဲခြင်း"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"ဟာ့ဒ်ဝဲ အရှိန်မြှင့် ပုံဖော်ခြင်း"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index cc73ef9..13d27ac 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Ingen"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Vent på feilsøkingsverktøyet"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Feilsøkt app venter til feilsøkingsverktøyet er lagt til før den kjører"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telefonimonitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor logger problemer som oppdages med funksjonaliteten til telefoni/modem, og varsler brukeren om å sende inn en feilrapport."</string>
<string name="debug_input_category" msgid="1811069939601180246">"Inndata"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Tegning"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Maskinvareakselerert gjengivelse"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index fef1ebe..58c5ffc 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"केही पनि होइन"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"डिबग गर्नेलाई पर्खनुहोस्"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"डिबग भएको अनुप्रयोग कार्यन्वयन हुनु अघि संलग्न हुन डिबग गर्नेलाई पर्खन्छ"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"टेलिफोनी मनिटर"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor ले टेलिफोन/मोडेमको सञ्चालनमा कुनै समस्या भेट्टायो भने लगहरू सङ्कलन गर्नेछ र प्रयोगकर्तालाई बग बारे रिपोर्ट गर्न सूचना पठाउनेछ"</string>
<string name="debug_input_category" msgid="1811069939601180246">"इनपुट"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"रेखाचित्र"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"हार्डवेयर प्रतिपादन फुर्तिलो बनाइयो"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 25d7cc5..5cce79e 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Niets"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Wachten op debugger"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Gedebugde app wacht op koppelen van debugger vóór uitvoering"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor verzamelt logbestanden wanneer een probleem wordt gedetecteerd met de functionaliteit van telefonie/modem. De gebruiker krijgt een melding te zien waarin wordt gevraagd of hij een bug wil indienen."</string>
<string name="debug_input_category" msgid="1811069939601180246">"Invoer"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Tekening"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Rendering met hardwareversnelling"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 6e93e869..becb559 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"ਕੁਝ ਨਹੀਂ"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"ਡੀਬੱਗਰ ਦੀ ਉਡੀਕ ਕਰੋ"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"ਡੀਬੱਗ ਕੀਤੇ ਐਪਲੀਕੇਸ਼ਨ ਐਗਜੀਕਿਊਟ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਅਟੈਚ ਕਰਨ ਲਈ ਡੀਬੱਗਰ ਦੀ ਉਡੀਕ ਕਰਦੇ ਹਨ"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"ਟੈਲੀਫ਼ੋਨੀ ਮੋਨੀਟਰ"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"ਟੈਲੀਫ਼ੋਨੀ ਮੋਨੀਟਰ ਟੈਲੀਫ਼ੋਨੀ/ਮੌਡਮ ਪ੍ਰਕਾਰਜਾਤਮਕਤਾ ਵਿੱਚ ਕਿਸੇ ਸਮੱਸਿਆ ਦਾ ਪਤਾ ਲੱਗਣ \'ਤੇ ਲੌਗਾਂ ਨੂੰ ਇਕੱਤਰ ਕਰੇਗਾ ਅਤੇ ਵਰਤੋਂਕਾਰ ਨੂੰ ਇੱਕ ਬੱਗ ਦਾਇਰ ਕਰਨ ਲਈ ਸੂਚਨਾ ਦੇਵੇਗਾ"</string>
<string name="debug_input_category" msgid="1811069939601180246">"ਇਨਪੁਟ"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"ਡਰਾਇੰਗ"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"ਹਾਰਡਵੇਅਰ ਐਕਸੇਲਰੇਟਿਡ ਰੈਂਡਰਿੰਗ"</string>
@@ -331,7 +329,7 @@
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ਤਬਦੀਲ ਕਰੋ ..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ਫ਼ਾਈਲ ਪਹਿਲਾਂ ਤੋਂ ਇਨਕ੍ਰਿਪਟਡ ਹੈ"</string>
<string name="title_convert_fbe" msgid="1263622876196444453">"ਫ਼ਾਈਲ ਆਧਾਰਿਤ ਇਨਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਤਬਦੀਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
- <string name="convert_to_fbe_warning" msgid="6139067817148865527">" ਡਾਟਾ ਪਾਰਟੀਸ਼ਨ ਦਾ ਫ਼ਾਈਲ ਆਧਾਰਿਤ ਇਨਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਰੁਪਾਂਤਰਣ ਕਰੋ\n !! ਚਿਤਾਵਨੀ !! ਇਹ ਤੁਹਾਡੇ ਸਾਰੇ ਡੈਟੇ ਨੂੰ ਮਿਟਾ ਦੇਵੇਗਾ\n ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਪ੍ਰਯੋਗਿਕ ਹੈ, ਅਤੇ ਸ਼ਾਇਦ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ।\n ਜਾਰੀ ਰੱਖਣ ਲਈ \'ਮਿਟਾਓ ਅਤੇ ਰੁਪਾਂਤਰਣ ਕਰੋ...\' ਨੂੰ ਦਬਾਓ।"</string>
+ <string name="convert_to_fbe_warning" msgid="6139067817148865527">" ਡਾਟਾ ਪਾਰਟੀਸ਼ਨ ਦਾ ਫ਼ਾਈਲ ਆਧਾਰਿਤ ਇਨਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਰੁਪਾਂਤਰਣ ਕਰੋ\n !! ਚਿਤਾਵਨੀ !! ਇਹ ਤੁਹਾਡੇ ਸਾਰੇ ਡਾਟੇ ਨੂੰ ਮਿਟਾ ਦੇਵੇਗਾ\n ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਪ੍ਰਯੋਗਿਕ ਹੈ, ਅਤੇ ਸ਼ਾਇਦ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ।\n ਜਾਰੀ ਰੱਖਣ ਲਈ \'ਮਿਟਾਓ ਅਤੇ ਰੁਪਾਂਤਰਣ ਕਰੋ...\' ਨੂੰ ਦਬਾਓ।"</string>
<string name="button_convert_fbe" msgid="5152671181309826405">"ਮਿਟਾਓ ਅਤੇ ਰੁਪਾਂਤਰਣ ਕਰੋ..."</string>
<string name="picture_color_mode" msgid="4560755008730283695">"ਤਸਵੀਰ ਰੰਗ ਮੋਡ"</string>
<string name="picture_color_mode_desc" msgid="1141891467675548590">"sRGB ਵਰਤੋਂ ਕਰੋ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 59d4724..e3c69fb 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Brak"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Poczekaj na debugera"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Aplikacja do debugowania czeka na przyłączenie debugera"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Monitorowanie telefonii"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor zbiera dzienniki po wykryciu problemu z funkcją telefonu/modemu i powiadamia użytkownika o możliwości zgłoszenia błędu"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Ekran dotykowy"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Rysowanie"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Sprzętowa akceleracja renderowania"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 4e85684..88c69eb 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nada"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Aguardar depurador"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"App depurado espera conexão com debugger antes de ser executado."</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Monitor de telefonia"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Ao detectar um problema com a funcionalidade de modem/telefonia, o Monitor de telefonia coletará registros e enviará uma notificação ao usuário para informar um bug"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Entrada"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Desenho"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Renderização acelerada por hardware"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 44035d4..dd73f3c 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nenhuma"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Aguarde pelo depurador"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"A aplicação depurada aguarda a anexação do depurador antes da execução"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Monitor de telefonia"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"O Monitor de telefonia recolhe registos quando deteta um problema de funcionalidade de telefonia/modem e apresenta uma notificação ao utilizador para reportar um erro"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Introdução"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Desenho"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Conversão acelerada de hardware"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 4e85684..88c69eb 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nada"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Aguardar depurador"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"App depurado espera conexão com debugger antes de ser executado."</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Monitor de telefonia"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Ao detectar um problema com a funcionalidade de modem/telefonia, o Monitor de telefonia coletará registros e enviará uma notificação ao usuário para informar um bug"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Entrada"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Desenho"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Renderização acelerada por hardware"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 09bcc8f..93ad315 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Niciuna"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Așteptați depanatorul"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Înaintea executării, aplicația așteaptă atașarea depanatorului"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor colectează jurnale când detectează o problemă privind performanțele serviciilor de telefonie/modemului și trimite notificări utilizatorului să semnaleze o eroare"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Intrare"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Desen"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Redare accelerată hardware"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 6563ac1..ee5169b 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Нет"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Подождите, пока подключится отладчик"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Приложение ожидает подключения отладчика"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Обнаружив проблему в работе модема или телефонной связи, Telephony Monitor будет собирать журналы и предлагать пользователю отправить информацию об ошибке"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Ввод"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Отрисовка"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Аппаратное ускорение визуализации"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 3fb05b0..1ce6786 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"කිසිවක් නැත"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"නිදොස්කරණය සඳහා රැඳෙන්න"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"නිදොස් කළ යෙදුම් ක්රියාකිරීමට පෙර ඇමිණීම සඳහා නිදොස්කරණය වෙත බලා සිටියි"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"දුරකථන/මොඩම් ක්රියාකාරිත්වයේ ගැටළුවක් හඳුනාගත් විට TelephonyMonitor ලොග එකතු කර දෝෂයක්ගොනු කිරීමට පරිශීලකයා වෙත දැනුම් දෙයි"</string>
<string name="debug_input_category" msgid="1811069939601180246">"ආදානය"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"ඇඳීම්"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"දෘඩාංග වේගය වැඩි කළ පිරිනැමීම"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 1ccfda6..b1d2fa3 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nič"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Čakať na ladiaci nástroj"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Aplikácia čaká na pripojenie ladiaceho nástroja"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Monitorovanie telefonických služieb"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Keď monitorovanie telefonických služieb zistí problém s fungovaním telefonických služieb alebo modemu, bude zhromažďovať denníky a zobrazí používateľovi upozornenie s výzvou na nahlásenie chyby"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Vstup"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Vykreslovanie"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardvérom zrýchlené vykresľovanie"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index de13147..21badd6 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Nič"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Počakajte na iskalnik napak"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Aplikacija, v kateri iščete napako, pred izvajanjem čaka na povezavo z iskalnikom napak"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Nadziranje telefonije"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Funkcija Nadziranje telefonije pridobi dnevnike, ko zazna težavo s funkcijami telefonije/modema, in uporabnika z obvestilom pozove, naj prijavi napako."</string>
<string name="debug_input_category" msgid="1811069939601180246">"Vnos"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Risba"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Upodabljanje s strojnim pospeševanjem"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index f77818d..54cde3c 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Asnjë"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Prit për korrigjuesin"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Aplikacioni i korrigjimit të gabimeve pret që korrigjuesi të bashkëngjitet para ekzekutimit"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Monitori i telefonisë"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Monitori i telefonisë do të mbledhë regjistrat kur të zbulojë një problem me funksionalitetin e telefonisë/modemit dhe do t\'i paraqesë një njoftim përdoruesit që të regjistrojë një defekt në kod"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Hyrja"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Vizatime"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Interpretimi i përshpejtuar i harduerit"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 2b59fee..4e0bee0 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Ниједна"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Сачекај програм за отклањање грешака"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Апликација чека програм за отклањање грешака да приложи пре извршавања"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"TelephonyMonitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor ће прикупљати евиденцију када открије проблем са функционисањем телефоније/модема и затражиће од корисника да пријави грешку"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Унос"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Цртање"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Хардверски убрзано приказивање"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 6ab3799..dea265d 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Ingen"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Vänta på felsökningen"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Felsökaren måste ansluta till appen först"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor samlar in loggar när ett problem upptäcks i telefoni-/modemfunktionen och uppmanar användaren att skicka in en felrapport"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Indata"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Ritning"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Maskinvaruaccelererad rendering"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index e5aa83f..7126594 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Hakuna chochote"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Subiri kitatuaji"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Programu ya utatuaji husubiri kitatuaji ili kuambatisha kabla ya kutekeleza"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Kichunguzi cha Shughuli za Simu"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Kichunguzi cha Shughuli za Simu kitakusanya kumbukumbu wakati kinatambua tatizo katika utendaji wa simu au modemu, na kumwarifu mtumiaji kuwasilisha ripoti ya hitilafu"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Ingizo"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Uchoraji"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Kutunguliza kwa maunzi kulikoharakishwa"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 75420bd..4f563fa 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"ஒன்றுமில்லை"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"பிழைதிருத்திக்குக் காத்திருக்கவும்"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"பிழைதிருத்தப்பட்ட பயன்பாடு செயல்படுவதற்கு முன்பு பிழைதிருத்தியை இணைப்பதற்குக் காத்திருக்கிறது"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"டெலிஃபோனி மானிட்டர்"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor ஆனது டெலிஃபோனி/மோடம் செயல்பாட்டில் சிக்கல் இருப்பதைக் கண்டறியும் போது, பதிவுகளைச் சேகரிக்கும். அத்துடன், பிழையைப் புகாரளிக்கும்படி பயனருக்கு அறிவிப்பைக் காட்டும்"</string>
<string name="debug_input_category" msgid="1811069939601180246">"உள்ளீடு"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"வரைபொருள்"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"வன்பொருள் முடுக்கத்துடன் கூடிய காட்சியாக்கம்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 23a98c2..0d1ca375 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"ఏదీ వద్దు"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"డీబగ్గర్ కోసం వేచి ఉండండి"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"డీబగ్ చేయబడిన యాప్ అమలు కావడానికి ముందు జోడించాల్సిన డీబగ్గర్ కోసం వేచి ఉంటుంది"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"టెలిఫోనీ మానిటర్"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"టెలిఫోనీ/మోడెమ్ కార్యాచరణలో సమస్యను గుర్తించినప్పుడు TelephonyMonitor లాగ్లను సేకరిస్తుంది మరియు బగ్ని ఫైల్ చేయమని వినియోగదారును ప్రోత్సహిస్తుంది"</string>
<string name="debug_input_category" msgid="1811069939601180246">"ఇన్పుట్"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"డ్రాయింగ్"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"హార్డ్వేర్ వేగవంతమైన భాషాంతరీకరణ"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 8608ac4..f7ba20a 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"ไม่มี"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"รอโปรแกรมแก้ไขข้อบกพร่อง"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"แอปพลิเคชันที่มีการแก้ปัญหาจะรอให้โปรแกรมแก้ไขข้อบกพร่องแนบข้อมูลก่อนปฏิบัติการ"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"การตรวจสอบโทรศัพท์"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"การตรวจสอบโทรศัพท์จะรวบรวมบันทึกเมื่อตรวจพบปัญหาด้านฟังก์ชันการทำงานของโทรศัพท์/โมเด็มและแจ้งเตือนให้ผู้ใช้ส่งข้อบกพร่อง"</string>
<string name="debug_input_category" msgid="1811069939601180246">"อินพุต"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"การวาดภาพ"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"การแสดงผลที่มีการเร่งด้วยฮาร์ดแวร์"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index cc6121e..10d8102 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Wala"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Maghintay ng debugger"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Hinihintay ng na-debug na application na ma-attach ang debugger bago magsagawa"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Mangongolekta ang TelephonyMonitor ng mga log kapag nakatukoy ito ng problema sa functionality ng telephony/modem at magpo-prompt ito ng notification sa user na mag-file ng bug"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Input"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Pagguhit"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Pag-render na pinapabilis ng hardware"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index fda52ef..3d8d0d3 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Hiçbiri"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Hata ayıklayıcıyı bekle"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Hata ayıklanan uygulama, çalıştırılmadan önce hata ayıklayıcının eklenmesini bekler"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor, telefon/modem işleviyle ilgili bir sorun algıladığında günlükleri toplar ve hatayı bildirmesi için kullanıcıya bir bildirim gösterir."</string>
<string name="debug_input_category" msgid="1811069939601180246">"Girdi"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Çizim"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Donanım hızlandırmalı oluşturma"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 6d86a8d..7d2c6fd 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Нічого"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Зачекайте на налагоджувач"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Додаток очікує підключення налагоджувача"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Коли функція Telephony Monitor виявить проблеми в роботі телефона чи модема, вона збере дані та запропонує користувачеві повідомити про помилку"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Ввід"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Рисування"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Апаратне прискорення"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 3dd1b3e..63c040e 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -110,11 +110,11 @@
<string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
<string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ہٹائی گئی ایپس"</string>
<string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ہٹائی گئی ایپس اور صارفین"</string>
- <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ٹیتھرنگ"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ٹیدرنگ"</string>
<string name="tether_settings_title_wifi" msgid="3277144155960302049">"پورٹیبل ہاٹ اسپاٹ"</string>
- <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"بلوٹوتھ ٹیتھرنگ"</string>
- <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"ٹیتھرنگ"</string>
- <string name="tether_settings_title_all" msgid="8356136101061143841">"ٹیتھرنگ و پورٹیبل ہاٹ اسپاٹ"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"بلوٹوتھ ٹیدرنگ"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"ٹیدرنگ"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"ٹیدرنگ و پورٹیبل ہاٹ اسپاٹ"</string>
<string name="managed_user_title" msgid="8109605045406748842">"تمام کام کی ایپس"</string>
<string name="user_guest" msgid="8475274842845401871">"مہمان"</string>
<string name="unknown" msgid="1592123443519355854">"نامعلوم"</string>
@@ -168,7 +168,7 @@
<string name="development_settings_summary" msgid="1815795401632854041">"ایپ ڈویلپمنٹ کیلئے اختیارات سیٹ کریں"</string>
<string name="development_settings_not_available" msgid="4308569041701535607">"اس صارف کیلئے ڈیولپر کے اختیارات دستیاب نہیں ہیں"</string>
<string name="vpn_settings_not_available" msgid="956841430176985598">"VPN ترتیبات اس صارف کیلئے دستیاب نہیں ہیں"</string>
- <string name="tethering_settings_not_available" msgid="6765770438438291012">"ٹیتھرنگ ترتیبات اس صارف کیلئے دستیاب نہیں ہیں"</string>
+ <string name="tethering_settings_not_available" msgid="6765770438438291012">"ٹیدرنگ ترتیبات اس صارف کیلئے دستیاب نہیں ہیں"</string>
<string name="apn_settings_not_available" msgid="7873729032165324000">"رسائی کی جگہ کے نام کی ترتیبات اس صارف کیلئے دستیاب نہیں ہیں"</string>
<string name="enable_adb" msgid="7982306934419797485">"USB ڈیبگ کرنا"</string>
<string name="enable_adb_summary" msgid="4881186971746056635">"USB مربوط ہونے پر ڈيبگ کرنے کی وضع"</string>
@@ -192,7 +192,7 @@
<string name="wifi_aggressive_handover" msgid="5309131983693661320">"Wi‑Fi سے موبائل کو جارحانہ ہینڈ اوور"</string>
<string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"ہمیشہ Wi‑Fi روم اسکینز کی اجازت دیں"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"موبائل ڈیٹا ہمیشہ فعال رکھیں"</string>
- <string name="tethering_hardware_offload" msgid="7470077827090325814">"ہارڈویئر کی سرعت کاری میں ربط بنایا جا رہا ہے"</string>
+ <string name="tethering_hardware_offload" msgid="7470077827090325814">"ٹیدرنگ ہارڈویئر سرعت کاری"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"بغیر نام والے بلوٹوتھ آلات دکھائیں"</string>
<string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"مطلق والیوم کو غیر فعال کریں"</string>
<string name="bluetooth_enable_inband_ringing" msgid="3291686366721786740">"ان بینڈ رنگنگ فعال کریں"</string>
@@ -225,7 +225,7 @@
<string name="allow_mock_location_summary" msgid="317615105156345626">"فرضی مقامات کی اجازت دیں"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"منظر انتساب کے معائنہ کو فعال کریں"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Wi‑Fi فعال ہونے پر بھی موبائل ڈیٹا کو ہمیشہ فعال رکھیں (تیزی سے نیٹ ورک سوئچ کرنے کیلئے)۔"</string>
- <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"اگر دستیاب ہو، تو ہارڈویئر کی سرعت کاری میں ربط کاری کا استعمال کریں"</string>
+ <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"اگر دستیاب ہو تو ٹیدرنگ ہارڈویئر سرعت کاری کا استعمال کریں"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB ڈیبگ کرنے کی اجازت دیں؟"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB ڈیبگ کرنا صرف ڈیولپمنٹ کے مقاصد کیلئے ہے۔ اپنے کمپیوٹر اور اپنے آلہ کے درمیان ڈیٹا کاپی کرنے کیلئے اسے استعمال کریں، بغیر اطلاع کے اپنے آلہ پر ایپس انسٹال کریں اور لاگ ڈیٹا پڑھیں۔"</string>
<string name="adb_keys_warning_message" msgid="5659849457135841625">"اپنے ذریعہ پہلے سے اجازت یافتہ سبھی کمپیوٹرز سے USB ڈیبگ کرنے کی رسائی کو کالعدم کریں؟"</string>
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"کچھ نہیں"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"ڈیبگر کا انتظار کریں"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"ڈیبگ کردہ ایپلیکیشن کاروائی سے پہلے ڈیبگر کے منسلک ہونے کا انتظار کرتی ہے"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"ٹیلیفونی/موڈم کی فعالیت کے ساتھ در پیش مسئلہ کا پتہ چلنے پر TelephonyMonitor لاگز اکٹھا کر کے بگ دائر کرنے کیلئے صارف کو اطلاع بھیجے گا"</string>
<string name="debug_input_category" msgid="1811069939601180246">"ان پٹ"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"ڈرائنگ"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"ہارڈ ویئر کے ذریعے تیز کردہ رینڈرنگ"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index dbdcb1c..fbe9026 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Yo‘q"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Tuzatuvchi dasturni kuting"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Ilova nosozliklarni tuzatuvchi dasturga ulanishni kutmoqda"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telefoniya nazorati"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Telefoniya nazorati telefoniya/model funksiyalari bilan bog‘liq muammolar aniqlansa va foydalanuvchiga xatolikni yuborishi uchun bildirishnoma yuborib, jurnallarni to‘playdi"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Matn kiritish"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Chizma"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Vizualizatsiyani apparatli tezlatish"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index e6d4eb7..07d0999 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Không có ứng dụng"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Đợi trình gỡ lỗi"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Ứng dụng được gỡ lỗi chờ trình gỡ lỗi đính kèm trước khi thực hiện"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Giám sát điện thoại"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"TelephonyMonitor sẽ thu thập nhật ký khi phát hiện thấy sự cố với chức năng điện thoại/modem và gửi thông báo khẩn cấp tới người dùng để gửi lỗi"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Nhập"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Vẽ"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Kết xuất có tăng tốc phần cứng"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 45d9835..1850f61 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"无"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"等待调试程序"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"调试应用会在执行前等待附加调试器"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"电话监控器"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"电话监控器会在检测到电话/调制解调器功能存在问题时收集相关日志,并向用户发出通知,提醒用户提交错误报告"</string>
<string name="debug_input_category" msgid="1811069939601180246">"输入"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"绘图"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"硬件加速渲染"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index a47e1e8..379a1f1 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"無"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"等待除錯程式"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"執行已除錯的應用程式前等待附加除錯程式"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"電話監控工具"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Telephony Monitor 會在偵測到電話/數據機功能發生問題時收集記錄,並通知使用者報告錯誤"</string>
<string name="debug_input_category" msgid="1811069939601180246">"輸入"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"繪圖"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"硬件加速轉譯"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index d13a4f6..2abca68 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"無"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"等待偵錯程式"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"執行受偵錯的應用程式之前,先等待偵錯程序附加完成"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Telephony Monitor"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"Telephony Monitor 會在偵測到電話/數據機功能發生問題時收集相關紀錄,並顯示通知方便使用者回報錯誤"</string>
<string name="debug_input_category" msgid="1811069939601180246">"輸入"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"繪圖"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"硬體加速轉譯"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 35d84a2..1de1312 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -248,8 +248,6 @@
<string name="no_application" msgid="2813387563129153880">"Lutho"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"Linda isilungisi senkinga"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Uhlelo lokusebenza olulungiswe inkinga lulinda isilungisi senkinga ukuba sinamathisele ngaphambi kokuphuma"</string>
- <string name="telephony_monitor_switch" msgid="1764958220062121194">"Ukuqashwa kwefoni"</string>
- <string name="telephony_monitor_switch_summary" msgid="7695552966547975635">"I-TelephonyMonitor izoqoqa amalogi uma ithola inkinga ngokusebenza kwefoni/imodemu iphinde ikhiphe isaziso esiya kumsebenzisi ukuthi athumele isiphazamisi"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Okufakwayo"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Umdwebo"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Ukunikezelwa okusheshisiwe kwezingxenyekazi zekhompyutha"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index bd884a3..fbfa725 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -608,11 +608,6 @@
<string name="wait_for_debugger_summary">Debugged application waits for debugger to
attach before executing</string>
- <!-- UI debug setting: title for Telephonymonitor switch [CHAR LIMIT=50] -->
- <string name="telephony_monitor_switch">Telephony Monitor</string>
- <!-- UI debug setting: summary for switch of Telephonymonitor [CHAR LIMIT=500] -->
- <string name="telephony_monitor_switch_summary">TelephonyMonitor will collect logs when it detects a problem with telephony/modem functionality and prompt notification to user to file a bug</string>
-
<!-- Preference category for input debugging development settings. [CHAR LIMIT=25] -->
<string name="debug_input_category">Input</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 40c2b1f..fa2499f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -52,6 +52,11 @@
import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
import java.io.File;
import java.io.IOException;
@@ -180,7 +185,11 @@
}
public Session newSession(Callbacks callbacks) {
- Session s = new Session(callbacks);
+ return newSession(callbacks, null);
+ }
+
+ public Session newSession(Callbacks callbacks, Lifecycle lifecycle) {
+ Session s = new Session(callbacks, lifecycle);
synchronized (mEntriesMap) {
mSessions.add(s);
}
@@ -586,7 +595,7 @@
.replaceAll("").toLowerCase();
}
- public class Session {
+ public class Session implements LifecycleObserver, OnPause, OnResume, OnDestroy {
final Callbacks mCallbacks;
boolean mResumed;
@@ -600,11 +609,19 @@
ArrayList<AppEntry> mLastAppList;
boolean mRebuildForeground;
- Session(Callbacks callbacks) {
+ private final boolean mHasLifecycle;
+
+ Session(Callbacks callbacks, Lifecycle lifecycle) {
mCallbacks = callbacks;
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ mHasLifecycle = true;
+ } else {
+ mHasLifecycle = false;
+ }
}
- public void resume() {
+ public void onResume() {
if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
synchronized (mEntriesMap) {
if (!mResumed) {
@@ -616,7 +633,7 @@
if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
}
- public void pause() {
+ public void onPause() {
if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
synchronized (mEntriesMap) {
if (mResumed) {
@@ -735,8 +752,11 @@
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
- public void release() {
- pause();
+ public void onDestroy() {
+ if (!mHasLifecycle) {
+ // TODO: Legacy, remove this later once all usages are switched to Lifecycle
+ onPause();
+ }
synchronized (mEntriesMap) {
mSessions.remove(this);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java b/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
index 8fc9fa6..9fbadee 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
@@ -131,7 +131,7 @@
}
public long getTotalBytes() {
- return mStats.getCacheBytes() + mStats.getCodeBytes() + mStats.getDataBytes();
+ return mStats.getAppBytes() + mStats.getDataBytes();
}
}
}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java
new file mode 100644
index 0000000..ff7536a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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.settingslib.deviceinfo;
+
+import android.content.Context;
+import android.os.Build;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * Preference controller for displaying device serial number. Wraps {@link Build#getSerial()}.
+ */
+public class AbstractSerialNumberPreferenceController extends AbstractPreferenceController {
+ private static final String KEY_SERIAL_NUMBER = "serial_number";
+
+ private final String mSerialNumber;
+
+ public AbstractSerialNumberPreferenceController(Context context) {
+ this(context, Build.getSerial());
+ }
+
+ @VisibleForTesting
+ AbstractSerialNumberPreferenceController(Context context, String serialNumber) {
+ super(context);
+ mSerialNumber = serialNumber;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return !TextUtils.isEmpty(mSerialNumber);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ final Preference pref = screen.findPreference(KEY_SERIAL_NUMBER);
+ if (pref != null) {
+ pref.setSummary(mSerialNumber);
+ }
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_SERIAL_NUMBER;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java b/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
index b7fd404..3c5ac8d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
@@ -73,7 +73,7 @@
final Drawable deviceDrawable = context.getDrawable(resId);
final BatteryMeterDrawable batteryDrawable = new BatteryMeterDrawable(context,
- R.color.meter_background_color, batteryLevel);
+ context.getColor(R.color.meter_background_color), batteryLevel);
final int pad = context.getResources().getDimensionPixelSize(R.dimen.bt_battery_padding);
batteryDrawable.setPadding(pad, pad, pad, pad);
@@ -107,6 +107,8 @@
@VisibleForTesting
static class BatteryMeterDrawable extends BatteryMeterDrawableBase {
private final float mAspectRatio;
+ @VisibleForTesting
+ int mFrameColor;
public BatteryMeterDrawable(Context context, int frameColor, int batteryLevel) {
super(context, frameColor);
@@ -118,6 +120,7 @@
final int tintColor = Utils.getColorAttr(context, android.R.attr.colorControlNormal);
setColorFilter(new PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_IN));
setBatteryLevel(batteryLevel);
+ mFrameColor = frameColor;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 664dcfc..12455d8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -24,7 +24,6 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
import android.net.NetworkKey;
import android.net.NetworkRequest;
import android.net.NetworkScoreManager;
@@ -36,10 +35,14 @@
import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiNetworkScoreCache.CacheListener;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.provider.Settings;
import android.support.annotation.GuardedBy;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -47,8 +50,12 @@
import android.util.SparseIntArray;
import android.widget.Toast;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -64,7 +71,7 @@
/**
* Tracks saved or available wifi networks and their state.
*/
-public class WifiTracker {
+public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestroy {
/**
* Default maximum age in millis of cached scored networks in
* {@link AccessPoint#mScoredNetworkCache} to be used for speed label generation.
@@ -80,7 +87,7 @@
* and used so as to assist with in-the-field WiFi connectivity debugging */
public static boolean sVerboseLogging;
- // TODO(b/36733768): Remove flag includeSaved and includePasspoints.
+ // TODO(b/36733768): Remove flag includeSaved
// TODO: Allow control of this?
// Combo scans can take 5-6s to complete - set to 10s.
@@ -96,9 +103,9 @@
private final WifiListener mListener;
private final boolean mIncludeSaved;
private final boolean mIncludeScans;
- private final boolean mIncludePasspoints;
- @VisibleForTesting final MainHandler mMainHandler;
- @VisibleForTesting final WorkHandler mWorkHandler;
+ @VisibleForTesting MainHandler mMainHandler;
+ @VisibleForTesting WorkHandler mWorkHandler;
+ private HandlerThread mWorkThread;
private WifiTrackerNetworkCallback mNetworkCallback;
@@ -142,7 +149,7 @@
private WifiInfo mLastInfo;
private final NetworkScoreManager mNetworkScoreManager;
- private final WifiNetworkScoreCache mScoreCache;
+ private WifiNetworkScoreCache mScoreCache;
private boolean mNetworkScoringUiEnabled;
private long mMaxSpeedLabelScoreCacheAge;
@@ -169,51 +176,43 @@
return filter;
}
+ /**
+ * Use the lifecycle constructor below whenever possible
+ */
+ @Deprecated
public WifiTracker(Context context, WifiListener wifiListener,
boolean includeSaved, boolean includeScans) {
- this(context, wifiListener, null, includeSaved, includeScans);
- }
-
- public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans) {
- this(context, wifiListener, workerLooper, includeSaved, includeScans, false);
- }
-
- public WifiTracker(Context context, WifiListener wifiListener,
- boolean includeSaved, boolean includeScans, boolean includePasspoints) {
- this(context, wifiListener, null, includeSaved, includeScans, includePasspoints);
- }
-
- public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans, boolean includePasspoints) {
- this(context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints,
+ this(context, wifiListener, includeSaved, includeScans,
context.getSystemService(WifiManager.class),
context.getSystemService(ConnectivityManager.class),
context.getSystemService(NetworkScoreManager.class),
- Looper.myLooper(), newIntentFilter());
+ newIntentFilter());
+ }
+
+ public WifiTracker(Context context, WifiListener wifiListener,
+ @NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) {
+ this(context, wifiListener, includeSaved, includeScans,
+ context.getSystemService(WifiManager.class),
+ context.getSystemService(ConnectivityManager.class),
+ context.getSystemService(NetworkScoreManager.class),
+ newIntentFilter());
+ lifecycle.addObserver(this);
}
@VisibleForTesting
- WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans, boolean includePasspoints,
- WifiManager wifiManager, ConnectivityManager connectivityManager,
- NetworkScoreManager networkScoreManager, Looper currentLooper,
- IntentFilter filter) {
+ WifiTracker(Context context, WifiListener wifiListener,
+ boolean includeSaved, boolean includeScans,
+ WifiManager wifiManager, ConnectivityManager connectivityManager,
+ NetworkScoreManager networkScoreManager,
+ IntentFilter filter) {
if (!includeSaved && !includeScans) {
throw new IllegalArgumentException("Must include either saved or scans");
}
mContext = context;
- if (currentLooper == null) {
- // When we aren't on a looper thread, default to the main.
- currentLooper = Looper.getMainLooper();
- }
- mMainHandler = new MainHandler(currentLooper);
- mWorkHandler = new WorkHandler(
- workerLooper != null ? workerLooper : currentLooper);
+ mMainHandler = new MainHandler(Looper.getMainLooper());
mWifiManager = wifiManager;
mIncludeSaved = includeSaved;
mIncludeScans = includeScans;
- mIncludePasspoints = includePasspoints;
mListener = wifiListener;
mConnectivityManager = connectivityManager;
@@ -229,7 +228,22 @@
mNetworkScoreManager = networkScoreManager;
- mScoreCache = new WifiNetworkScoreCache(context, new CacheListener(mWorkHandler) {
+ final HandlerThread workThread = new HandlerThread(TAG
+ + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ workThread.start();
+ setWorkThread(workThread);
+ }
+
+ /**
+ * Sanity warning: this wipes out mScoreCache, so use with extreme caution
+ * @param workThread substitute Handler thread, for testing purposes only
+ */
+ @VisibleForTesting
+ void setWorkThread(HandlerThread workThread) {
+ mWorkThread = workThread;
+ mWorkHandler = new WorkHandler(workThread.getLooper());
+ mScoreCache = new WifiNetworkScoreCache(mContext, new CacheListener(mWorkHandler) {
@Override
public void networkCacheUpdated(List<ScoredNetwork> networks) {
synchronized (mLock) {
@@ -244,6 +258,11 @@
});
}
+ @Override
+ public void onDestroy() {
+ mWorkThread.quit();
+ }
+
/** Synchronously update the list of access points with the latest information. */
@MainThread
public void forceUpdate() {
@@ -312,8 +331,9 @@
* <p>Registers listeners and starts scanning for wifi networks. If this is not called
* then forceUpdate() must be called to populate getAccessPoints().
*/
+ @Override
@MainThread
- public void startTracking() {
+ public void onStart() {
synchronized (mLock) {
registerScoreCache();
@@ -361,15 +381,16 @@
/**
* Stop tracking wifi networks and scores.
*
- * <p>This should always be called when done with a WifiTracker (if startTracking was called) to
+ * <p>This should always be called when done with a WifiTracker (if onStart was called) to
* ensure proper cleanup and prevent any further callbacks from occurring.
*
* <p>Calling this method will set the {@link #mStaleScanResults} bit, which prevents
* {@link WifiListener#onAccessPointsChanged()} callbacks from being invoked (until the bit
* is unset on the next SCAN_RESULTS_AVAILABLE_ACTION).
*/
+ @Override
@MainThread
- public void stopTracking() {
+ public void onStop() {
synchronized (mLock) {
if (mRegistered) {
mContext.unregisterReceiver(mReceiver);
@@ -769,9 +790,8 @@
}
public static List<AccessPoint> getCurrentAccessPoints(Context context, boolean includeSaved,
- boolean includeScans, boolean includePasspoints) {
- WifiTracker tracker = new WifiTracker(context,
- null, null, includeSaved, includeScans, includePasspoints);
+ boolean includeScans) {
+ WifiTracker tracker = new WifiTracker(context, null, includeSaved, includeScans);
tracker.forceUpdate();
tracker.copyAndNotifyListeners(false /*notifyListeners*/);
return tracker.getAccessPoints();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
index 79cee04..8b5863a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
@@ -16,8 +16,10 @@
package com.android.settingslib.wifi;
import android.content.Context;
-import android.os.Looper;
import android.support.annotation.Keep;
+import android.support.annotation.NonNull;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
/**
* Factory method used to inject WifiTracker instances.
@@ -31,12 +33,11 @@
}
public static WifiTracker create(
- Context context, WifiTracker.WifiListener wifiListener, Looper workerLooper,
- boolean includeSaved, boolean includeScans, boolean includePasspoints) {
+ Context context, WifiTracker.WifiListener wifiListener, @NonNull Lifecycle lifecycle,
+ boolean includeSaved, boolean includeScans) {
if(sTestingWifiTracker != null) {
return sTestingWifiTracker;
}
- return new WifiTracker(
- context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints);
+ return new WifiTracker(context, wifiListener, lifecycle, includeSaved, includeScans);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java b/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java
index cd62bc3..b1f3f3c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java
@@ -60,6 +60,13 @@
}
/**
+ * Calls {@code PackageManager.getInstalledPackagesAsUser}
+ */
+ public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
+ return mPm.getInstalledPackagesAsUser(flags, userId);
+ }
+
+ /**
* Calls {@code PackageManager.hasSystemFeature()}.
*
* @see android.content.pm.PackageManager#hasSystemFeature
@@ -132,11 +139,11 @@
/**
* Gets information about a particular package from the package manager.
+ *
* @param packageName The name of the package we would like information about.
- * @param i additional options flags. see javadoc for
- * {@link PackageManager#getPackageInfo(String, int)}
+ * @param i additional options flags. see javadoc for
+ * {@link PackageManager#getPackageInfo(String, int)}
* @return The PackageInfo for the requested package
- * @throws NameNotFoundException
*/
public PackageInfo getPackageInfo(String packageName, int i) throws NameNotFoundException {
return mPm.getPackageInfo(packageName, i);
@@ -144,6 +151,7 @@
/**
* Retrieves the icon associated with this particular set of ApplicationInfo
+ *
* @param info The ApplicationInfo to retrieve the icon for
* @return The icon as a drawable.
*/
@@ -154,6 +162,7 @@
/**
* Retrieves the label associated with the particular set of ApplicationInfo
+ *
* @param app The ApplicationInfo to retrieve the label for
* @return the label as a CharSequence
*/
@@ -190,4 +199,48 @@
throws PackageManager.NameNotFoundException {
return mPm.getPackageUidAsUser(pkg, userId);
}
+
+ /**
+ * Calls {@code PackageManager.setApplicationEnabledSetting}
+ */
+ public void setApplicationEnabledSetting(String packageName, int newState, int flags) {
+ mPm.setApplicationEnabledSetting(packageName, newState, flags);
+ }
+
+ /**
+ * Calls {@code PackageManager.getApplicationEnabledSetting}
+ */
+ public int getApplicationEnabledSetting(String packageName) {
+ return mPm.getApplicationEnabledSetting(packageName);
+ }
+
+ /**
+ * Calls {@code PackageManager.setComponentEnabledSetting}
+ */
+ public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) {
+ mPm.setComponentEnabledSetting(componentName, newState, flags);
+ }
+
+ /**
+ * Calls {@code PackageManager.getApplicationInfo}
+ */
+ public ApplicationInfo getApplicationInfo(String packageName, int flags)
+ throws NameNotFoundException {
+ return mPm.getApplicationInfo(packageName, flags);
+ }
+
+ /**
+ * Calls {@code PackageManager.getApplicationLabel}
+ */
+ public CharSequence getApplicationLabel(ApplicationInfo info) {
+ return mPm.getApplicationLabel(info);
+ }
+
+ /**
+ * Calls {@code PackageManager.queryBroadcastReceivers}
+ */
+ public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
+ return mPm.queryBroadcastReceivers(intent, flags);
+ }
}
+
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/StorageStatsSourceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/StorageStatsSourceTest.java
new file mode 100644
index 0000000..3dabe99
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/StorageStatsSourceTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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.settingslib.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.usage.StorageStats;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class StorageStatsSourceTest {
+ @Test
+ public void AppStorageStatsImpl_totalCorrectly() {
+ StorageStats storageStats = new StorageStats();
+ storageStats.cacheBytes = 1;
+ storageStats.codeBytes = 10;
+ storageStats.dataBytes = 100;
+ StorageStatsSource.AppStorageStatsImpl stats = new StorageStatsSource.AppStorageStatsImpl(
+ storageStats);
+
+ // Note that this does not double add the cache (111).
+ assertThat(stats.getTotalBytes()).isEqualTo(110);
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index f25bb28..4a1d392 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -54,7 +54,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.Looper;
import android.os.SystemClock;
import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
@@ -147,10 +146,7 @@
private CountDownLatch mAccessPointsChangedLatch;
private CountDownLatch mRequestScoresLatch;
private Handler mScannerHandler;
- private HandlerThread mMainThread;
private HandlerThread mWorkerThread;
- private Looper mWorkerLooper;
- private Looper mMainLooper;
private int mOriginalScoringUiSettingValue;
@@ -162,10 +158,6 @@
mWorkerThread = new HandlerThread("TestHandlerWorkerThread");
mWorkerThread.start();
- mWorkerLooper = mWorkerThread.getLooper();
- mMainThread = new HandlerThread("TestHandlerThread");
- mMainThread.start();
- mMainLooper = mMainThread.getLooper();
// Make sure the scanner doesn't try to run on the testing thread.
HandlerThread scannerThread = new HandlerThread("ScannerWorkerThread");
@@ -283,18 +275,17 @@
}
private WifiTracker createMockedWifiTracker() {
- return new WifiTracker(
+ final WifiTracker wifiTracker = new WifiTracker(
mContext,
mockWifiListener,
- mWorkerLooper,
- true,
true,
true,
mockWifiManager,
mockConnectivityManager,
mockNetworkScoreManager,
- mMainLooper,
new IntentFilter()); // empty filter to ignore system broadcasts
+ wifiTracker.setWorkThread(mWorkerThread);
+ return wifiTracker;
}
private void startTracking(WifiTracker tracker) throws InterruptedException {
@@ -302,7 +293,7 @@
mScannerHandler.post(new Runnable() {
@Override
public void run() {
- tracker.startTracking();
+ tracker.onStart();
latch.countDown();
}
});
@@ -406,7 +397,7 @@
scanResult.capabilities = "";
WifiTracker tracker = new WifiTracker(
- InstrumentationRegistry.getTargetContext(), null, mWorkerLooper, true, true);
+ InstrumentationRegistry.getTargetContext(), null, true, true);
AccessPoint result = tracker.getCachedOrCreate(scanResult, new ArrayList<AccessPoint>());
assertTrue(result.mAccessPointListener != null);
@@ -422,7 +413,7 @@
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiTracker tracker = new WifiTracker(
- InstrumentationRegistry.getTargetContext(), null, mWorkerLooper, true, true);
+ InstrumentationRegistry.getTargetContext(), null, true, true);
AccessPoint result = tracker.getCachedOrCreate(configuration, new ArrayList<AccessPoint>());
assertTrue(result.mAccessPointListener != null);
@@ -452,7 +443,7 @@
.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, scoreCache);
// Test unregister
- tracker.stopTracking();
+ tracker.onStop();
assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
verify(mockNetworkScoreManager)
@@ -496,7 +487,7 @@
// Start the tracker and inject the initial scan results and then stop tracking
WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
- tracker.stopTracking();
+ tracker.onStop();
mRequestedKeys.clear();
mRequestScoresLatch = new CountDownLatch(1);
@@ -515,7 +506,7 @@
// Start the tracker and inject the initial scan results and then stop tracking
WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
updateScoresAndWaitForAccessPointsChangedCallback(tracker);
- tracker.stopTracking();
+ tracker.onStop();
assertThat(mScoreCacheCaptor.getValue().getScoredNetwork(NETWORK_KEY_1)).isNotNull();
}
@@ -675,7 +666,7 @@
WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
WifiNetworkScoreCache cache = mScoreCacheCaptor.getValue();
- tracker.stopTracking();
+ tracker.onStop();
verify(mockNetworkScoreManager).unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, cache);
// Verify listener is unregistered so updating a score does not throw an error by posting
@@ -795,7 +786,7 @@
tracker.mMainHandler.sendEmptyMessage(
WifiTracker.MainHandler.MSG_WIFI_STATE_CHANGED);
- tracker.stopTracking();
+ tracker.onStop();
verify(mockWifiListener, atMost(1)).onAccessPointsChanged();
verify(mockWifiListener, atMost(1)).onConnectedChanged();
@@ -821,7 +812,7 @@
startTracking(tracker);
waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
- tracker.stopTracking();
+ tracker.onStop();
waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
startTracking(tracker);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
new file mode 100644
index 0000000..4dbb957
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 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.settingslib.deviceinfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class SerialNumberPreferenceControllerTest {
+
+ @Mock(answer = RETURNS_DEEP_STUBS)
+ private Context mContext;
+ @Mock(answer = RETURNS_DEEP_STUBS)
+ private PreferenceScreen mScreen;
+
+ private AbstractSerialNumberPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testIsAvaiable_noSerial_shouldReturnFalse() {
+ mController = new ConcreteSerialNumberPreferenceController(mContext, null);
+
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void testIsAvaiable_hasSerial_shouldReturnTrue() {
+ mController = new ConcreteSerialNumberPreferenceController(mContext, "123");
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void testDisplay_noSerial_shouldHidePreference() {
+ final Preference preference = mock(Preference.class);
+ when(mScreen.getPreferenceCount()).thenReturn(1);
+ when(mScreen.getPreference(0)).thenReturn(preference);
+ mController = new ConcreteSerialNumberPreferenceController(mContext, null);
+ when(preference.getKey()).thenReturn(mController.getPreferenceKey());
+
+ mController.displayPreference(mScreen);
+
+ verify(mScreen).removePreference(any(Preference.class));
+ }
+
+ @Test
+ public void testDisplay_hasSerial_shouldSetSummary() {
+ final String serial = "123";
+ final Preference preference = mock(Preference.class);
+ when(mScreen.findPreference(anyString())).thenReturn(preference);
+
+ mController = new ConcreteSerialNumberPreferenceController(mContext, serial);
+ mController.displayPreference(mScreen);
+
+ verify(mScreen, never()).removePreference(any(Preference.class));
+ verify(preference).setSummary(serial);
+ }
+
+ private static class ConcreteSerialNumberPreferenceController
+ extends AbstractSerialNumberPreferenceController {
+
+ ConcreteSerialNumberPreferenceController(Context context, String serialNumber) {
+ super(context, serialNumber);
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
index 1e65a0a..94f80d3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
@@ -100,4 +100,15 @@
assertThat(twinDrawable.getLayerInsetTop(1)).isEqualTo(
drawable.getLayerInsetTop(1));
}
+
+ @Test
+ public void testCreateLayerDrawable_bluetoothDrawable_hasCorrectFrameColor() {
+ BluetoothDeviceLayerDrawable drawable = BluetoothDeviceLayerDrawable.createLayerDrawable(
+ mContext, RES_ID, BATTERY_LEVEL);
+ BluetoothDeviceLayerDrawable.BatteryMeterDrawable batteryMeterDrawable =
+ (BluetoothDeviceLayerDrawable.BatteryMeterDrawable) drawable.getDrawable(1);
+
+ assertThat(batteryMeterDrawable.mFrameColor).isEqualTo(
+ mContext.getColor(R.color.meter_background_color));
+ }
}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 6da67df..1be0645 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -193,4 +193,7 @@
<!-- Default for Settings.Secure.BACKUP_MANAGER_CONSTANTS -->
<string name="def_backup_manager_constants"></string>
+
+ <!-- Default setting for Settings.Global.MOBILE_DATA_ALWAYS_ON -->
+ <bool name="def_mobile_data_always_on">true</bool>
</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index a463db6..36f9b84 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2896,7 +2896,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 149;
+ private static final int SETTINGS_VERSION = 150;
private final int mUserId;
@@ -3470,9 +3470,25 @@
true, SettingsState.SYSTEM_PACKAGE_NAME);
}
}
-
currentVersion = 149;
}
+
+ if (currentVersion == 149) {
+ // Version 150: Set a default value for mobile data always on
+ final SettingsState globalSettings = getGlobalSettingsLocked();
+ final Setting currentSetting = globalSettings.getSettingLocked(
+ Settings.Global.MOBILE_DATA_ALWAYS_ON);
+ if (currentSetting.isNull()) {
+ globalSettings.insertSettingLocked(
+ Settings.Global.MOBILE_DATA_ALWAYS_ON,
+ getContext().getResources().getBoolean(
+ R.bool.def_mobile_data_always_on) ? "1" : "0",
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+
+ currentVersion = 150;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b758e7f5..eab42da 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -96,6 +96,9 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.CREATE_USERS" />
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
+ <uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/>
+ <uses-permission android:name="android.permission.CHANGE_LOWPAN_STATE"/>
+ <uses-permission android:name="android.permission.READ_LOWPAN_CREDENTIAL"/>
<uses-permission android:name="android.permission.BLUETOOTH_STACK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.RETRIEVE_WINDOW_TOKEN" />
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index 82f7227..92c8b9e 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -35,7 +35,7 @@
<string name="keyguard_plugged_in_charging_slowly" msgid="6637043106038550407">"ਹੌਲੀ-ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
<string name="keyguard_low_battery" msgid="9218432555787624490">"ਆਪਣਾ ਚਾਰਜਰ ਕਨੈਕਟ ਕਰੋ।"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8566679946700751371">"ਅਣਲਾਕ ਕਰਨ ਲਈ \"ਮੀਨੂ\" ਦਬਾਓ।"</string>
- <string name="keyguard_network_locked_message" msgid="6743537524631420759">"ਨੈੱਟਵਰਕ ਲੌਕ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="keyguard_network_locked_message" msgid="6743537524631420759">"ਨੈੱਟਵਰਕ ਲਾਕ ਕੀਤਾ ਗਿਆ"</string>
<string name="keyguard_missing_sim_message_short" msgid="6327533369959764518">"ਕੋਈ ਸਿਮ ਕਾਰਡ ਨਹੀਂ"</string>
<string name="keyguard_missing_sim_message" product="tablet" msgid="4550152848200783542">"ਟੈਬਲੈੱਟ ਵਿੱਚ ਕੋਈ ਸਿਮ ਕਾਰਡ ਮੌਜੂਦ ਨਹੀਂ।"</string>
<string name="keyguard_missing_sim_message" product="default" msgid="6585414237800161146">"ਫ਼ੋਨ ਵਿੱਚ ਕੋਈ ਸਿਮ ਕਾਰਡ ਮੌਜੂਦ ਨਹੀਂ।"</string>
@@ -43,9 +43,9 @@
<string name="keyguard_missing_sim_instructions_long" msgid="589889372883904477">"SIM ਕਾਰਡ ਮੌਜੂਦ ਨਹੀਂ ਜਾਂ ਪੜ੍ਹਨਯੋਗ ਨਹੀਂ ਹੈ। ਇੱਕ SIM ਕਾਰਡ ਪਾਓ।"</string>
<string name="keyguard_permanent_disabled_sim_message_short" msgid="654102080186420706">"ਨਾ-ਵਰਤਣਯੋਗ SIM ਕਾਰਡ।"</string>
<string name="keyguard_permanent_disabled_sim_instructions" msgid="4683178224791318347">"ਤੁਹਾਡਾ ਸਿਮ ਕਾਰਡ ਸਥਾਈ ਤੌਰ \'ਤੇ ਅਯੋਗ ਬਣਾ ਦਿੱਤਾ ਗਿਆ ਹੈ।\n ਇੱਕ ਹੋਰ ਸਿਮ ਕਾਰਡ ਲਈ ਆਪਣੇ ਵਾਇਰਲੈੱਸ ਸੇਵਾ ਪ੍ਰਦਾਨਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string>
- <string name="keyguard_sim_locked_message" msgid="953766009432168127">"SIM ਕਾਰਡ ਲੌਕ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
- <string name="keyguard_sim_puk_locked_message" msgid="1772789643694942073">"SIM ਕਾਰਡ PUK-ਲੌਕ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
- <string name="keyguard_sim_unlock_progress_dialog_message" msgid="3586601150825821675">"SIM ਕਾਰਡ ਨੂੰ ਅਨਲੌਕ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
+ <string name="keyguard_sim_locked_message" msgid="953766009432168127">"SIM ਕਾਰਡ ਲਾਕ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="1772789643694942073">"SIM ਕਾਰਡ PUK- ਲਾਕ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="3586601150825821675">"SIM ਕਾਰਡ ਨੂੰ ਅਣਲਾਕ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
<string name="keyguard_accessibility_pin_area" msgid="703175752097279029">"ਪਿੰਨ ਖੇਤਰ"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="912702510825058921">"ਸਿਮ ਪਿੰਨ ਖੇਤਰ"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="136979425761438705">"SIM PUK ਖੇਤਰ"</string>
@@ -71,7 +71,7 @@
<string name="kg_puk_enter_puk_hint_multi" msgid="1373131883510840794">"ਸਿਮ \"<xliff:g id="CARRIER">%1$s</xliff:g>\" ਹੁਣ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ। ਜਾਰੀ ਰੱਖਣ ਲਈ PUK ਕੋਡ ਦਾਖਲ ਕਰੋ। ਵੇਰਵਿਆਂ ਲਈ ਕੈਰੀਅਰ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"</string>
<string name="kg_puk_enter_pin_hint" msgid="3137789674920391087">"ਇੱਛਤ ਪਿੰਨ ਕੋਡ ਦਾਖਲ ਕਰੋ"</string>
<string name="kg_enter_confirm_pin_hint" msgid="3089485999116759671">"ਇੱਛਤ ਪਿੰਨ ਕੋਡ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ"</string>
- <string name="kg_sim_unlock_progress_dialog_message" msgid="4471738151810900114">"SIM ਕਾਰਡ ਨੂੰ ਅਨਲੌਕ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="4471738151810900114">"SIM ਕਾਰਡ ਨੂੰ ਅਣਲਾਕ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
<string name="kg_invalid_sim_pin_hint" msgid="3057533256729513335">"ਕੋਈ ਪਿੰਨ ਟਾਈਪ ਕਰੋ ਜੋ 4 ਤੋਂ 8 ਨੰਬਰਾਂ ਦਾ ਹੋਵੇ।"</string>
<string name="kg_invalid_sim_puk_hint" msgid="6003602401368264144">"PUK ਕੋਡ 8 ਜਾਂ ਵੱਧ ਨੰਬਰਾਂ ਦਾ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ।"</string>
<string name="kg_invalid_puk" msgid="5399287873762592502">"ਸਹੀ PUK ਕੋਡ ਮੁੜ-ਦਾਖਲ ਕਰੋ। ਬਾਰ-ਬਾਰ ਕੀਤੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ ਸਿਮ ਨੂੰ ਸਥਾਈ ਤੌਰ \'ਤੇ ਬੰਦ ਕਰ ਦੇਣਗੀਆਂ।"</string>
diff --git a/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml b/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml
index db2eb3a..bff97f6 100644
--- a/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml
+++ b/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml
@@ -17,6 +17,6 @@
<corners android:radius="@dimen/borderless_button_radius" />
- <solid android:color="#CC000000" />
+ <solid android:color="?attr/clearAllBackgroundColor" />
</shape>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 520df00..116a061 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -659,7 +659,7 @@
<string name="volume_dnd_silent" msgid="4363882330723050727">"اختصار أزرار مستوى الصوت"</string>
<string name="volume_up_silent" msgid="7141255269783588286">"تعطيل \"عدم الإزعاج\" عند رفع مستوى الصوت"</string>
<string name="battery" msgid="7498329822413202973">"البطارية"</string>
- <string name="clock" msgid="7416090374234785905">"ساعة"</string>
+ <string name="clock" msgid="7416090374234785905">"الساعة"</string>
<string name="headset" msgid="4534219457597457353">"سماعة الرأس"</string>
<string name="accessibility_status_bar_headphones" msgid="9156307120060559989">"تم توصيل سماعات رأس"</string>
<string name="accessibility_status_bar_headset" msgid="8666419213072449202">"تم توصيل سماعات رأس"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 6cab2ec..f43a048 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -336,7 +336,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"épinglage d\'écran"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Impossible de lancer <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> est désactivée en mode sécurisé."</string>
+ <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> est désactivée en mode sans échec."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Effacer tout"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Glissez l\'élément ici pour utiliser l\'écran partagé"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Séparation horizontale"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index fd78108..e30dbcb 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -188,7 +188,7 @@
<string name="accessibility_desc_settings" msgid="3417884241751434521">"ക്രമീകരണം"</string>
<string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"അവലോകനം."</string>
<string name="accessibility_desc_work_lock" msgid="4288774420752813383">"ഔദ്യോഗിക ലോക്ക് സ്ക്രീൻ"</string>
- <string name="accessibility_desc_close" msgid="7479755364962766729">"അടയ്ക്കുക"</string>
+ <string name="accessibility_desc_close" msgid="7479755364962766729">"അവസാനിപ്പിക്കുക"</string>
<string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
<string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"വൈഫൈ ഓഫാക്കി."</string>
<string name="accessibility_quick_settings_wifi_changed_on" msgid="6440117170789528622">"വൈഫൈ ഓണാക്കി."</string>
@@ -724,7 +724,7 @@
<string name="tuner_lock_screen" msgid="5755818559638850294">"ലോക്ക് സ്ക്രീൻ"</string>
<string name="pip_phone_expand" msgid="5889780005575693909">"വികസിപ്പിക്കുക"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"ചെറുതാക്കുക"</string>
- <string name="pip_phone_close" msgid="8416647892889710330">"അടയ്ക്കുക"</string>
+ <string name="pip_phone_close" msgid="8416647892889710330">"അവസാനിപ്പിക്കുക"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"തള്ളിക്കളയാൻ താഴേക്ക് വലിച്ചിടുക"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"മെനു"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ചിത്രത്തിനുള്ളിലെ ചിത്രത്തിലാണ്"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index ec9fde8..3b31b2d 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -752,7 +752,7 @@
<string name="tuner_left" msgid="8404287986475034806">"Links"</string>
<string name="tuner_right" msgid="6222734772467850156">"Rechts"</string>
<string name="tuner_menu" msgid="191640047241552081">"Menu"</string>
- <string name="tuner_app" msgid="3507057938640108777">"Apps <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="tuner_app" msgid="3507057938640108777">"<xliff:g id="APP">%1$s</xliff:g>-app"</string>
<string name="notification_channel_alerts" msgid="4496839309318519037">"Meldingen"</string>
<string name="notification_channel_battery" msgid="5786118169182888462">"Batterij"</string>
<string name="notification_channel_screenshot" msgid="6314080179230000938">"Screenshots"</string>
diff --git a/packages/SystemUI/res/values-pa-land/strings.xml b/packages/SystemUI/res/values-pa-land/strings.xml
index fef122a..dfe2a5d 100644
--- a/packages/SystemUI/res/values-pa-land/strings.xml
+++ b/packages/SystemUI/res/values-pa-land/strings.xml
@@ -19,5 +19,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="toast_rotation_locked" msgid="7609673011431556092">"ਸਕ੍ਰੀਨ ਹੁਣ ਲੈਂਡਸਕੇਪ ਅਨੁਕੂਲਨ ਵਿੱਚ ਲੌਕ ਕੀਤੀ ਗਈ ਹੈ।"</string>
+ <string name="toast_rotation_locked" msgid="7609673011431556092">"ਸਕ੍ਰੀਨ ਹੁਣ ਲੈਂਡਸਕੇਪ ਅਨੁਕੂਲਨ ਵਿੱਚ ਲਾਕ ਕੀਤੀ ਗਈ ਹੈ।"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 166e0cb..c2089a6 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -184,10 +184,10 @@
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"ਸੂਚਨਾ ਰੱਦ ਕੀਤੀ।"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"ਸੂਚਨਾ ਸ਼ੇਡ।"</string>
<string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ।"</string>
- <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"ਲੌਕ ਸਕ੍ਰੀਨ।"</string>
+ <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">" ਲਾਕ ਸਕ੍ਰੀਨ।"</string>
<string name="accessibility_desc_settings" msgid="3417884241751434521">"ਸੈਟਿੰਗਾਂ"</string>
<string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"ਰੂਪ-ਰੇਖਾ।"</string>
- <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"ਕਾਰਜ-ਸਥਾਨ ਲੌਕ ਸਕ੍ਰੀਨ"</string>
+ <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"ਕਾਰਜ-ਸਥਾਨ ਲਾਕ ਸਕ੍ਰੀਨ"</string>
<string name="accessibility_desc_close" msgid="7479755364962766729">"ਬੰਦ ਕਰੋ"</string>
<string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>।"</string>
<string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi ਬੰਦ ਕੀਤਾ।"</string>
@@ -258,11 +258,11 @@
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"ਸੂਚਨਾ ਸੈਟਿੰਗਾਂ"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> ਸੈਟਿੰਗਾਂ"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"ਸਕ੍ਰੀਨ ਆਟੋਮੈਟਿਕਲੀ ਰੋਟੇਟ ਕਰੇਗੀ।"</string>
- <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"ਸਕ੍ਰੀਨ ਲੈਂਡਸਕੇਪ ਅਨੁਕੂਲਨ ਵਿੱਚ ਲੌਕ ਕੀਤੀ ਹੈ।"</string>
- <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"ਸਕ੍ਰੀਨ ਪੋਰਟਰੇਟ ਅਨੁਕੂਲਨ ਵਿੱਚ ਲੌਕ ਕੀਤੀ ਗਈ ਹੈ।"</string>
+ <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"ਸਕ੍ਰੀਨ ਲੈਂਡਸਕੇਪ ਅਨੁਕੂਲਨ ਵਿੱਚ ਲਾਕ ਕੀਤੀ ਹੈ।"</string>
+ <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"ਸਕ੍ਰੀਨ ਪੋਰਟਰੇਟ ਅਨੁਕੂਲਨ ਵਿੱਚ ਲਾਕ ਕੀਤੀ ਗਈ ਹੈ।"</string>
<string name="accessibility_rotation_lock_off_changed" msgid="8134601071026305153">"ਸਕ੍ਰੀਨ ਹੁਣ ਆਟੋਮੈਟਿਕਲੀ ਰੋਟੇਟ ਕਰੇਗੀ।"</string>
- <string name="accessibility_rotation_lock_on_landscape_changed" msgid="3135965553707519743">"ਸਕ੍ਰੀਨ ਹੁਣ ਲੈਂਡਸਕੇਪ ਅਨੁਕੂਲਨ ਵਿੱਚ ਲੌਕ ਕੀਤੀ ਗਈ ਹੈ।"</string>
- <string name="accessibility_rotation_lock_on_portrait_changed" msgid="8922481981834012126">"ਸਕ੍ਰੀਨ ਹੁਣ ਪੋਰਟਰੇਟ ਅਨੁਕੂਲਨ ਵਿੱਚ ਲੌਕ ਕੀਤੀ ਗਈ ਹੈ।"</string>
+ <string name="accessibility_rotation_lock_on_landscape_changed" msgid="3135965553707519743">"ਸਕ੍ਰੀਨ ਹੁਣ ਲੈਂਡਸਕੇਪ ਅਨੁਕੂਲਨ ਵਿੱਚ ਲਾਕ ਕੀਤੀ ਗਈ ਹੈ।"</string>
+ <string name="accessibility_rotation_lock_on_portrait_changed" msgid="8922481981834012126">"ਸਕ੍ਰੀਨ ਹੁਣ ਪੋਰਟਰੇਟ ਅਨੁਕੂਲਨ ਵਿੱਚ ਲਾਕ ਕੀਤੀ ਗਈ ਹੈ।"</string>
<string name="dessert_case" msgid="1295161776223959221">"ਡੈਜ਼ਰਟ ਕੇਸ"</string>
<string name="start_dreams" msgid="5640361424498338327">"ਸਕ੍ਰੀਨ ਸੇਵਰ"</string>
<string name="ethernet_label" msgid="7967563676324087464">"ਈਥਰਨੈਟ"</string>
@@ -278,7 +278,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"ਆਟੋ-ਰੋਟੇਟ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4231661040698488779">"ਸਕ੍ਰੀਨ ਨੂੰ ਆਪਣੇ ਆਪ ਘੁੰਮਾਓ"</string>
<string name="accessibility_quick_settings_rotation_value" msgid="8187398200140760213">"<xliff:g id="ID_1">%s</xliff:g> ਮੋਡ"</string>
- <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"ਰੋਟੇਸ਼ਨ ਲੌਕ ਕੀਤੀ"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"ਰੋਟੇਸ਼ਨ ਲਾਕ ਕੀਤੀ"</string>
<string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"ਪੋਰਟਰੇਟ"</string>
<string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"ਲੈਂਡਸਕੇਪ"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"ਇਨਪੁੱਟ ਵਿਧੀ"</string>
@@ -468,7 +468,7 @@
<string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"ਤੁਸੀਂ <xliff:g id="APPLICATION">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੋ, ਜੋ ਈਮੇਲਾਂ, ਐਪਾਂ, ਅਤੇ ਵੈੱਬਸਾਈਟਾਂ ਸਮੇਤ ਤੁਹਾਡੀ ਨਿੱਜੀ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ।"</string>
<string name="monitoring_description_app_work" msgid="4612997849787922906">"ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਦਾ ਪ੍ਰਬੰਧਨ <xliff:g id="ORGANIZATION">%1$s</xliff:g> ਵੱਲੋਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਇਹ ਪ੍ਰੋਫਾਈਲ <xliff:g id="APPLICATION">%2$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ, ਜੋ ਈਮੇਲਾਂ, ਐਪਾਂ, ਅਤੇ ਵੈੱਬਸਾਈਟਾਂ ਸਮੇਤ ਤੁਹਾਡੀ ਕਾਰਜ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ।\n\nਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"</string>
<string name="monitoring_description_app_personal_work" msgid="5664165460056859391">"ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਦਾ ਪ੍ਰਬੰਧਨ <xliff:g id="ORGANIZATION">%1$s</xliff:g> ਵੱਲੋਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਪ੍ਰੋਫਾਈਲ <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ ਹੈ, ਜੋ ਈਮੇਲਾਂ, ਐਪਾਂ, ਅਤੇ ਵੈੱਬਸਾਈਟਾਂ ਸਮੇਤ ਤੁਹਾਡੀ ਕਾਰਜ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ।\n\nਤੁਸੀਂ <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> ਨਾਲ ਵੀ ਕਨੈਕਟ ਹੋਂ, ਜੋ ਤੁਹਾਡੀ ਨਿੱਜੀ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ।"</string>
- <string name="keyguard_indication_trust_granted" msgid="4985003749105182372">"<xliff:g id="USER_NAME">%1$s</xliff:g> ਲਈ ਅਨਲੌਕ ਕੀਤੀ ਗਈ"</string>
+ <string name="keyguard_indication_trust_granted" msgid="4985003749105182372">"<xliff:g id="USER_NAME">%1$s</xliff:g> ਲਈ ਅਣਲਾਕ ਕੀਤੀ ਗਈ"</string>
<string name="keyguard_indication_trust_managed" msgid="8319646760022357585">"<xliff:g id="TRUST_AGENT">%1$s</xliff:g> ਚੱਲ ਰਿਹਾ ਹੈ"</string>
<string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"ਡੀਵਾਈਸ ਲਾਕ ਰਹੇਗਾ ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਮੈਨੂਅਲੀ ਅਣਲਾਕ ਨਹੀਂ ਕਰਦੇ"</string>
<string name="hidden_notifications_title" msgid="7139628534207443290">"ਤੇਜ਼ੀ ਨਾਲ ਸੂਚਨਾਵਾਂ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
@@ -587,7 +587,7 @@
<string name="battery_panel_title" msgid="7944156115535366613">"ਬੈਟਰੀ ਵਰਤੋਂ"</string>
<string name="battery_detail_charging_summary" msgid="4055327085770378335">"ਬੈਟਰੀ ਸੇਵਰ ਚਾਰਜਿੰਗ ਦੌਰਾਨ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="battery_detail_switch_title" msgid="8763441006881907058">"ਬੈਟਰੀ ਸੇਵਰ"</string>
- <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ਕਾਰਗੁਜ਼ਾਰੀ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਡੈਟੇ ਨੂੰ ਘਟਾਉਂਦਾ ਹੈ"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ਕਾਰਗੁਜ਼ਾਰੀ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਡਾਟੇ ਨੂੰ ਘਟਾਉਂਦਾ ਹੈ"</string>
<string name="keyboard_key_button_template" msgid="6230056639734377300">"ਬਟਨ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
<string name="keyboard_key_back" msgid="2337450286042721351">"Back"</string>
@@ -721,7 +721,7 @@
<string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ।"</string>
<string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ਸੈਟਿੰਗਾਂ ਦੇ ਕ੍ਰਮ ਦਾ ਸੰਪਾਦਨ ਕਰੋ।"</string>
<string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> ਦਾ <xliff:g id="ID_1">%1$d</xliff:g> ਪੰਨਾ"</string>
- <string name="tuner_lock_screen" msgid="5755818559638850294">"ਲੌਕ ਸਕ੍ਰੀਨ"</string>
+ <string name="tuner_lock_screen" msgid="5755818559638850294">" ਲਾਕ ਸਕ੍ਰੀਨ"</string>
<string name="pip_phone_expand" msgid="5889780005575693909">"ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"ਛੋਟਾ ਕਰੋ"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"ਬੰਦ ਕਰੋ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 7ff9b73..a725e53 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -244,7 +244,7 @@
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Transmisja danych 4G została wstrzymana"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="6801382439018099779">"Mobilna transmisja danych jest wstrzymana"</string>
<string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Transmisja danych została wstrzymana"</string>
- <string name="data_usage_disabled_dialog" msgid="4919541636934603816">"Osiągnięto ustawiony limit danych. Nie korzystasz już z komórkowej transmisji danych.\n\nJeśli włączysz ją ponownie, może zostać naliczona opłata za transmisję danych."</string>
+ <string name="data_usage_disabled_dialog" msgid="4919541636934603816">"Osiągnięto ustawiony limit danych. Nie korzystasz już z komórkowej transmisji danych.\n\nJeśli włączysz ją ponownie, może zostać naliczona opłata za użycie danych."</string>
<string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Wznów"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Brak internetu"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi: połączono"</string>
@@ -785,7 +785,7 @@
<string name="qs_dnd_keep" msgid="1825009164681928736">"Zachowaj"</string>
<string name="qs_dnd_replace" msgid="8019520786644276623">"Zastąp"</string>
<string name="running_foreground_services_title" msgid="381024150898615683">"Aplikacje działające w tle"</string>
- <string name="running_foreground_services_msg" msgid="6326247670075574355">"Kliknij, by wyświetlić szczegóły wykorzystania baterii i transmisji danych"</string>
+ <string name="running_foreground_services_msg" msgid="6326247670075574355">"Kliknij, by wyświetlić szczegóły wykorzystania baterii i użycia danych"</string>
<string name="data_usage_disable_mobile" msgid="5116269981510015864">"Wyłączyć mobilną transmisję danych?"</string>
<string name="touch_filtered_warning" msgid="8671693809204767551">"Aplikacja Ustawienia nie może zweryfikować Twojej odpowiedzi, ponieważ inna aplikacja zasłania prośbę o udzielenie uprawnień."</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index a4b45e5..db7ea81 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -616,7 +616,7 @@
<string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
<string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Prehrať/pozastaviť"</string>
<string name="keyboard_key_media_stop" msgid="2859963958595908962">"Zastaviť"</string>
- <string name="keyboard_key_media_next" msgid="1894394911630345607">"Nasledujúce"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Ďalej"</string>
<string name="keyboard_key_media_previous" msgid="4256072387192967261">"Predchádzajúce"</string>
<string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Pretočiť späť"</string>
<string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Pretočiť dopredu"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 969a0a2..ad39fea 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -243,7 +243,7 @@
<string name="data_usage_disabled_dialog_mobile_title" msgid="6801382439018099779">"หยุดการใช้อินเทอร์เน็ตมือถือชั่วคราว"</string>
<string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"หยุดการใช้ข้อมูลชั่วคราวแล้ว"</string>
<string name="data_usage_disabled_dialog" msgid="4919541636934603816">"คุณใช้อินเทอร์เน็ตถึงปริมาณที่กำหนดไว้แล้ว คุณไม่ได้ใช้อินเทอร์เน็ตมือถือแล้วในขณะนี้\n\nหากใช้ต่อ อาจมีการเรียกเก็บค่าบริการอินเทอร์เน็ต"</string>
- <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"ทำต่อ"</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"ใช้ต่อ"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"ไม่มีอินเทอร์เน็ต"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"เชื่อมต่อ WiFi แล้ว"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"กำลังค้นหา GPS"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 9f44d67..aa2eee4 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -155,7 +155,7 @@
<string name="accessibility_cell_data" msgid="5326139158682385073">"موبائل ڈیٹا"</string>
<string name="accessibility_cell_data_on" msgid="5927098403452994422">"موبائل ڈیٹا آن ہے"</string>
<string name="accessibility_cell_data_off" msgid="443267573897409704">"موبائل ڈیٹا آف ہے"</string>
- <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"بلوٹوتھ ٹیتھرنگ۔"</string>
+ <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"بلوٹوتھ ٹیدرنگ۔"</string>
<string name="accessibility_airplane_mode" msgid="834748999790763092">"ہوائی جہاز وضع۔"</string>
<string name="accessibility_vpn_on" msgid="5993385083262856059">"VPN آن ہے۔"</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"کوئی SIM کارڈ نہیں ہے۔"</string>
@@ -312,7 +312,7 @@
<string name="quick_settings_connected" msgid="1722253542984847487">"مربوط"</string>
<string name="quick_settings_connected_battery_level" msgid="4136051440381328892">"منسلک ہے، بیٹری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="quick_settings_connecting" msgid="47623027419264404">"مربوط ہو رہا ہے…"</string>
- <string name="quick_settings_tethering_label" msgid="7153452060448575549">"ٹیتھرنگ"</string>
+ <string name="quick_settings_tethering_label" msgid="7153452060448575549">"ٹیدرنگ"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ہاٹ اسپاٹ"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"اطلاعات"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"فلیش لائٹ"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index d767979..2152ae3 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -73,7 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"已拍攝螢幕擷取畫面。"</string>
<string name="screenshot_saved_text" msgid="2685605830386712477">"輕觸即可查看螢幕擷圖。"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"無法拍攝螢幕擷取畫面。"</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"儲存螢幕擷圖時發生問題。"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"儲存螢幕擷取畫面時發生問題。"</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"由於儲存空間有限,因此無法儲存螢幕擷取畫面。"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"這個應用程式或貴機構不允許擷取螢幕畫面"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB 檔案傳輸選項"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 87f6306..8a1e0b9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -422,4 +422,14 @@
increase the rate of unintentional unlocks. -->
<bool name="config_lockscreenAntiFalsingClassifierEnabled">true</bool>
+ <!-- Snooze: default notificaiton snooze time. -->
+ <integer name="config_notification_snooze_time_default">60</integer>
+
+ <!-- Snooze: List of snooze values in integer minutes. -->
+ <integer-array name="config_notification_snooze_times">
+ <item>15</item>
+ <item>30</item>
+ <item>60</item>
+ <item>120</item>
+ </integer-array>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e6a357f..9901f6f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -80,12 +80,6 @@
<!-- Height of a small notification in the status bar which was used before android N -->
<dimen name="notification_min_height_legacy">64dp</dimen>
- <!-- The increase in minHeight that is allowed when the notification is colorized -->
- <dimen name="notification_height_increase_colorized">11sp</dimen>
-
- <!-- The increase in minHeight that is allowed when the notification is colorized and has increased height (i.e messages) -->
- <dimen name="notification_height_increase_colorized_increased">13sp</dimen>
-
<!-- Height of a large notification in the status bar -->
<dimen name="notification_max_height">284dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 2148c80..e5f8029 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -82,10 +82,10 @@
<!-- Accessibility actions for the notification menu -->
<item type="id" name="action_snooze_undo"/>
- <item type="id" name="action_snooze_15_min"/>
- <item type="id" name="action_snooze_30_min"/>
- <item type="id" name="action_snooze_1_hour"/>
- <item type="id" name="action_snooze_2_hours"/>
+ <item type="id" name="action_snooze_shorter"/>
+ <item type="id" name="action_snooze_short"/>
+ <item type="id" name="action_snooze_long"/>
+ <item type="id" name="action_snooze_longer"/>
<item type="id" name="action_snooze_assistant_suggestion_1"/>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index 7225ba9..432b406 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -66,7 +66,11 @@
// again when the PUK locked SIM is re-entered.
case ABSENT: {
KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
- mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ // onSimStateChanged callback can fire when the SIM PIN lock is not currently
+ // active and mCallback is null.
+ if (mCallback != null) {
+ mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ }
break;
}
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 171cf23..7f79008 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -72,7 +72,11 @@
// move into the READY state and the PUK lock keyguard should be removed.
case READY: {
KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
- mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ // mCallback can be null if onSimStateChanged callback is called when keyguard
+ // isn't active.
+ if (mCallback != null) {
+ mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ }
break;
}
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d95402c..d83a6c6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.ACTION_USER_UNLOCKED;
import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
import static android.os.BatteryManager.BATTERY_STATUS_FULL;
@@ -452,6 +454,7 @@
*/
public void setKeyguardGoingAway(boolean goingAway) {
mKeyguardGoingAway = goingAway;
+ updateFingerprintListeningState();
}
/**
@@ -1069,6 +1072,7 @@
cb.onDreamingStateChanged(mIsDreaming);
}
}
+ updateFingerprintListeningState();
}
/**
@@ -1772,7 +1776,7 @@
public void onTaskStackChangedBackground() {
try {
ActivityManager.StackInfo info = ActivityManager.getService().getStackInfo(
- ActivityManager.StackId.ASSISTANT_STACK_ID);
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
if (info == null) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 2b31967..2fe66a1 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui;
+import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
+import static android.app.StatusBarManager.DISABLE_NONE;
import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
import android.animation.ArgbEvaluator;
@@ -52,6 +54,7 @@
import com.android.systemui.statusbar.policy.IconLogger;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.Utils.DisableStateTracker;
import java.text.NumberFormat;
@@ -101,6 +104,9 @@
mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));
+ addOnAttachStateChangeListener(
+ new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS));
+
mSlotBattery = context.getString(
com.android.internal.R.string.status_bar_battery);
mBatteryIconView = new ImageView(context);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index c5eebcc..8a8bafa 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -61,6 +61,7 @@
private AssistOrbContainer mView;
private final DeviceProvisionedController mDeviceProvisionedController;
protected final AssistUtils mAssistUtils;
+ private final boolean mShouldEnableOrb;
private IVoiceInteractionSessionShowCallback mShowCallback =
new IVoiceInteractionSessionShowCallback.Stub() {
@@ -96,6 +97,7 @@
| ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_UI_MODE
| ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS);
onConfigurationChanged(context.getResources().getConfiguration());
+ mShouldEnableOrb = !ActivityManager.isLowRamDeviceStatic();
}
protected void registerVoiceInteractionSessionListener() {
@@ -179,7 +181,9 @@
private void showOrb(@NonNull ComponentName assistComponent, boolean isService) {
maybeSwapSearchIcon(assistComponent, isService);
- mView.show(true /* show */, true /* animate */);
+ if (mShouldEnableOrb) {
+ mView.show(true /* show */, true /* animate */);
+ }
}
private void startAssistInternal(Bundle args, @NonNull ComponentName assistComponent,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
index 5c99961..debda21 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
@@ -16,8 +16,13 @@
package com.android.systemui.doze;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.util.KeyValueListParser;
@@ -34,6 +39,10 @@
public class AlwaysOnDisplayPolicy {
public static final String TAG = "AlwaysOnDisplayPolicy";
+ private static final long DEFAULT_PROX_SCREEN_OFF_DELAY_MS = 10 * DateUtils.SECOND_IN_MILLIS;
+ private static final long DEFAULT_PROX_COOLDOWN_TRIGGER_MS = 2 * DateUtils.SECOND_IN_MILLIS;
+ private static final long DEFAULT_PROX_COOLDOWN_PERIOD_MS = 5 * DateUtils.SECOND_IN_MILLIS;
+
static final String KEY_SCREEN_BRIGHTNESS_ARRAY = "screen_brightness_array";
static final String KEY_DIMMING_SCRIM_ARRAY = "dimming_scrim_array";
static final String KEY_PROX_SCREEN_OFF_DELAY_MS = "prox_screen_off_delay";
@@ -46,7 +55,7 @@
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_SCREEN_BRIGHTNESS_ARRAY
*/
- public final int[] screenBrightnessArray;
+ public int[] screenBrightnessArray;
/**
* Integer array to map ambient brightness type to dimming scrim.
@@ -54,7 +63,7 @@
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_DIMMING_SCRIM_ARRAY
*/
- public final int[] dimmingScrimArray;
+ public int[] dimmingScrimArray;
/**
* Delay time(ms) from covering the prox to turning off the screen.
@@ -62,7 +71,7 @@
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_PROX_SCREEN_OFF_DELAY_MS
*/
- public final long proxScreenOffDelayMs;
+ public long proxScreenOffDelayMs;
/**
* The threshold time(ms) to trigger the cooldown timer, which will
@@ -71,7 +80,7 @@
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_PROX_COOLDOWN_TRIGGER_MS
*/
- public final long proxCooldownTriggerMs;
+ public long proxCooldownTriggerMs;
/**
* The period(ms) to turning off the prox sensor if
@@ -80,43 +89,78 @@
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_PROX_COOLDOWN_PERIOD_MS
*/
- public final long proxCooldownPeriodMs;
+ public long proxCooldownPeriodMs;
private final KeyValueListParser mParser;
+ private final Context mContext;
+ private SettingsObserver mSettingsObserver;
public AlwaysOnDisplayPolicy(Context context) {
- final Resources resources = context.getResources();
+ mContext = context;
mParser = new KeyValueListParser(',');
-
- final String value = Settings.Global.getString(context.getContentResolver(),
- Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
-
- try {
- mParser.setString(value);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Bad AOD constants");
- }
-
- proxScreenOffDelayMs = mParser.getLong(KEY_PROX_SCREEN_OFF_DELAY_MS,
- 10 * DateUtils.SECOND_IN_MILLIS);
- proxCooldownTriggerMs = mParser.getLong(KEY_PROX_COOLDOWN_TRIGGER_MS,
- 2 * DateUtils.SECOND_IN_MILLIS);
- proxCooldownPeriodMs = mParser.getLong(KEY_PROX_COOLDOWN_PERIOD_MS,
- 5 * DateUtils.SECOND_IN_MILLIS);
- screenBrightnessArray = parseIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
- resources.getIntArray(R.array.config_doze_brightness_sensor_to_brightness));
- dimmingScrimArray = parseIntArray(KEY_DIMMING_SCRIM_ARRAY,
- resources.getIntArray(R.array.config_doze_brightness_sensor_to_scrim_opacity));
+ mSettingsObserver = new SettingsObserver(context.getMainThreadHandler());
+ mSettingsObserver.observe();
}
private int[] parseIntArray(final String key, final int[] defaultArray) {
final String value = mParser.getString(key, null);
if (value != null) {
- return Arrays.stream(value.split(":")).map(String::trim).mapToInt(
- Integer::parseInt).toArray();
+ try {
+ return Arrays.stream(value.split(":")).map(String::trim).mapToInt(
+ Integer::parseInt).toArray();
+ } catch (NumberFormatException e) {
+ return defaultArray;
+ }
} else {
return defaultArray;
}
}
+ private final class SettingsObserver extends ContentObserver {
+ private final Uri ALWAYS_ON_DISPLAY_CONSTANTS_URI
+ = Settings.Global.getUriFor(Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
+
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(ALWAYS_ON_DISPLAY_CONSTANTS_URI,
+ false, this, UserHandle.USER_ALL);
+ update(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ update(uri);
+ }
+
+ public void update(Uri uri) {
+ if (uri == null || ALWAYS_ON_DISPLAY_CONSTANTS_URI.equals(uri)) {
+ final Resources resources = mContext.getResources();
+ final String value = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
+
+ try {
+ mParser.setString(value);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Bad AOD constants");
+ }
+
+ proxScreenOffDelayMs = mParser.getLong(KEY_PROX_SCREEN_OFF_DELAY_MS,
+ DEFAULT_PROX_SCREEN_OFF_DELAY_MS);
+ proxCooldownTriggerMs = mParser.getLong(KEY_PROX_COOLDOWN_TRIGGER_MS,
+ DEFAULT_PROX_COOLDOWN_TRIGGER_MS);
+ proxCooldownPeriodMs = mParser.getLong(KEY_PROX_COOLDOWN_PERIOD_MS,
+ DEFAULT_PROX_COOLDOWN_PERIOD_MS);
+ screenBrightnessArray = parseIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
+ resources.getIntArray(
+ R.array.config_doze_brightness_sensor_to_brightness));
+ dimmingScrimArray = parseIntArray(KEY_DIMMING_SCRIM_ARRAY,
+ resources.getIntArray(
+ R.array.config_doze_brightness_sensor_to_scrim_opacity));
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
index 76a1902..58f1448 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
@@ -28,20 +28,21 @@
public static final String TAG = DozePauser.class.getSimpleName();
private final AlarmTimeout mPauseTimeout;
private final DozeMachine mMachine;
- private final long mTimeoutMs;
+ private final AlwaysOnDisplayPolicy mPolicy;
public DozePauser(Handler handler, DozeMachine machine, AlarmManager alarmManager,
AlwaysOnDisplayPolicy policy) {
mMachine = machine;
mPauseTimeout = new AlarmTimeout(alarmManager, this::onTimeout, TAG, handler);
- mTimeoutMs = policy.proxScreenOffDelayMs;
+ mPolicy = policy;
}
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case DOZE_AOD_PAUSING:
- mPauseTimeout.schedule(mTimeoutMs, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ mPauseTimeout.schedule(mPolicy.proxScreenOffDelayMs,
+ AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
break;
default:
mPauseTimeout.cancel();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 03407e2..4bb4e79 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -22,6 +22,7 @@
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
+import android.os.Trace;
import com.android.internal.annotations.VisibleForTesting;
@@ -94,9 +95,14 @@
@Override
public void onSensorChanged(SensorEvent event) {
- if (mRegistered) {
- mLastSensorValue = (int) event.values[0];
- updateBrightnessAndReady();
+ Trace.beginSection("DozeScreenBrightness.onSensorChanged" + event.values[0]);
+ try {
+ if (mRegistered) {
+ mLastSensorValue = (int) event.values[0];
+ updateBrightnessAndReady();
+ }
+ } finally {
+ Trace.endSection();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 50720e9..b5c0d53 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -29,6 +29,8 @@
import android.os.SystemClock;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.LinkedList;
/**
@@ -57,8 +59,12 @@
}
}
- private LinkedList<Command> mCmdQueue = new LinkedList();
+ private final LinkedList<Command> mCmdQueue = new LinkedList<Command>();
+ private final Object mCompletionHandlingLock = new Object();
+ @GuardedBy("mCompletionHandlingLock")
+ private CreationAndCompletionThread mCompletionThread;
+ @GuardedBy("mCompletionHandlingLock")
private Looper mLooper;
/*
@@ -76,7 +82,10 @@
public void run() {
Looper.prepare();
+ // ok to modify mLooper as here we are
+ // synchronized on mCompletionHandlingLock due to the Object.wait() in startSound(cmd)
mLooper = Looper.myLooper();
+ if (DEBUG) Log.d(mTag, "in run: new looper " + mLooper);
synchronized(this) {
AudioManager audioManager =
(AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE);
@@ -97,7 +106,7 @@
if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null)
&& (mCmd.uri.getEncodedPath().length() > 0)) {
if (!audioManager.isMusicActiveRemotely()) {
- synchronized(mQueueAudioFocusLock) {
+ synchronized (mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus == null) {
if (DEBUG) Log.d(mTag, "requesting AudioFocus");
int focusGain = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
@@ -129,7 +138,9 @@
Log.e(mTag, "Exception while sleeping to sync notification playback"
+ " with ducking", e);
}
+ if (DEBUG) { Log.d(mTag, "player.start"); }
if (mPlayer != null) {
+ if (DEBUG) { Log.d(mTag, "mPlayer.release"); }
mPlayer.release();
}
mPlayer = player;
@@ -148,7 +159,7 @@
// is playing, let it continue until we're done, so there
// is less of a glitch.
try {
- if (DEBUG) Log.d(mTag, "Starting playback");
+ if (DEBUG) { Log.d(mTag, "startSound()"); }
//-----------------------------------
// This is were we deviate from the AsyncPlayer implementation and create the
// MediaPlayer in a new thread with which we're synchronized
@@ -158,10 +169,11 @@
// matters
if((mLooper != null)
&& (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
+ if (DEBUG) { Log.d(mTag, "in startSound quitting looper " + mLooper); }
mLooper.quit();
}
mCompletionThread = new CreationAndCompletionThread(cmd);
- synchronized(mCompletionThread) {
+ synchronized (mCompletionThread) {
mCompletionThread.start();
mCompletionThread.wait();
}
@@ -209,13 +221,18 @@
mPlayer = null;
synchronized(mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus != null) {
+ if (DEBUG) { Log.d(mTag, "in STOP: abandonning AudioFocus"); }
mAudioManagerWithAudioFocus.abandonAudioFocus(null);
mAudioManagerWithAudioFocus = null;
}
}
- if((mLooper != null)
- && (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
- mLooper.quit();
+ synchronized (mCompletionHandlingLock) {
+ if ((mLooper != null) &&
+ (mLooper.getThread().getState() != Thread.State.TERMINATED))
+ {
+ if (DEBUG) { Log.d(mTag, "in STOP: quitting looper "+ mLooper); }
+ mLooper.quit();
+ }
}
} else {
Log.w(mTag, "STOP command without a player");
@@ -250,9 +267,11 @@
}
// if there are no more sounds to play, end the Looper to listen for media completion
synchronized (mCmdQueue) {
- if (mCmdQueue.size() == 0) {
- synchronized(mCompletionHandlingLock) {
- if(mLooper != null) {
+ synchronized(mCompletionHandlingLock) {
+ if (DEBUG) { Log.d(mTag, "onCompletion queue size=" + mCmdQueue.size()); }
+ if ((mCmdQueue.size() == 0)) {
+ if (mLooper != null) {
+ if (DEBUG) { Log.d(mTag, "in onCompletion quitting looper " + mLooper); }
mLooper.quit();
}
mCompletionThread = null;
@@ -269,13 +288,20 @@
}
private String mTag;
+
+ @GuardedBy("mCmdQueue")
private CmdThread mThread;
- private CreationAndCompletionThread mCompletionThread;
- private final Object mCompletionHandlingLock = new Object();
+
private MediaPlayer mPlayer;
+
+
+ @GuardedBy("mCmdQueue")
private PowerManager.WakeLock mWakeLock;
+
private final Object mQueueAudioFocusLock = new Object();
- private AudioManager mAudioManagerWithAudioFocus; // synchronized on mQueueAudioFocusLock
+ @GuardedBy("mQueueAudioFocusLock")
+ private AudioManager mAudioManagerWithAudioFocus;
+
private int mNotificationRampTimeMs = 0;
// The current state according to the caller. Reality lags behind
@@ -311,6 +337,7 @@
*/
@Deprecated
public void play(Context context, Uri uri, boolean looping, int stream) {
+ if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); }
PlayerBase.deprecateStreamTypeForPlayback(stream, "NotificationPlayer", "play");
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
@@ -339,6 +366,7 @@
* (see {@link MediaPlayer#setAudioAttributes(AudioAttributes)})
*/
public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) {
+ if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); }
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
cmd.code = PLAY;
@@ -357,6 +385,7 @@
* at this point. Calling this multiple times has no ill effects.
*/
public void stop() {
+ if (DEBUG) { Log.d(mTag, "stop"); }
synchronized (mCmdQueue) {
// This check allows stop to be called multiple times without starting
// a thread that ends up doing nothing.
@@ -370,6 +399,7 @@
}
}
+ @GuardedBy("mCmdQueue")
private void enqueueLocked(Command cmd) {
mCmdQueue.add(cmd);
if (mThread == null) {
@@ -393,22 +423,26 @@
* @hide
*/
public void setUsesWakeLock(Context context) {
- if (mWakeLock != null || mThread != null) {
- // if either of these has happened, we've already played something.
- // and our releases will be out of sync.
- throw new RuntimeException("assertion failed mWakeLock=" + mWakeLock
- + " mThread=" + mThread);
+ synchronized (mCmdQueue) {
+ if (mWakeLock != null || mThread != null) {
+ // if either of these has happened, we've already played something.
+ // and our releases will be out of sync.
+ throw new RuntimeException("assertion failed mWakeLock=" + mWakeLock
+ + " mThread=" + mThread);
+ }
+ PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
}
- PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
}
+ @GuardedBy("mCmdQueue")
private void acquireWakeLock() {
if (mWakeLock != null) {
mWakeLock.acquire();
}
}
+ @GuardedBy("mCmdQueue")
private void releaseWakeLock() {
if (mWakeLock != null) {
mWakeLock.release();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index b3f992d..58243b4 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -17,6 +17,8 @@
package com.android.systemui.pip.phone;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
import android.app.ActivityManager;
@@ -30,6 +32,7 @@
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Pair;
import android.view.IPinnedStackController;
import android.view.IPinnedStackListener;
import android.view.IWindowManager;
@@ -70,11 +73,11 @@
*/
TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
- public void onActivityPinned(String packageName, int taskId) {
+ public void onActivityPinned(String packageName, int userId, int taskId) {
mTouchHandler.onActivityPinned();
mMediaController.onActivityPinned();
mMenuController.onActivityPinned();
- mNotificationController.onActivityPinned(packageName,
+ mNotificationController.onActivityPinned(packageName, userId,
true /* deferUntilAnimationEnds */);
SystemServicesProxy.getInstance(mContext).setPipVisibility(true);
@@ -82,13 +85,15 @@
@Override
public void onActivityUnpinned() {
- ComponentName topPipActivity = PipUtils.getTopPinnedActivity(mContext,
- mActivityManager);
- mMenuController.onActivityUnpinned(topPipActivity);
- mTouchHandler.onActivityUnpinned(topPipActivity);
- mNotificationController.onActivityUnpinned(topPipActivity);
+ final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPinnedActivity(
+ mContext, mActivityManager);
+ final ComponentName topActivity = topPipActivityInfo.first;
+ final int userId = topActivity != null ? topPipActivityInfo.second : 0;
+ mMenuController.onActivityUnpinned();
+ mTouchHandler.onActivityUnpinned(topActivity);
+ mNotificationController.onActivityUnpinned(topActivity, userId);
- SystemServicesProxy.getInstance(mContext).setPipVisibility(topPipActivity != null);
+ SystemServicesProxy.getInstance(mContext).setPipVisibility(topActivity != null);
}
@Override
@@ -196,7 +201,8 @@
public final void onBusEvent(ExpandPipEvent event) {
if (event.clearThumbnailWindows) {
try {
- StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ StackInfo stackInfo = mActivityManager.getStackInfo(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (stackInfo != null && stackInfo.taskIds != null) {
SystemServicesProxy ssp = SystemServicesProxy.getInstance(mContext);
for (int taskId : stackInfo.taskIds) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
index b3a0794..174a7ef 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
@@ -230,7 +230,7 @@
private void resolveActiveMediaController(List<MediaController> controllers) {
if (controllers != null) {
final ComponentName topActivity = PipUtils.getTopPinnedActivity(mContext,
- mActivityManager);
+ mActivityManager).first;
if (topActivity != null) {
for (int i = 0; i < controllers.size(); i++) {
final MediaController controller = controllers.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 34666fb..d71d4de 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -17,6 +17,8 @@
package com.android.systemui.pip.phone;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityOptions;
@@ -223,7 +225,7 @@
}
}
- public void onActivityUnpinned(ComponentName topPipActivity) {
+ public void onActivityUnpinned() {
hideMenu();
setStartActivityRequested(false);
}
@@ -383,7 +385,8 @@
private void startMenuActivity(int menuState, Rect stackBounds, Rect movementBounds,
boolean allowMenuTimeout, boolean willResizeMenu) {
try {
- StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ StackInfo pinnedStackInfo = mActivityManager.getStackInfo(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
pinnedStackInfo.taskIds.length > 0) {
Intent intent = new Intent(mContext, PipMenuActivity.class);
@@ -421,7 +424,8 @@
// Fetch the pinned stack bounds
Rect stackBounds = null;
try {
- StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ StackInfo pinnedStackInfo = mActivityManager.getStackInfo(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (pinnedStackInfo != null) {
stackBounds = pinnedStackInfo.bounds;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index cebb22f..eef0e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -18,6 +18,8 @@
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static com.android.systemui.Interpolators.FAST_OUT_LINEAR_IN;
import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.systemui.Interpolators.LINEAR_OUT_SLOW_IN;
@@ -121,7 +123,8 @@
void synchronizePinnedStackBounds() {
cancelAnimations();
try {
- StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ StackInfo stackInfo =
+ mActivityManager.getStackInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (stackInfo != null) {
mBounds.set(stackInfo.bounds);
}
@@ -529,7 +532,8 @@
Rect toBounds = (Rect) args.arg1;
int duration = args.argi1;
try {
- StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ StackInfo stackInfo = mActivityManager.getStackInfo(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (stackInfo == null) {
// In the case where we've already re-expanded or dismissed the PiP, then
// just skip the resize
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
index 696fdbc..6d083e9 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
@@ -35,10 +35,15 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.graphics.drawable.Icon;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.UserHandle;
+import android.util.IconDrawableFactory;
import android.util.Log;
+import android.util.Pair;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
@@ -57,22 +62,29 @@
private IActivityManager mActivityManager;
private AppOpsManager mAppOpsManager;
private NotificationManager mNotificationManager;
+ private IconDrawableFactory mIconDrawableFactory;
private PipMotionHelper mMotionHelper;
// Used when building a deferred notification
private String mDeferredNotificationPackageName;
+ private int mDeferredNotificationUserId;
private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() {
@Override
public void onOpChanged(String op, String packageName) {
try {
// Dismiss the PiP once the user disables the app ops setting for that package
- final ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
- packageName, 0);
- if (mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid, packageName)
- != MODE_ALLOWED) {
- mMotionHelper.dismissPip();
+ final Pair<ComponentName, Integer> topPipActivityInfo =
+ PipUtils.getTopPinnedActivity(mContext, mActivityManager);
+ if (topPipActivityInfo.first != null) {
+ final ApplicationInfo appInfo = mContext.getPackageManager()
+ .getApplicationInfoAsUser(packageName, 0, topPipActivityInfo.second);
+ if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) &&
+ mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid,
+ packageName) != MODE_ALLOWED) {
+ mMotionHelper.dismissPip();
+ }
}
} catch (NameNotFoundException e) {
// Unregister the listener if the package can't be found
@@ -88,16 +100,18 @@
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mNotificationManager = NotificationManager.from(context);
mMotionHelper = motionHelper;
+ mIconDrawableFactory = IconDrawableFactory.newInstance(context);
}
- public void onActivityPinned(String packageName, boolean deferUntilAnimationEnds) {
+ public void onActivityPinned(String packageName, int userId, boolean deferUntilAnimationEnds) {
// Clear any existing notification
mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
if (deferUntilAnimationEnds) {
mDeferredNotificationPackageName = packageName;
+ mDeferredNotificationUserId = userId;
} else {
- showNotificationForApp(mDeferredNotificationPackageName);
+ showNotificationForApp(packageName, userId);
}
// Register for changes to the app ops setting for this package while it is in PiP
@@ -106,22 +120,25 @@
public void onPinnedStackAnimationEnded() {
if (mDeferredNotificationPackageName != null) {
- showNotificationForApp(mDeferredNotificationPackageName);
+ showNotificationForApp(mDeferredNotificationPackageName, mDeferredNotificationUserId);
mDeferredNotificationPackageName = null;
+ mDeferredNotificationUserId = 0;
}
}
- public void onActivityUnpinned(ComponentName topPipActivity) {
+ public void onActivityUnpinned(ComponentName topPipActivity, int userId) {
// Unregister for changes to the previously PiP'ed package
unregisterAppOpsListener();
// Reset the deferred notification package
mDeferredNotificationPackageName = null;
+ mDeferredNotificationUserId = 0;
if (topPipActivity != null) {
// onActivityUnpinned() is only called after the transition is complete, so we don't
// need to defer until the animation ends to update the notification
- onActivityPinned(topPipActivity.getPackageName(), false /* deferUntilAnimationEnds */);
+ onActivityPinned(topPipActivity.getPackageName(), userId,
+ false /* deferUntilAnimationEnds */);
} else {
mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
}
@@ -130,20 +147,27 @@
/**
* Builds and shows the notification for the given app.
*/
- private void showNotificationForApp(String packageName) {
+ private void showNotificationForApp(String packageName, int userId) {
// Build a new notification
- final Notification.Builder builder =
- new Notification.Builder(mContext, NotificationChannels.GENERAL)
- .setLocalOnly(true)
- .setOngoing(true)
- .setSmallIcon(R.drawable.pip_notification_icon)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color));
- if (updateNotificationForApp(builder, packageName)) {
- SystemUI.overrideNotificationAppName(mContext, builder);
+ try {
+ final UserHandle user = UserHandle.of(userId);
+ final Context userContext = mContext.createPackageContextAsUser(
+ mContext.getPackageName(), 0, user);
+ final Notification.Builder builder =
+ new Notification.Builder(userContext, NotificationChannels.GENERAL)
+ .setLocalOnly(true)
+ .setOngoing(true)
+ .setSmallIcon(R.drawable.pip_notification_icon)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color.system_notification_accent_color));
+ if (updateNotificationForApp(builder, packageName, user)) {
+ SystemUI.overrideNotificationAppName(mContext, builder);
- // Show the new notification
- mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
+ // Show the new notification
+ mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
+ }
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Could not show notification for application", e);
}
}
@@ -151,33 +175,33 @@
* Updates the notification builder with app-specific information, returning whether it was
* successful.
*/
- private boolean updateNotificationForApp(Notification.Builder builder, String packageName) {
+ private boolean updateNotificationForApp(Notification.Builder builder, String packageName,
+ UserHandle user) throws NameNotFoundException {
final PackageManager pm = mContext.getPackageManager();
final ApplicationInfo appInfo;
try {
- appInfo = pm.getApplicationInfo(packageName, 0);
+ appInfo = pm.getApplicationInfoAsUser(packageName, 0, user.getIdentifier());
} catch (NameNotFoundException e) {
Log.e(TAG, "Could not update notification for application", e);
return false;
}
if (appInfo != null) {
- final String appName = pm.getApplicationLabel(appInfo).toString();
+ final String appName = pm.getUserBadgedLabel(pm.getApplicationLabel(appInfo), user)
+ .toString();
final String message = mContext.getString(R.string.pip_notification_message, appName);
final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
Uri.fromParts("package", packageName, null));
+ settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
- final Icon appIcon = appInfo.icon != 0
- ? Icon.createWithResource(packageName, appInfo.icon)
- : Icon.createWithResource(Resources.getSystem(),
- com.android.internal.R.drawable.sym_def_app_icon);
+ final Drawable iconDrawable = mIconDrawableFactory.getBadgedIcon(appInfo);
builder.setContentTitle(mContext.getString(R.string.pip_notification_title, appName))
.setContentText(message)
- .setContentIntent(PendingIntent.getActivity(mContext, packageName.hashCode(),
- settingsIntent, FLAG_CANCEL_CURRENT))
+ .setContentIntent(PendingIntent.getActivityAsUser(mContext, packageName.hashCode(),
+ settingsIntent, FLAG_CANCEL_CURRENT, null, user))
.setStyle(new Notification.BigTextStyle().bigText(message))
- .setLargeIcon(appIcon);
+ .setLargeIcon(createBitmap(iconDrawable).createAshmemBitmap());
return true;
}
return false;
@@ -191,4 +215,17 @@
private void unregisterAppOpsListener() {
mAppOpsManager.stopWatchingMode(mAppOpsChangedListener);
}
+
+ /**
+ * Bakes a drawable into a bitmap.
+ */
+ private Bitmap createBitmap(Drawable d) {
+ Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(),
+ Config.ARGB_8888);
+ Canvas c = new Canvas(bitmap);
+ d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
+ d.draw(c);
+ c.setBitmap(null);
+ return bitmap;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
index a8cdd1b..84410f2 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
@@ -17,6 +17,8 @@
package com.android.systemui.pip.phone;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import android.app.ActivityManager.StackInfo;
import android.app.IActivityManager;
@@ -24,33 +26,35 @@
import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Pair;
public class PipUtils {
private static final String TAG = "PipUtils";
/**
- * @return the ComponentName of the top non-SystemUI activity in the pinned stack, or null if
- * none exists.
+ * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
+ * The component name may be null if no such activity exists.
*/
- public static ComponentName getTopPinnedActivity(Context context,
+ public static Pair<ComponentName, Integer> getTopPinnedActivity(Context context,
IActivityManager activityManager) {
try {
final String sysUiPackageName = context.getPackageName();
- final StackInfo pinnedStackInfo = activityManager.getStackInfo(PINNED_STACK_ID);
+ final StackInfo pinnedStackInfo =
+ activityManager.getStackInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
pinnedStackInfo.taskIds.length > 0) {
for (int i = pinnedStackInfo.taskNames.length - 1; i >= 0; i--) {
ComponentName cn = ComponentName.unflattenFromString(
pinnedStackInfo.taskNames[i]);
if (cn != null && !cn.getPackageName().equals(sysUiPackageName)) {
- return cn;
+ return new Pair<>(cn, pinnedStackInfo.taskUserIds[i]);
}
}
}
} catch (RemoteException e) {
Log.w(TAG, "Unable to get pinned stack.");
}
- return null;
+ return new Pair<>(null, 0);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index e8c1295..6e85549 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -54,6 +54,8 @@
import java.util.List;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
/**
@@ -502,7 +504,8 @@
private StackInfo getPinnedStackInfo() {
StackInfo stackInfo = null;
try {
- stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ stackInfo = mActivityManager.getStackInfo(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
} catch (RemoteException e) {
Log.e(TAG, "getStackInfo failed", e);
}
@@ -654,7 +657,7 @@
}
@Override
- public void onActivityPinned(String packageName, int taskId) {
+ public void onActivityPinned(String packageName, int userId, int taskId) {
if (DEBUG) Log.d(TAG, "onActivityPinned()");
if (!checkCurrentUserId(mContext, DEBUG)) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AlphaControlledSignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/AlphaControlledSignalTileView.java
new file mode 100644
index 0000000..2c7ec70
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/AlphaControlledSignalTileView.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 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.systemui.qs;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import com.android.systemui.qs.tileimpl.SlashImageView;
+
+
+/**
+ * Creates AlphaControlledSlashImageView instead of SlashImageView
+ */
+public class AlphaControlledSignalTileView extends SignalTileView {
+ public AlphaControlledSignalTileView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected SlashImageView createSlashImageView(Context context) {
+ return new AlphaControlledSlashImageView(context);
+ }
+
+ /**
+ * Creates AlphaControlledSlashDrawable instead of regular SlashDrawables
+ */
+ public static class AlphaControlledSlashImageView extends SlashImageView {
+ public AlphaControlledSlashImageView(Context context) {
+ super(context);
+ }
+
+ public void setFinalImageTintList(ColorStateList tint) {
+ super.setImageTintList(tint);
+ final SlashDrawable slash = getSlash();
+ if (slash != null) {
+ ((AlphaControlledSlashDrawable)slash).setFinalTintList(tint);
+ }
+ }
+
+ @Override
+ protected void ensureSlashDrawable() {
+ if (getSlash() == null) {
+ final SlashDrawable slash = new AlphaControlledSlashDrawable(getDrawable());
+ setSlash(slash);
+ slash.setAnimationEnabled(getAnimationEnabled());
+ setImageViewDrawable(slash);
+ }
+ }
+ }
+
+ /**
+ * SlashDrawable that disobeys orders to change its drawable's tint except when you tell
+ * it not to disobey. The slash still will animate its alpha.
+ */
+ public static class AlphaControlledSlashDrawable extends SlashDrawable {
+ AlphaControlledSlashDrawable(Drawable d) {
+ super(d);
+ }
+
+ @Override
+ protected void setDrawableTintList(ColorStateList tint) {
+ }
+
+ /**
+ * Set a target tint list instead of
+ */
+ public void setFinalTintList(ColorStateList tint) {
+ super.setDrawableTintList(tint);
+ }
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
index b300e4a..9ee40cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
@@ -63,13 +63,17 @@
@Override
protected View createIcon() {
mIconFrame = new FrameLayout(mContext);
- mSignal = new SlashImageView(mContext);
+ mSignal = createSlashImageView(mContext);
mIconFrame.addView(mSignal);
mOverlay = new ImageView(mContext);
mIconFrame.addView(mOverlay, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
return mIconFrame;
}
+ protected SlashImageView createSlashImageView(Context context) {
+ return new SlashImageView(context);
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java b/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
index c356148..a9b2376 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
@@ -197,11 +197,15 @@
public void setTintList(@Nullable ColorStateList tint) {
mTintList = tint;
super.setTintList(tint);
- mDrawable.setTintList(tint);
+ setDrawableTintList(tint);
mPaint.setColor(tint.getDefaultColor());
invalidateSelf();
}
+ protected void setDrawableTintList(@Nullable ColorStateList tint) {
+ mDrawable.setTintList(tint);
+ }
+
@Override
public void setTintMode(@NonNull Mode tintMode) {
mTintMode = tintMode;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 8074cb9..e8c8b90 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -33,6 +33,7 @@
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
+import com.android.systemui.qs.AlphaControlledSignalTileView.AlphaControlledSlashImageView;
import java.util.Objects;
public class QSIconViewImpl extends QSIconView {
@@ -138,7 +139,12 @@
animateGrayScale(mTint, color, iv);
mTint = color;
} else {
- setTint(iv, color);
+ if (iv instanceof AlphaControlledSlashImageView) {
+ ((AlphaControlledSlashImageView)iv)
+ .setFinalImageTintList(ColorStateList.valueOf(color));
+ } else {
+ setTint(iv, color);
+ }
mTint = color;
}
}
@@ -149,6 +155,10 @@
}
public static void animateGrayScale(int fromColor, int toColor, ImageView iv) {
+ if (iv instanceof AlphaControlledSlashImageView) {
+ ((AlphaControlledSlashImageView)iv)
+ .setFinalImageTintList(ColorStateList.valueOf(toColor));
+ }
if (ValueAnimator.areAnimatorsEnabled()) {
final float fromAlpha = Color.alpha(fromColor);
final float toAlpha = Color.alpha(toColor);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
index 97e9c3d..63d6f82 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
@@ -34,7 +34,15 @@
super(context);
}
- private void ensureSlashDrawable() {
+ protected SlashDrawable getSlash() {
+ return mSlash;
+ }
+
+ protected void setSlash(SlashDrawable slash) {
+ mSlash = slash;
+ }
+
+ protected void ensureSlashDrawable() {
if (mSlash == null) {
mSlash = new SlashDrawable(getDrawable());
mSlash.setAnimationEnabled(mAnimationEnabled);
@@ -56,10 +64,18 @@
}
}
+ protected void setImageViewDrawable(SlashDrawable slash) {
+ super.setImageDrawable(slash);
+ }
+
public void setAnimationEnabled(boolean enabled) {
mAnimationEnabled = enabled;
}
+ public boolean getAnimationEnabled() {
+ return mAnimationEnabled;
+ }
+
private void setSlashState(@NonNull SlashState slashState) {
ensureSlashDrawable();
mSlash.setRotation(slashState.rotation);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 8d62f2a..81b8622 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -23,6 +23,7 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
+import android.graphics.drawable.Drawable;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
@@ -34,10 +35,8 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.R.drawable;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -135,11 +134,7 @@
if (lastDevice != null) {
int batteryLevel = lastDevice.getBatteryLevel();
if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
- BluetoothDeviceLayerDrawable drawable = createLayerDrawable(mContext,
- R.drawable.ic_qs_bluetooth_connected, batteryLevel,
- mContext.getResources().getFraction(
- R.fraction.bt_battery_scale_fraction, 1, 1));
- state.icon = new DrawableIcon(drawable);
+ state.icon = new BluetoothBatteryDrawable(batteryLevel);
}
}
state.contentDescription = mContext.getString(
@@ -215,6 +210,22 @@
return new BluetoothDetailAdapter();
}
+ private class BluetoothBatteryDrawable extends Icon {
+ private int mLevel;
+
+ BluetoothBatteryDrawable(int level) {
+ mLevel = level;
+ }
+
+ @Override
+ public Drawable getDrawable(Context context) {
+ return createLayerDrawable(context,
+ R.drawable.ic_qs_bluetooth_connected, mLevel,
+ context.getResources().getFraction(
+ R.fraction.bt_battery_scale_fraction, 1, 1));
+ }
+ }
+
protected class BluetoothDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
// We probably won't ever have space in the UI for more than 20 devices, so don't
// get info for them.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 2e389ba..0ce3e6a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -266,7 +266,7 @@
}
@Override
- public void setNoSims(boolean show) {
+ public void setNoSims(boolean show, boolean simDetected) {
mInfo.noSim = show;
if (mInfo.noSim) {
// Make sure signal gets cleared out when no sims.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 33b1512..2370273 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -37,10 +37,10 @@
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState;
+import com.android.systemui.qs.AlphaControlledSignalTileView;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.SignalTileView;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
@@ -104,7 +104,7 @@
@Override
public QSIconView createTileView(Context context) {
- return new SignalTileView(context);
+ return new AlphaControlledSignalTileView(context);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 406bcac..283ac0c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -16,6 +16,9 @@
package com.android.systemui.recents;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
import android.app.ActivityManager;
@@ -437,9 +440,12 @@
int currentUser = sSystemServicesProxy.getCurrentUser();
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
+ final int activityType = runningTask != null
+ ? runningTask.configuration.windowConfiguration.getActivityType()
+ : ACTIVITY_TYPE_UNDEFINED;
boolean screenPinningActive = ssp.isScreenPinningActive();
- boolean isRunningTaskInHomeOrRecentsStack = runningTask != null &&
- ActivityManager.StackId.isHomeOrRecentsStack(runningTask.stackId);
+ boolean isRunningTaskInHomeOrRecentsStack =
+ activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode);
if (runningTask.supportsSplitScreenMultiWindow) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index f545556..86b7790 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -358,6 +358,9 @@
mScrimViews = new SystemBarScrimViews(this);
getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
+ if (Recents.getConfiguration().isLowRamDevice) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
mLastConfig = new Configuration(Utilities.getAppConfiguration(this));
mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index c4e8701..dc83333 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -17,8 +17,8 @@
package com.android.systemui.recents;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.isHomeOrRecentsStack;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.view.View.MeasureSpec;
import android.app.ActivityManager;
@@ -173,7 +173,7 @@
}
@Override
- public void onActivityPinned(String packageName, int taskId) {
+ public void onActivityPinned(String packageName, int userId, int taskId) {
// Check this is for the right user
if (!checkCurrentUserId(mContext, false /* debug */)) {
return;
@@ -533,7 +533,9 @@
if (runningTask == null) return;
// Find the task in the recents list
- boolean isRunningTaskInHomeStack = SystemServicesProxy.isHomeStack(runningTask.stackId);
+ boolean isRunningTaskInHomeStack =
+ runningTask.configuration.windowConfiguration.getActivityType()
+ == ACTIVITY_TYPE_HOME;
ArrayList<Task> tasks = focusedStack.getStackTasks();
Task toTask = null;
ActivityOptions launchOpts = null;
@@ -583,9 +585,10 @@
// Return early if there is no running task (can't determine affiliated tasks in this case)
ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
+ final int activityType = runningTask.configuration.windowConfiguration.getActivityType();
if (runningTask == null) return;
// Return early if the running task is in the home/recents stack (optimization)
- if (isHomeOrRecentsStack(runningTask.stackId)) return;
+ if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) return;
// Find the task in the recents list
ArrayList<Task> tasks = focusedStack.getStackTasks();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
index e02fb14..e4a4f59 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -22,5 +22,15 @@
* This is sent when the stack action button should be hidden.
*/
public class HideStackActionButtonEvent extends EventBus.Event {
- // Simple event
+
+ // Whether or not to translate the stack action button when hiding it
+ public final boolean translate;
+
+ public HideStackActionButtonEvent() {
+ this(true);
+ }
+
+ public HideStackActionButtonEvent(boolean translate) {
+ this.translate = translate;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 366a908..4580b1f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -18,12 +18,12 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -38,6 +38,7 @@
import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.KeyguardManager;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -176,7 +177,7 @@
public void onTaskStackChangedBackground() { }
public void onTaskStackChanged() { }
public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
- public void onActivityPinned(String packageName, int taskId) { }
+ public void onActivityPinned(String packageName, int userId, int taskId) { }
public void onActivityUnpinned() { }
public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
public void onPinnedStackAnimationStarted() { }
@@ -231,9 +232,10 @@
}
@Override
- public void onActivityPinned(String packageName, int taskId) throws RemoteException {
+ public void onActivityPinned(String packageName, int userId, int taskId)
+ throws RemoteException {
mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
- mHandler.obtainMessage(H.ON_ACTIVITY_PINNED, taskId, 0, packageName).sendToTarget();
+ mHandler.obtainMessage(H.ON_ACTIVITY_PINNED, userId, taskId, packageName).sendToTarget();
}
@Override
@@ -485,16 +487,22 @@
public ActivityManager.RunningTaskInfo getRunningTask() {
// Note: The set of running tasks from the system is ordered by recency
List<ActivityManager.RunningTaskInfo> tasks = mAm.getRunningTasks(10);
- if (tasks != null && !tasks.isEmpty()) {
- // Find the first task in a valid stack, we ignore everything from the Recents and PiP
- // stacks
- for (int i = 0; i < tasks.size(); i++) {
- ActivityManager.RunningTaskInfo task = tasks.get(i);
- int stackId = task.stackId;
- if (stackId != RECENTS_STACK_ID && stackId != PINNED_STACK_ID) {
- return task;
- }
+ if (tasks == null || tasks.isEmpty()) {
+ return null;
+ }
+
+ // Find the first task in a valid stack, we ignore everything from the Recents and PiP
+ // stacks
+ for (int i = 0; i < tasks.size(); i++) {
+ final ActivityManager.RunningTaskInfo task = tasks.get(i);
+ final WindowConfiguration winConfig = task.configuration.windowConfiguration;
+ if (winConfig.getActivityType() == ACTIVITY_TYPE_RECENTS) {
+ continue;
}
+ if (winConfig.getWindowingMode() == WINDOWING_MODE_PINNED) {
+ continue;
+ }
+ return task;
}
return null;
}
@@ -521,12 +529,17 @@
ActivityManager.StackInfo fullscreenStackInfo = null;
ActivityManager.StackInfo recentsStackInfo = null;
for (int i = 0; i < stackInfos.size(); i++) {
- StackInfo stackInfo = stackInfos.get(i);
- if (stackInfo.stackId == HOME_STACK_ID) {
+ final StackInfo stackInfo = stackInfos.get(i);
+ final WindowConfiguration winConfig = stackInfo.configuration.windowConfiguration;
+ final int activityType = winConfig.getActivityType();
+ final int windowingMode = winConfig.getWindowingMode();
+ if (activityType == ACTIVITY_TYPE_HOME) {
homeStackInfo = stackInfo;
- } else if (stackInfo.stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ } else if (activityType == ACTIVITY_TYPE_STANDARD
+ && (windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
fullscreenStackInfo = stackInfo;
- } else if (stackInfo.stackId == RECENTS_STACK_ID) {
+ } else if (activityType == ACTIVITY_TYPE_RECENTS) {
recentsStackInfo = stackInfo;
}
}
@@ -605,27 +618,6 @@
}
/**
- * Returns whether the given stack id is the home stack id.
- */
- public static boolean isHomeStack(int stackId) {
- return stackId == HOME_STACK_ID;
- }
-
- /**
- * Returns whether the given stack id is the pinned stack id.
- */
- public static boolean isPinnedStack(int stackId){
- return stackId == PINNED_STACK_ID;
- }
-
- /**
- * Returns whether the given stack id is the docked stack id.
- */
- public static boolean isDockedStack(int stackId) {
- return stackId == DOCKED_STACK_ID;
- }
-
- /**
* Returns whether the given stack id is the freeform workspace stack id.
*/
public static boolean isFreeformStack(int stackId) {
@@ -640,7 +632,8 @@
ActivityManager.StackInfo stackInfo = null;
try {
- stackInfo = mIam.getStackInfo(DOCKED_STACK_ID);
+ stackInfo =
+ mIam.getStackInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -1105,9 +1098,10 @@
try {
// Use the recents stack bounds, fallback to fullscreen stack if it is null
- ActivityManager.StackInfo stackInfo = mIam.getStackInfo(RECENTS_STACK_ID);
+ ActivityManager.StackInfo stackInfo =
+ mIam.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
if (stackInfo == null) {
- stackInfo = mIam.getStackInfo(FULLSCREEN_WORKSPACE_STACK_ID);
+ stackInfo = mIam.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
}
if (stackInfo != null) {
windowRect.set(stackInfo.bounds);
@@ -1356,7 +1350,8 @@
}
case ON_ACTIVITY_PINNED: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onActivityPinned((String) msg.obj, msg.arg1);
+ mTaskStackListeners.get(i).onActivityPinned((String) msg.obj, msg.arg1,
+ msg.arg2);
}
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 4d33216..ee05d81 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -16,11 +16,6 @@
package com.android.systemui.recents.views;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -32,7 +27,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.annotation.Nullable;
-import android.app.ActivityManager.StackId;
import android.app.ActivityOptions;
import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index fd1b806..c7edb9a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -174,8 +174,6 @@
? R.layout.recents_low_ram_stack_action_button
: R.layout.recents_stack_action_button,
this, false);
- mStackActionButton.setOnClickListener(
- v -> EventBus.getDefault().send(new DismissAllTaskViewsEvent()));
mStackButtonShadowRadius = mStackActionButton.getShadowRadius();
mStackButtonShadowDistance = new PointF(mStackActionButton.getShadowDx(),
@@ -205,10 +203,6 @@
mStackButtonShadowDistance.x, mStackButtonShadowDistance.y,
mStackButtonShadowColor);
}
- if (Recents.getConfiguration().isLowRamDevice) {
- int bgColor = Utils.getColorAttr(mContext, R.attr.clearAllBackgroundColor);
- mStackActionButton.setBackgroundColor(bgColor);
- }
}
// Let's also require dark status and nav bars if the text is dark
@@ -553,7 +547,7 @@
event.taskView, event.screenPinningRequested, event.targetWindowingMode,
event.targetActivityType);
if (Recents.getConfiguration().isLowRamDevice) {
- hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, false /* translate */);
+ EventBus.getDefault().send(new HideStackActionButtonEvent(false /* translate */));
}
}
@@ -561,7 +555,7 @@
int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
if (RecentsDebugFlags.Static.EnableStackActionButton) {
// Hide the stack action button
- hideStackActionButton(taskViewExitToHomeDuration, false /* translate */);
+ EventBus.getDefault().send(new HideStackActionButtonEvent());
}
animateBackgroundScrim(0f, taskViewExitToHomeDuration);
@@ -722,13 +716,10 @@
animateBackgroundScrim(getOpaqueScrimAlpha(),
TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
}
- if (Recents.getConfiguration().isLowRamDevice && mEmptyView.getVisibility() != View.VISIBLE) {
- showStackActionButton(SHOW_STACK_ACTION_BUTTON_DURATION, false /* translate */);
- }
}
public final void onBusEvent(AllTaskViewsDismissedEvent event) {
- hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
+ EventBus.getDefault().send(new HideStackActionButtonEvent());
}
public final void onBusEvent(DismissAllTaskViewsEvent event) {
@@ -776,7 +767,8 @@
mStackActionButton.setVisibility(View.VISIBLE);
mStackActionButton.setAlpha(0f);
if (translate) {
- mStackActionButton.setTranslationY(-mStackActionButton.getMeasuredHeight() * 0.25f);
+ mStackActionButton.setTranslationY(mStackActionButton.getMeasuredHeight() *
+ (Recents.getConfiguration().isLowRamDevice ? 1 : -0.25f));
} else {
mStackActionButton.setTranslationY(0f);
}
@@ -822,8 +814,8 @@
if (mStackActionButton.getVisibility() == View.VISIBLE) {
if (translate) {
- mStackActionButton.animate()
- .translationY(-mStackActionButton.getMeasuredHeight() * 0.25f);
+ mStackActionButton.animate().translationY(mStackActionButton.getMeasuredHeight()
+ * (Recents.getConfiguration().isLowRamDevice ? 1 : -0.25f));
}
mStackActionButton.animate()
.alpha(0f)
@@ -935,7 +927,7 @@
/**
* @return the bounds of the stack action button.
*/
- private Rect getStackActionButtonBoundsFromStackLayout() {
+ Rect getStackActionButtonBoundsFromStackLayout() {
Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect());
int left, top;
if (Recents.getConfiguration().isLowRamDevice) {
@@ -957,6 +949,16 @@
return actionButtonRect;
}
+ View getStackActionButton() {
+ return mStackActionButton;
+ }
+
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ mTouchHandler.cancelStackActionButtonClick();
+ }
+
public void dump(String prefix, PrintWriter writer) {
String innerPrefix = prefix + " ";
String id = Integer.toHexString(System.identityHashCode(this));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 46619c2..b6b24bc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -23,6 +23,7 @@
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.PointerIcon;
+import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
@@ -31,6 +32,8 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
+import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
@@ -99,8 +102,7 @@
/** Touch preprocessing for handling below */
public boolean onInterceptTouchEvent(MotionEvent ev) {
- handleTouchEvent(ev);
- return mDragRequested;
+ return handleTouchEvent(ev) || mDragRequested;
}
/** Handles touch events once we have intercepted them */
@@ -183,22 +185,47 @@
}
}
+ void cancelStackActionButtonClick() {
+ mRv.getStackActionButton().setPressed(false);
+ }
+
+ private boolean isWithinStackActionButton(float x, float y) {
+ Rect rect = mRv.getStackActionButtonBoundsFromStackLayout();
+ return mRv.getStackActionButton().getVisibility() == View.VISIBLE &&
+ mRv.getStackActionButton().pointInView(x - rect.left, y - rect.top, 0 /* slop */);
+ }
+
+ private void changeStackActionButtonDrawableHotspot(float x, float y) {
+ Rect rect = mRv.getStackActionButtonBoundsFromStackLayout();
+ mRv.getStackActionButton().drawableHotspotChanged(x - rect.left, y - rect.top);
+ }
+
/**
* Handles dragging touch events
*/
- private void handleTouchEvent(MotionEvent ev) {
+ private boolean handleTouchEvent(MotionEvent ev) {
int action = ev.getActionMasked();
+ boolean consumed = false;
+ float evX = ev.getX();
+ float evY = ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
- mDownPos.set((int) ev.getX(), (int) ev.getY());
+ mDownPos.set((int) evX, (int) evY);
mDeviceId = ev.getDeviceId();
+
+ if (isWithinStackActionButton(evX, evY)) {
+ changeStackActionButtonDrawableHotspot(evX, evY);
+ mRv.getStackActionButton().setPressed(true);
+ }
break;
case MotionEvent.ACTION_MOVE: {
- float evX = ev.getX();
- float evY = ev.getY();
float x = evX - mTaskViewOffset.x;
float y = evY - mTaskViewOffset.y;
+ if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {
+ changeStackActionButtonDrawableHotspot(evX, evY);
+ }
+
if (mDragRequested) {
if (!mIsDragging) {
mIsDragging = Math.hypot(evX - mDownPos.x, evY - mDownPos.y) > mDragSlop;
@@ -232,9 +259,7 @@
EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask,
currentDropTarget));
}
-
}
-
mTaskView.setTranslationX(x);
mTaskView.setTranslationY(y);
}
@@ -242,6 +267,11 @@
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
+ if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {
+ EventBus.getDefault().send(new DismissAllTaskViewsEvent());
+ consumed = true;
+ }
+ cancelStackActionButtonClick();
if (mDragRequested) {
boolean cancelled = action == MotionEvent.ACTION_CANCEL;
if (cancelled) {
@@ -254,5 +284,6 @@
mDeviceId = -1;
}
}
+ return consumed;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 74e9ef2..7ede0c5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1768,8 +1768,17 @@
}
// In grid layout, the stack action button always remains visible.
- if (mEnterAnimationComplete && !useGridLayout() &&
- !Recents.getConfiguration().isLowRamDevice) {
+ if (mEnterAnimationComplete && !useGridLayout()) {
+ if (Recents.getConfiguration().isLowRamDevice) {
+ // Show stack button when user drags down to show older tasks on low ram devices
+ if (mStack.getTaskCount() > 0 && !mStackActionButtonVisible
+ && mTouchHandler.mIsScrolling && curScroll - prevScroll < 0) {
+ // Going up
+ EventBus.getDefault().send(
+ new ShowStackActionButtonEvent(true /* translate */));
+ }
+ return;
+ }
if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
mStack.getTaskCount() > 0) {
@@ -1956,6 +1965,9 @@
// Remove the task from the stack
mStack.removeTask(event.task, event.animation, false /* fromDockGesture */);
EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
+ if (mStack.getTaskCount() > 0 && Recents.getConfiguration().isLowRamDevice) {
+ EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
+ }
MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS,
event.task.key.getComponent().toString());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
index bcf4f17..2d7cfb1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
@@ -26,8 +26,8 @@
public class GridTaskViewThumbnail extends TaskViewThumbnail {
- private Path mThumbnailOutline;
- private Path mRestBackgroundOutline;
+ private final Path mThumbnailOutline = new Path();
+ private final Path mRestBackgroundOutline = new Path();
// True if either this view's size or thumbnail scale has changed and mThumbnailOutline should
// be updated.
private boolean mUpdateThumbnailOutline = true;
@@ -77,47 +77,38 @@
(int) (mThumbnailRect.width() * mThumbnailScale));
final int thumbnailHeight = Math.min(viewHeight,
(int) (mThumbnailRect.height() * mThumbnailScale));
- // Draw the thumbnail, we only round the bottom corners:
- //
- // outerLeft outerRight
- // <-----------------------> mRestBackgroundOutline
- // _________________________ (thumbnailWidth < viewWidth)
- // |_______________________| outerTop A ____ B
- // | | ↑ | |
- // | | | | |
- // | | | | |
- // | | | | | C
- // \_______________________/ ↓ |__/
- // mCornerRadius outerBottom E D
- //
- // mRestBackgroundOutline (thumbnailHeight < viewHeight)
- // A _________________________ B
- // | | C
- // F \_______________________/
- // E D
- final int outerLeft = 0;
- final int outerTop = 0;
- final int outerRight = outerLeft + thumbnailWidth;
- final int outerBottom = outerTop + thumbnailHeight;
- mThumbnailOutline = new Path();
- mThumbnailOutline.moveTo(outerLeft, outerTop);
- mThumbnailOutline.lineTo(outerRight, outerTop);
- mThumbnailOutline.lineTo(outerRight, outerBottom - mCornerRadius);
- mThumbnailOutline.arcTo(outerRight - 2 * mCornerRadius, outerBottom - 2 * mCornerRadius,
- outerRight, outerBottom, 0, 90, false);
- mThumbnailOutline.lineTo(outerLeft + mCornerRadius, outerBottom);
- mThumbnailOutline.arcTo(outerLeft, outerBottom - 2 * mCornerRadius,
- outerLeft + 2 * mCornerRadius, outerBottom, 90, 90, false);
- mThumbnailOutline.lineTo(outerLeft, outerTop);
- mThumbnailOutline.close();
if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
+ // Draw the thumbnail, we only round the bottom corners:
+ //
+ // outerLeft outerRight
+ // <-----------------------> mRestBackgroundOutline
+ // _________________________ (thumbnailWidth < viewWidth)
+ // |_______________________| outerTop A ____ B
+ // | | ↑ | |
+ // | | | | |
+ // | | | | |
+ // | | | | | C
+ // \_______________________/ ↓ |__/
+ // mCornerRadius outerBottom E D
+ //
+ // mRestBackgroundOutline (thumbnailHeight < viewHeight)
+ // A _________________________ B
+ // | | C
+ // F \_______________________/
+ // E D
+ final int outerLeft = 0;
+ final int outerTop = 0;
+ final int outerRight = outerLeft + thumbnailWidth;
+ final int outerBottom = outerTop + thumbnailHeight;
+ createThumbnailPath(outerLeft, outerTop, outerRight, outerBottom, mThumbnailOutline);
+
if (thumbnailWidth < viewWidth) {
final int l = Math.max(0, outerRight - mCornerRadius);
final int r = outerRight;
final int t = outerTop;
final int b = outerBottom;
- mRestBackgroundOutline = new Path();
+ mRestBackgroundOutline.reset();
mRestBackgroundOutline.moveTo(l, t); // A
mRestBackgroundOutline.lineTo(r, t); // B
mRestBackgroundOutline.lineTo(r, b - mCornerRadius); // C
@@ -133,7 +124,7 @@
final int r = outerRight;
final int t = Math.max(0, thumbnailHeight - mCornerRadius);
final int b = outerBottom;
- mRestBackgroundOutline = new Path();
+ mRestBackgroundOutline.reset();
mRestBackgroundOutline.moveTo(l, t); // A
mRestBackgroundOutline.lineTo(r, t); // B
mRestBackgroundOutline.lineTo(r, b - mCornerRadius); // C
@@ -145,9 +136,26 @@
mRestBackgroundOutline.lineTo(l, t); // A
mRestBackgroundOutline.close();
}
+ } else {
+ createThumbnailPath(0, 0, viewWidth, viewHeight, mThumbnailOutline);
}
}
+ private void createThumbnailPath(int outerLeft, int outerTop, int outerRight, int outerBottom,
+ Path outPath) {
+ outPath.reset();
+ outPath.moveTo(outerLeft, outerTop);
+ outPath.lineTo(outerRight, outerTop);
+ outPath.lineTo(outerRight, outerBottom - mCornerRadius);
+ outPath.arcTo(outerRight - 2 * mCornerRadius, outerBottom - 2 * mCornerRadius, outerRight,
+ outerBottom, 0, 90, false);
+ outPath.lineTo(outerLeft + mCornerRadius, outerBottom);
+ outPath.arcTo(outerLeft, outerBottom - 2 * mCornerRadius, outerLeft + 2 * mCornerRadius,
+ outerBottom, 90, 90, false);
+ outPath.lineTo(outerLeft, outerTop);
+ outPath.close();
+ }
+
@Override
protected void onDraw(Canvas canvas) {
final int titleHeight = getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 44e60ee..7bcef57 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -16,6 +16,9 @@
package com.android.systemui.stackdivider;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
@@ -23,7 +26,6 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
-import android.app.ActivityManager.StackId;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -36,8 +38,6 @@
import android.view.Choreographer;
import android.view.Display;
import android.view.DisplayInfo;
-import android.view.GestureDetector;
-import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.VelocityTracker;
@@ -62,8 +62,8 @@
import com.android.internal.view.SurfaceFlingerVsyncChoreographer;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
import com.android.systemui.recents.events.activity.UndockingTaskEvent;
@@ -661,7 +661,7 @@
} else {
mWindowManagerProxy.maximizeDockedStack();
}
- mWindowManagerProxy.setResizeDimLayer(false, -1, 0f);
+ mWindowManagerProxy.setResizeDimLayer(false, WINDOWING_MODE_UNDEFINED, 0f);
}
private void liftBackground() {
@@ -997,8 +997,7 @@
SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position);
float dimFraction = getDimFraction(position, closestDismissTarget);
mWindowManagerProxy.setResizeDimLayer(dimFraction != 0f,
- getStackIdForDismissTarget(closestDismissTarget),
- dimFraction);
+ getWindowingModeForDismissTarget(closestDismissTarget), dimFraction);
}
private void applyExitAnimationParallax(Rect taskRect, int position) {
@@ -1132,13 +1131,13 @@
}
}
- private int getStackIdForDismissTarget(SnapTarget dismissTarget) {
+ private int getWindowingModeForDismissTarget(SnapTarget dismissTarget) {
if ((dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide))
|| (dismissTarget.flag == SnapTarget.FLAG_DISMISS_END
&& dockSideBottomRight(mDockSide))) {
- return StackId.DOCKED_STACK_ID;
+ return WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
} else {
- return StackId.RECENTS_STACK_ID;
+ return WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
}
}
@@ -1192,6 +1191,10 @@
}
}
+ public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
+ saveSnapTargetBeforeMinimized(mSnapAlgorithm.getMiddleTarget());
+ }
+
public final void onBusEvent(DockedTopTaskEvent event) {
if (event.dragMode == NavigationBarGestureHelper.DRAG_MODE_NONE) {
mState.growAfterRecentsDrawn = false;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 185f6e3..f7c0411 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -56,7 +56,7 @@
private final Rect mTouchableRegion = new Rect();
private boolean mDimLayerVisible;
- private int mDimLayerTargetStack;
+ private int mDimLayerTargetWindowingMode;
private float mDimLayerAlpha;
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
@@ -113,7 +113,7 @@
public void run() {
try {
WindowManagerGlobal.getWindowManagerService().setResizeDimLayer(mDimLayerVisible,
- mDimLayerTargetStack, mDimLayerAlpha);
+ mDimLayerTargetWindowingMode, mDimLayerAlpha);
} catch (RemoteException e) {
Log.w(TAG, "Failed to resize stack: " + e);
}
@@ -200,9 +200,9 @@
return DOCKED_INVALID;
}
- public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
+ public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
mDimLayerVisible = visible;
- mDimLayerTargetStack = targetStackId;
+ mDimLayerTargetWindowingMode = targetWindowingMode;
mDimLayerAlpha = alpha;
mExecutor.execute(mDimLayerRunnable);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 8fa904e..966e789 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -64,10 +64,10 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.statusbar.NotificationGuts.GutsContent;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
+import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.HybridNotificationView;
import com.android.systemui.statusbar.notification.NotificationInflater;
import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.notification.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -436,9 +436,6 @@
} else {
minHeight = mNotificationMinHeight;
}
- NotificationViewWrapper collapsedWrapper = layout.getVisibleWrapper(
- NotificationContentView.VISIBLE_TYPE_CONTRACTED);
- minHeight += collapsedWrapper.getMinHeightIncrease(mUseIncreasedCollapsedHeight);
boolean headsUpCustom = layout.getHeadsUpChild() != null &&
layout.getHeadsUpChild().getId()
!= com.android.internal.R.id.status_bar_latest_event_content;
@@ -450,11 +447,6 @@
} else {
headsUpheight = mMaxHeadsUpHeight;
}
- NotificationViewWrapper headsUpWrapper = layout.getVisibleWrapper(
- NotificationContentView.VISIBLE_TYPE_HEADSUP);
- if (headsUpWrapper != null) {
- headsUpheight += headsUpWrapper.getMinHeightIncrease(mUseIncreasedCollapsedHeight);
- }
layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight,
mNotificationAmbientHeight);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index d370a63..2d16d22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -433,24 +433,27 @@
// Assist.
final AssistUtils assistUtils = new AssistUtils(mContext);
final ComponentName assistComponent = assistUtils.getAssistComponentForUser(userId);
- PackageInfo assistPackageInfo = null;
- try {
- assistPackageInfo = mPackageManager.getPackageInfo(
- assistComponent.getPackageName(), 0, userId);
- } catch (RemoteException e) {
- Log.e(TAG, "PackageManagerService is dead");
- }
+ // Not all devices have an assist component.
+ if (assistComponent != null) {
+ PackageInfo assistPackageInfo = null;
+ try {
+ assistPackageInfo = mPackageManager.getPackageInfo(
+ assistComponent.getPackageName(), 0, userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "PackageManagerService is dead");
+ }
- if (assistPackageInfo != null) {
- final Icon assistIcon = Icon.createWithResource(
- assistPackageInfo.applicationInfo.packageName,
- assistPackageInfo.applicationInfo.icon);
+ if (assistPackageInfo != null) {
+ final Icon assistIcon = Icon.createWithResource(
+ assistPackageInfo.applicationInfo.packageName,
+ assistPackageInfo.applicationInfo.icon);
- keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
- mContext.getString(R.string.keyboard_shortcut_group_applications_assist),
- assistIcon,
- KeyEvent.KEYCODE_UNKNOWN,
- KeyEvent.META_META_ON));
+ keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
+ mContext.getString(R.string.keyboard_shortcut_group_applications_assist),
+ assistIcon,
+ KeyEvent.KEYCODE_UNKNOWN,
+ KeyEvent.META_META_ON));
+ }
}
// Browser.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
index c45ca54..492ab44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
@@ -16,8 +16,13 @@
*/
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.TimeUnit;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
@@ -28,12 +33,15 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Typeface;
+import android.metrics.LogMaker;
import android.os.Bundle;
+import android.provider.Settings;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.text.SpannableString;
import android.text.style.StyleSpan;
import android.util.AttributeSet;
+import android.util.KeyValueListParser;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -51,11 +59,23 @@
public class NotificationSnooze extends LinearLayout
implements NotificationGuts.GutsContent, View.OnClickListener {
+ private static final String TAG = "NotificationSnooze";
/**
* If this changes more number increases, more assistant action resId's should be defined for
* accessibility purposes, see {@link #setSnoozeOptions(List)}
*/
private static final int MAX_ASSISTANT_SUGGESTIONS = 1;
+ private static final String KEY_DEFAULT_SNOOZE = "default";
+ private static final String KEY_OPTIONS = "options_array";
+ private static final LogMaker OPTIONS_OPEN_LOG =
+ new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS)
+ .setType(MetricsEvent.TYPE_OPEN);
+ private static final LogMaker OPTIONS_CLOSE_LOG =
+ new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS)
+ .setType(MetricsEvent.TYPE_CLOSE);
+ private static final LogMaker UNDO_LOG =
+ new LogMaker(MetricsEvent.NOTIFICATION_UNDO_SNOOZE)
+ .setType(MetricsEvent.TYPE_ACTION);
private NotificationGuts mGutsContainer;
private NotificationSwipeActionHelper mSnoozeListener;
private StatusBarNotification mSbn;
@@ -72,9 +92,31 @@
private boolean mSnoozing;
private boolean mExpanded;
private AnimatorSet mExpandAnimation;
+ private KeyValueListParser mParser;
+
+ private final static int[] sAccessibilityActions = {
+ R.id.action_snooze_shorter,
+ R.id.action_snooze_short,
+ R.id.action_snooze_long,
+ R.id.action_snooze_longer,
+ };
+
+ private MetricsLogger mMetricsLogger = new MetricsLogger();
public NotificationSnooze(Context context, AttributeSet attrs) {
super(context, attrs);
+ mParser = new KeyValueListParser(',');
+ }
+
+ @VisibleForTesting
+ SnoozeOption getDefaultOption()
+ {
+ return mDefaultOption;
+ }
+
+ @VisibleForTesting
+ void setKeyValueListParser(KeyValueListParser parser) {
+ mParser = parser;
}
@Override
@@ -96,7 +138,13 @@
mSnoozeOptions = getDefaultSnoozeOptions();
createOptionViews();
- setSelected(mDefaultOption);
+ setSelected(mDefaultOption, false);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ logOptionSelection(MetricsEvent.NOTIFICATION_SNOOZE_CLICKED, mDefaultOption);
}
@Override
@@ -136,7 +184,7 @@
SnoozeOption so = mSnoozeOptions.get(i);
if (so.getAccessibilityAction() != null
&& so.getAccessibilityAction().getId() == action) {
- setSelected(so);
+ setSelected(so, true);
return true;
}
}
@@ -172,17 +220,49 @@
mSbn = sbn;
}
- private ArrayList<SnoozeOption> getDefaultSnoozeOptions() {
+ @VisibleForTesting
+ ArrayList<SnoozeOption> getDefaultSnoozeOptions() {
+ final Resources resources = getContext().getResources();
ArrayList<SnoozeOption> options = new ArrayList<>();
+ try {
+ final String config = Settings.Global.getString(getContext().getContentResolver(),
+ Settings.Global.NOTIFICATION_SNOOZE_OPTIONS);
+ mParser.setString(config);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Bad snooze constants");
+ }
- options.add(createOption(15 /* minutes */, R.id.action_snooze_15_min));
- options.add(createOption(30 /* minutes */, R.id.action_snooze_30_min));
- mDefaultOption = createOption(60 /* minutes */, R.id.action_snooze_1_hour);
- options.add(mDefaultOption);
- options.add(createOption(60 * 2 /* minutes */, R.id.action_snooze_2_hours));
+ final int defaultSnooze = mParser.getInt(KEY_DEFAULT_SNOOZE,
+ resources.getInteger(R.integer.config_notification_snooze_time_default));
+ final int[] snoozeTimes = parseIntArray(KEY_OPTIONS,
+ resources.getIntArray(R.array.config_notification_snooze_times));
+
+ for (int i = 0; i < snoozeTimes.length && i < sAccessibilityActions.length; i++) {
+ int snoozeTime = snoozeTimes[i];
+ SnoozeOption option = createOption(snoozeTime, sAccessibilityActions[i]);
+ if (i == 0 || snoozeTime == defaultSnooze) {
+ mDefaultOption = option;
+ }
+ options.add(option);
+ }
return options;
}
+ @VisibleForTesting
+ int[] parseIntArray(final String key, final int[] defaultArray) {
+ final String value = mParser.getString(key, null);
+ if (value != null) {
+ try {
+ return Arrays.stream(value.split(":")).map(String::trim).mapToInt(
+ Integer::parseInt).toArray();
+ } catch (NumberFormatException e) {
+ return defaultArray;
+ }
+ } else {
+ return defaultArray;
+ }
+ }
+
private SnoozeOption createOption(int minutes, int accessibilityActionId) {
Resources res = getResources();
boolean showInHours = minutes >= 60;
@@ -268,12 +348,24 @@
mExpandAnimation.start();
}
- private void setSelected(SnoozeOption option) {
+ private void setSelected(SnoozeOption option, boolean userAction) {
mSelectedOption = option;
mSelectedOptionText.setText(option.getConfirmation());
showSnoozeOptions(false);
hideSelectedOption();
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ if (userAction) {
+ logOptionSelection(MetricsEvent.NOTIFICATION_SELECT_SNOOZE, option);
+ }
+ }
+
+ private void logOptionSelection(int category, SnoozeOption option) {
+ int index = mSnoozeOptions.indexOf(option);
+ long duration = TimeUnit.MINUTES.toMillis(option.getMinutesToSnoozeFor());
+ mMetricsLogger.write(new LogMaker(category)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_INDEX, index)
+ .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS, duration));
}
@Override
@@ -284,13 +376,15 @@
final int id = v.getId();
final SnoozeOption tag = (SnoozeOption) v.getTag();
if (tag != null) {
- setSelected(tag);
+ setSelected(tag, true);
} else if (id == R.id.notification_snooze) {
// Toggle snooze options
showSnoozeOptions(!mExpanded);
+ mMetricsLogger.write(!mExpanded ? OPTIONS_OPEN_LOG : OPTIONS_CLOSE_LOG);
} else {
// Undo snooze was selected
undoSnooze(v);
+ mMetricsLogger.write(UNDO_LOG);
}
}
@@ -321,7 +415,7 @@
@Override
public View getContentView() {
// Reset the view before use
- setSelected(mDefaultOption);
+ setSelected(mDefaultOption, false);
return this;
}
@@ -343,7 +437,7 @@
return true;
} else {
// The view should actually be closed
- setSelected(mSnoozeOptions.get(0));
+ setSelected(mSnoozeOptions.get(0), false);
return false; // Return false here so that guts handles closing the view
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 759d2cf..a7fb61a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -16,14 +16,15 @@
package com.android.systemui.statusbar;
+import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
+import static android.app.StatusBarManager.DISABLE_NONE;
+
import android.annotation.DrawableRes;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.telephony.SubscriptionInfo;
import android.util.ArraySet;
@@ -50,14 +51,15 @@
import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.Utils.DisableStateTracker;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
// Intimately tied to the design of res/layout/signal_cluster_view.xml
public class SignalClusterView extends LinearLayout implements NetworkControllerImpl.SignalCallback,
- SecurityController.SecurityControllerCallback, Tunable,
- DarkReceiver {
+ SecurityController.SecurityControllerCallback, Tunable, DarkReceiver {
static final String TAG = "SignalClusterView";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -73,6 +75,7 @@
private boolean mNoSimsVisible = false;
private boolean mVpnVisible = false;
+ private boolean mSimDetected;
private int mVpnIconId = 0;
private int mLastVpnIconId = -1;
private boolean mEthernetVisible = false;
@@ -148,6 +151,8 @@
mIconScaleFactor = typedValue.getFloat();
mNetworkController = Dependency.get(NetworkController.class);
mSecurityController = Dependency.get(SecurityController.class);
+ addOnAttachStateChangeListener(
+ new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS));
updateActivityEnabled();
}
@@ -327,8 +332,9 @@
}
@Override
- public void setNoSims(boolean show) {
+ public void setNoSims(boolean show, boolean simDetected) {
mNoSimsVisible = show && !mBlockMobile;
+ mSimDetected = simDetected;
apply();
}
@@ -548,6 +554,23 @@
if (mNoSimsVisible) {
mIconLogger.onIconShown(SLOT_MOBILE);
mNoSimsCombo.setVisibility(View.VISIBLE);
+ if (!Objects.equals(mSimDetected, mNoSimsCombo.getTag())) {
+ mNoSimsCombo.setTag(mSimDetected);
+ if (mSimDetected) {
+ SignalDrawable d = new SignalDrawable(mNoSims.getContext());
+ d.setDarkIntensity(0);
+ mNoSims.setImageDrawable(d);
+ mNoSims.setImageLevel(SignalDrawable.getEmptyState(4));
+
+ SignalDrawable dark = new SignalDrawable(mNoSims.getContext());
+ dark.setDarkIntensity(1);
+ mNoSimsDark.setImageDrawable(dark);
+ mNoSimsDark.setImageLevel(SignalDrawable.getEmptyState(4));
+ } else {
+ mNoSims.setImageResource(R.drawable.stat_sys_no_sims);
+ mNoSimsDark.setImageResource(R.drawable.stat_sys_no_sims);
+ }
+ }
} else {
mIconLogger.onIconHidden(SLOT_MOBILE);
mNoSimsCombo.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 2cff79d..561fbb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -796,6 +796,24 @@
}
}
+ /**
+ * This method returns the drawing rect for the view which is different from the regular
+ * drawing rect, since we layout all children at position 0 and usually the translation is
+ * neglected. The standard implementation doesn't account for translation.
+ *
+ * @param outRect The (scrolled) drawing bounds of the view.
+ */
+ @Override
+ public void getDrawingRect(Rect outRect) {
+ super.getDrawingRect(outRect);
+ float translationX = getTranslationX();
+ float translationY = getTranslationY();
+ outRect.left += translationX;
+ outRect.right += translationX;
+ outRect.top += translationY;
+ outRect.bottom += translationY;
+ }
+
public void setIsInShelf(boolean isInShelf) {
mIsInShelf = isInShelf;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
index 9d2d71e..f5c77f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
@@ -21,7 +21,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import android.app.ActivityManager.StackId;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -107,7 +107,7 @@
}
}
- public void taskChanged(String packageName, int stackId) {
+ public void taskChanged(String packageName, ActivityManager.RunningTaskInfo taskInfo) {
// If the package name belongs to a filter, then highlight appropriate button in
// the navigation bar.
if (mFacetPackageMap.containsKey(packageName)) {
@@ -121,8 +121,9 @@
}
// Set up the persistent docked task if needed.
- if (mPersistentTaskIntent != null && !mStatusBar.hasDockedTask()
- && stackId != StackId.HOME_STACK_ID) {
+ boolean isHomeTask =
+ taskInfo.configuration.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME;
+ if (mPersistentTaskIntent != null && !mStatusBar.hasDockedTask() && !isHomeTask) {
mStatusBar.startActivityOnStack(mPersistentTaskIntent,
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 10fc496..59d3e0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -314,7 +314,7 @@
ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask();
if (runningTaskInfo != null && runningTaskInfo.baseActivity != null) {
mController.taskChanged(runningTaskInfo.baseActivity.getPackageName(),
- runningTaskInfo.stackId);
+ runningTaskInfo);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index 9bfa7a9..bb979eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -25,7 +25,6 @@
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.TransformableView;
@@ -48,7 +47,6 @@
private int mContentHeight;
private int mMinHeightHint;
- private boolean mColorized;
protected NotificationTemplateViewWrapper(Context ctx, View view,
ExpandableNotificationRow row) {
@@ -164,9 +162,7 @@
public void onContentUpdated(ExpandableNotificationRow row) {
// Reinspect the notification. Before the super call, because the super call also updates
// the transformation types and we need to have our values set by then.
- StatusBarNotification sbn = row.getStatusBarNotification();
- resolveTemplateViews(sbn);
- mColorized = sbn.getNotification().isColorized();
+ resolveTemplateViews(row.getStatusBarNotification());
super.onContentUpdated(row);
}
@@ -269,17 +265,6 @@
updateActionOffset();
}
- @Override
- public int getMinHeightIncrease(boolean useIncreasedCollapsedHeight) {
- if (mColorized) {
- int dimen = useIncreasedCollapsedHeight
- ? R.dimen.notification_height_increase_colorized_increased
- : R.dimen.notification_height_increase_colorized;
- return mRow.getResources().getDimensionPixelSize(dimen);
- }
- return super.getMinHeightIncrease(useIncreasedCollapsedHeight);
- }
-
private void updateActionOffset() {
if (mActionsContainer != null) {
// We should never push the actions higher than they are in the headsup view.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 085bce9..5200d69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -190,14 +190,4 @@
public boolean disallowSingleClick(float x, float y) {
return false;
}
-
- /**
- * Get the amount that the minheight is allowed to be increased based on this layout.
- *
- * @param increasedHeight is the view allowed to show even bigger, i.e for messaging layouts
- * @return
- */
- public int getMinHeightIncrease(boolean increasedHeight) {
- return 0;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 533771a..d226fed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -264,8 +264,10 @@
pw.println(" StatusBarTransitionsController:");
mStatusBarIconController.getTransitionsController().dump(fd, pw, args);
pw.println();
- pw.println(" NavigationBarTransitionsController:");
- mNavigationBarController.dump(fd, pw, args);
- pw.println();
+ if (mNavigationBarController != null) {
+ pw.println(" NavigationBarTransitionsController:");
+ mNavigationBarController.dump(fd, pw, args);
+ pw.println();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index cfe0a4a..6d3bc1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -437,7 +437,7 @@
}
private boolean onNavigationTouch(View v, MotionEvent event) {
- mStatusBar.checkUserAutohide(v, event);
+ mStatusBar.checkUserAutohide(event);
return false;
}
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 c191618..7b11ace 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -667,7 +667,7 @@
return false;
}
initDownStates(event);
- if (mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
+ if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
mIsExpansionFromHeadsUp = true;
MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 4ae1393..9c837ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -16,8 +16,12 @@
package com.android.systemui.statusbar.phone;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+
import android.app.ActivityManager;
-import android.app.ActivityManager.StackId;
import android.app.ActivityManager.StackInfo;
import android.app.AlarmManager;
import android.app.AlarmManager.AlarmClockInfo;
@@ -523,12 +527,18 @@
mCurrentNotifs.clear();
mUiOffloadThread.submit(() -> {
try {
- int focusedId = ActivityManager.getService().getFocusedStackId();
- if (focusedId == StackId.FULLSCREEN_WORKSPACE_STACK_ID) {
- checkStack(StackId.FULLSCREEN_WORKSPACE_STACK_ID, notifs, noMan, pm);
+ final StackInfo focusedStack = ActivityManager.getService().getFocusedStackInfo();
+ if (focusedStack != null) {
+ final int windowingMode =
+ focusedStack.configuration.windowConfiguration.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ checkStack(focusedStack, notifs, noMan, pm);
+ }
}
if (mDockedStackExists) {
- checkStack(StackId.DOCKED_STACK_ID, notifs, noMan, pm);
+ checkStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED,
+ notifs, noMan, pm);
}
} catch (RemoteException e) {
e.rethrowFromSystemServer();
@@ -539,10 +549,19 @@
});
}
- private void checkStack(int stackId, ArraySet<Pair<String, Integer>> notifs,
+ private void checkStack(int windowingMode, int activityType,
+ ArraySet<Pair<String, Integer>> notifs, NotificationManager noMan, IPackageManager pm) {
+ try {
+ final StackInfo info =
+ ActivityManager.getService().getStackInfo(windowingMode, activityType);
+ checkStack(info, notifs, noMan, pm);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ private void checkStack(StackInfo info, ArraySet<Pair<String, Integer>> notifs,
NotificationManager noMan, IPackageManager pm) {
try {
- StackInfo info = ActivityManager.getService().getStackInfo(stackId);
if (info == null || info.topActivity == null) return;
String pkg = info.topActivity.getPackageName();
if (!hasNotif(notifs, pkg, info.userId)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 4739a2e..d3bb17f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -69,9 +69,6 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.PorterDuff;
@@ -108,7 +105,6 @@
import android.service.notification.StatusBarNotification;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
-import android.text.TextUtils;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -127,13 +123,11 @@
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.ViewParent;
-import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
-import android.view.animation.Interpolator;
import android.widget.DateTimeView;
import android.widget.ImageView;
import android.widget.RemoteViews;
@@ -259,7 +253,6 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
@@ -279,11 +272,9 @@
= SystemProperties.getBoolean("debug.child_notifs", true);
public static final boolean FORCE_REMOTE_INPUT_HISTORY =
SystemProperties.getBoolean("debug.force_remoteinput_history", false);
- private static boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
+ private static final boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
- protected static final int MSG_SHOW_RECENT_APPS = 1019;
protected static final int MSG_HIDE_RECENT_APPS = 1020;
- protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026;
@@ -365,10 +356,6 @@
/** If true, the lockscreen will show a distinct wallpaper */
private static final boolean ENABLE_LOCKSCREEN_WALLPAPER = true;
- /* If true, the device supports freeform window management.
- * This affects the status bar UI. */
- private static final boolean FREEFORM_WINDOW_MANAGEMENT;
-
/**
* How long to wait before auto-dismissing a notification that was kept for remote input, and
* has now sent a remote input. We auto-dismiss, because the app may not see a reason to cancel
@@ -387,19 +374,14 @@
static {
boolean onlyCoreApps;
- boolean freeformWindowManagement;
try {
IPackageManager packageManager =
IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
onlyCoreApps = packageManager.isOnlyCoreApps();
- freeformWindowManagement = packageManager.hasSystemFeature(
- PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT, 0);
} catch (RemoteException e) {
onlyCoreApps = false;
- freeformWindowManagement = false;
}
ONLY_CORE_APPS = onlyCoreApps;
- FREEFORM_WINDOW_MANAGEMENT = freeformWindowManagement;
}
/**
@@ -410,17 +392,17 @@
protected boolean mShowLockscreenNotifications;
protected boolean mAllowLockscreenRemoteInput;
- PhoneStatusBarPolicy mIconPolicy;
+ private PhoneStatusBarPolicy mIconPolicy;
- VolumeComponent mVolumeComponent;
- BrightnessMirrorController mBrightnessMirrorController;
+ private VolumeComponent mVolumeComponent;
+ private BrightnessMirrorController mBrightnessMirrorController;
protected FingerprintUnlockController mFingerprintUnlockController;
- LightBarController mLightBarController;
+ private LightBarController mLightBarController;
protected LockscreenWallpaper mLockscreenWallpaper;
- int mNaturalBarHeight = -1;
+ private int mNaturalBarHeight = -1;
- Point mCurrentDisplaySize = new Point();
+ private final Point mCurrentDisplaySize = new Point();
protected StatusBarWindowView mStatusBarWindow;
protected PhoneStatusBarView mStatusBarView;
@@ -431,15 +413,13 @@
private boolean mWakeUpComingFromTouch;
private PointF mWakeUpTouchLocation;
- int mPixelFormat;
- Object mQueueLock = new Object();
+ private final Object mQueueLock = new Object();
protected StatusBarIconController mIconController;
// expanded notifications
protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
- View mExpandedContents;
- TextView mNotificationPanelDebugText;
+ private TextView mNotificationPanelDebugText;
/**
* {@code true} if notifications not part of a group should by default be rendered in their
@@ -452,12 +432,10 @@
private QSPanel mQSPanel;
// top bar
- protected KeyguardStatusBarView mKeyguardStatusBar;
- boolean mLeaveOpenOnKeyguardHide;
+ private KeyguardStatusBarView mKeyguardStatusBar;
+ private boolean mLeaveOpenOnKeyguardHide;
KeyguardIndicationController mKeyguardIndicationController;
- // Keyguard is going away soon.
- private boolean mKeyguardGoingAway;
// Keyguard is actually fading away now.
protected boolean mKeyguardFadingAway;
protected long mKeyguardFadingAwayDelay;
@@ -469,25 +447,19 @@
private View mReportRejectedTouch;
- int mMaxAllowedKeyguardNotifications;
+ private int mMaxAllowedKeyguardNotifications;
- boolean mExpandedVisible;
+ private boolean mExpandedVisible;
- // the tracker view
- int mTrackingPosition; // the position of the top of the tracking view.
-
- // Tracking finger for opening/closing.
- boolean mTracking;
-
- int[] mAbsPos = new int[2];
- ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
+ private final int[] mAbsPos = new int[2];
+ private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
// for disabling the status bar
- int mDisabled1 = 0;
- int mDisabled2 = 0;
+ private int mDisabled1 = 0;
+ private int mDisabled2 = 0;
// tracking calls to View.setSystemUiVisibility()
- int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
+ private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
private final Rect mLastFullscreenStackBounds = new Rect();
private final Rect mLastDockedStackBounds = new Rect();
private final Rect mTmpRect = new Rect();
@@ -495,7 +467,7 @@
// last value sent to window manager
private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
- DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
// XXX: gesture research
private final GestureRecorder mGestureRec = DEBUG_GESTURES
@@ -507,14 +479,17 @@
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
// ensure quick settings is disabled until the current user makes it through the setup wizard
- private boolean mUserSetup = false;
- private DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
+ @VisibleForTesting
+ protected boolean mUserSetup = false;
+ private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
@Override
public void onUserSetupChanged() {
final boolean userSetup = mDeviceProvisionedController.isUserSetup(
mDeviceProvisionedController.getCurrentUser());
- if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " +
- "userSetup=%s mUserSetup=%s", userSetup, mUserSetup));
+ if (MULTIUSER_DEBUG) {
+ Log.d(TAG, String.format("User setup changed: userSetup=%s mUserSetup=%s",
+ userSetup, mUserSetup));
+ }
if (userSetup != mUserSetup) {
mUserSetup = userSetup;
@@ -528,7 +503,7 @@
}
};
- protected H mHandler = createHandler();
+ protected final H mHandler = createHandler();
final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
@@ -537,8 +512,6 @@
&& Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
Settings.Global.HEADS_UP_OFF);
- mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt(
- mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0);
Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
if (wasUsing != mUseHeadsUp) {
if (!mUseHeadsUp) {
@@ -566,26 +539,21 @@
}
};
- private boolean mWaitingForKeyguardExit;
protected boolean mDozing;
private boolean mDozingRequested;
protected boolean mScrimSrcModeEnabled;
- public static final Interpolator ALPHA_IN = Interpolators.ALPHA_IN;
- public static final Interpolator ALPHA_OUT = Interpolators.ALPHA_OUT;
-
protected BackDropView mBackdrop;
protected ImageView mBackdropFront, mBackdropBack;
- protected PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
- protected PorterDuffXfermode mSrcOverXferMode =
+ protected final PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
+ protected final PorterDuffXfermode mSrcOverXferMode =
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
private MediaSessionManager mMediaSessionManager;
private MediaController mMediaController;
private String mMediaNotificationKey;
private MediaMetadata mMediaMetadata;
- private MediaController.Callback mMediaListener
- = new MediaController.Callback() {
+ private final MediaController.Callback mMediaListener = new MediaController.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
super.onPlaybackStateChanged(state);
@@ -607,17 +575,6 @@
}
};
- private final OnChildLocationsChangedListener mOnChildLocationsChangedListener =
- new OnChildLocationsChangedListener() {
- @Override
- public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) {
- userActivity();
- }
- };
-
- private int mDisabledUnmodified1;
- private int mDisabledUnmodified2;
-
/** Keys of notifications currently visible to the user. */
private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications =
new ArraySet<>();
@@ -644,15 +601,6 @@
private boolean mWereIconsJustHidden;
private boolean mBouncerWasShowingWhenHidden;
- public boolean isStartedGoingToSleep() {
- return mStartedGoingToSleep;
- }
-
- /**
- * If set, the device has started going to sleep but isn't fully non-interactive yet.
- */
- protected boolean mStartedGoingToSleep;
-
private final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
new OnChildLocationsChangedListener() {
@Override
@@ -686,7 +634,6 @@
@Override
public void run() {
mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
- final String mediaKey = getCurrentMediaNotificationKey();
// 1. Loop over mNotificationData entries:
// A. Keep list of visible notifications.
@@ -743,10 +690,10 @@
private boolean mKeyguardRequested;
private boolean mIsKeyguard;
private LogMaker mStatusBarStateLog;
- private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
+ private final LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
protected NotificationIconAreaController mNotificationIconAreaController;
private boolean mReinflateNotificationsOnUserSwitched;
- private HashMap<String, Entry> mPendingNotifications = new HashMap<>();
+ private final HashMap<String, Entry> mPendingNotifications = new HashMap<>();
private boolean mClearAllEnabled;
@Nullable private View mAmbientIndicationContainer;
private String mKeyToRemoveOnGutsClosed;
@@ -769,20 +716,21 @@
goToLockedShade(null);
}
};
- private HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap
- = new HashMap<>();
+ private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
+ mTmpChildOrderMap = new HashMap<>();
private RankingMap mLatestRankingMap;
private boolean mNoAnimationOnNextBarModeChange;
private FalsingManager mFalsingManager;
- private KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onDreamingStateChanged(boolean dreaming) {
- if (dreaming) {
- maybeEscalateHeadsUp();
- }
- }
- };
+ private final KeyguardUpdateMonitorCallback mUpdateCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onDreamingStateChanged(boolean dreaming) {
+ if (dreaming) {
+ maybeEscalateHeadsUp();
+ }
+ }
+ };
private NavigationBarFragment mNavigationBar;
private View mNavigationBarView;
@@ -861,10 +809,6 @@
mRecents = getComponent(Recents.class);
- final Configuration currentConfig = res.getConfiguration();
- mLocale = currentConfig.locale;
- mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
-
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
mLockPatternUtils = new LockPatternUtils(mContext);
@@ -994,9 +938,6 @@
Dependency.get(ConfigurationController.class).addCallback(this);
}
- protected void createIconController() {
- }
-
// ================================================================================
// Constructing the view
// ================================================================================
@@ -1012,16 +953,14 @@
// TODO: Deal with the ugliness that comes from having some of the statusbar broken out
// into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
- mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
- R.id.notification_panel);
- mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
- R.id.notification_stack_scroller);
+ mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
+ mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
mNotificationPanel.setStatusBar(this);
mNotificationPanel.setGroupManager(mGroupManager);
mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
mAboveShelfObserver.setListener(mStatusBarWindow.findViewById(
R.id.notification_container_parent));
- mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
+ mKeyguardStatusBar = mStatusBarWindow.findViewById(R.id.keyguard_header);
mNotificationIconAreaController = SystemUIFactory.getInstance()
.createNotificationIconAreaController(context, this);
@@ -1060,8 +999,7 @@
putComponent(HeadsUpManager.class, mHeadsUpManager);
if (MULTIUSER_DEBUG) {
- mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
- R.id.header_debug_info);
+ mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info);
mNotificationPanelDebugText.setVisibility(View.VISIBLE);
}
@@ -1075,9 +1013,6 @@
// no window manager? good luck with that
}
- // figure out which pixel-format to use for the status bar.
- mPixelFormat = PixelFormat.OPAQUE;
-
mStackScroller.setLongPressListener(getNotificationLongClicker());
mStackScroller.setStatusBar(this);
mStackScroller.setGroupManager(mGroupManager);
@@ -1087,11 +1022,10 @@
inflateEmptyShadeView();
inflateDismissView();
- mExpandedContents = mStackScroller;
- mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop);
- mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front);
- mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back);
+ mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop);
+ mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front);
+ mBackdropBack = mBackdrop.findViewById(R.id.backdrop_back);
if (ENABLE_LOCKSCREEN_WALLPAPER) {
mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
@@ -1099,8 +1033,8 @@
mKeyguardIndicationController =
SystemUIFactory.getInstance().createKeyguardIndicationController(mContext,
- (ViewGroup) mStatusBarWindow.findViewById(R.id.keyguard_indication_area),
- mNotificationPanel.getLockIcon());
+ mStatusBarWindow.findViewById(R.id.keyguard_indication_area),
+ mNotificationPanel.getLockIcon());
mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController);
@@ -1131,8 +1065,8 @@
mNavigationBar.setLightBarController(mLightBarController);
}
- ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind);
- ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);
+ ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind);
+ ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front);
View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim);
mScrimController = SystemUIFactory.getInstance().createScrimController(mLightBarController,
scrimBehind, scrimInFront, headsUpScrim, mLockscreenWallpaper,
@@ -1142,13 +1076,10 @@
}
});
if (mScrimSrcModeEnabled) {
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE;
- mScrimController.setDrawBehindAsSrc(asSrc);
- mStackScroller.setDrawBackgroundAsSrc(asSrc);
- }
+ Runnable runnable = () -> {
+ boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE;
+ mScrimController.setDrawBehindAsSrc(asSrc);
+ mStackScroller.setDrawBackgroundAsSrc(asSrc);
};
mBackdrop.setOnVisibilityChangedRunnable(runnable);
runnable.run();
@@ -1170,11 +1101,11 @@
if (container != null) {
FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
- Dependency.get(ExtensionController.class).newExtension(QS.class)
+ Dependency.get(ExtensionController.class)
+ .newExtension(QS.class)
.withPlugin(QS.class)
- .withFeature(
- PackageManager.FEATURE_AUTOMOTIVE, () -> new CarQSFragment())
- .withDefault(() -> new QSFragment())
+ .withFeature(PackageManager.FEATURE_AUTOMOTIVE, CarQSFragment::new)
+ .withDefault(QSFragment::new)
.build());
final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
mIconController);
@@ -1276,7 +1207,7 @@
*/
protected View.OnTouchListener getStatusBarWindowTouchListener() {
return (v, event) -> {
- checkUserAutohide(v, event);
+ checkUserAutohide(event);
checkRemoteInputOutside(event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mExpandedVisible) {
@@ -1380,8 +1311,7 @@
public static SignalClusterView reinflateSignalCluster(View view) {
Context context = view.getContext();
- SignalClusterView signalCluster =
- (SignalClusterView) view.findViewById(R.id.signal_cluster);
+ SignalClusterView signalCluster = view.findViewById(R.id.signal_cluster);
if (signalCluster != null) {
ViewParent parent = signalCluster.getParent();
if (parent instanceof ViewGroup) {
@@ -1421,20 +1351,17 @@
mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
- mDismissView.setOnButtonClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES);
- clearAllNotifications();
- }
+ mDismissView.setOnButtonClickListener(v -> {
+ mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES);
+ clearAllNotifications();
});
mStackScroller.setDismissView(mDismissView);
}
protected void createUserSwitcher() {
mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
- (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
- mKeyguardStatusBar, mNotificationPanel);
+ mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mKeyguardStatusBar,
+ mNotificationPanel);
}
protected void inflateStatusBarWindow(Context context) {
@@ -1447,7 +1374,7 @@
// animate-swipe all dismissable notifications, then animate the shade closed
int numChildren = mStackScroller.getChildCount();
- final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren);
+ final ArrayList<View> viewsToHide = new ArrayList<>(numChildren);
final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren);
for (int i = 0; i < numChildren; i++) {
final View child = mStackScroller.getChildAt(i);
@@ -1487,20 +1414,18 @@
return;
}
- addPostCollapseAction(new Runnable() {
- @Override
- public void run() {
- mStackScroller.setDismissAllInProgress(false);
- for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
- if (mStackScroller.canChildBeDismissed(rowToRemove)) {
- removeNotification(rowToRemove.getEntry().key, null);
- } else {
- rowToRemove.resetTranslation();
- }
+ addPostCollapseAction(() -> {
+ mStackScroller.setDismissAllInProgress(false);
+ for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
+ if (mStackScroller.canChildBeDismissed(rowToRemove)) {
+ removeNotification(rowToRemove.getEntry().key, null);
+ } else {
+ rowToRemove.resetTranslation();
}
- try {
- mBarService.onClearAllNotifications(mCurrentUserId);
- } catch (Exception ex) { }
+ }
+ try {
+ mBarService.onClearAllNotifications(mCurrentUserId);
+ } catch (Exception ex) {
}
});
@@ -1509,13 +1434,15 @@
}
private void performDismissAllAnimations(ArrayList<View> hideAnimatedList) {
- Runnable animationFinishAction = new Runnable() {
- @Override
- public void run() {
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
- }
+ Runnable animationFinishAction = () -> {
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
};
+ if (hideAnimatedList.isEmpty()) {
+ animationFinishAction.run();
+ return;
+ }
+
// let's disable our normal animations
mStackScroller.setDismissAllInProgress(true);
@@ -1632,10 +1559,6 @@
SystemServicesProxy.getInstance(mContext).awakenDreamsAsync();
}
- public UserHandle getCurrentUserHandle() {
- return new UserHandle(mCurrentUserId);
- }
-
public void addNotification(StatusBarNotification notification, RankingMap ranking)
throws InflationException {
String key = notification.getKey();
@@ -1746,7 +1669,7 @@
boolean deferRemoval = false;
abortExistingInflation(key);
if (mHeadsUpManager.isHeadsUp(key)) {
- // A cancel() in repsonse to a remote input shouldn't be delayed, as it makes the
+ // A cancel() in response to a remote input shouldn't be delayed, as it makes the
// sending look longer than it takes.
// Also we should not defer the removal if reordering isn't allowed since otherwise
// some notifications can't disappear before the panel is closed.
@@ -1772,9 +1695,7 @@
newHistory = new CharSequence[1];
} else {
newHistory = new CharSequence[oldHistory.length + 1];
- for (int i = 0; i < oldHistory.length; i++) {
- newHistory[i + 1] = oldHistory[i];
- }
+ System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length);
}
newHistory[0] = String.valueOf(entry.remoteInputText);
b.setRemoteInputHistory(newHistory);
@@ -1833,7 +1754,7 @@
mStackScroller.cleanUpViewState(entry.row);
}
// Let's remove the children if this was a summary
- handleGroupSummaryRemoved(key, ranking);
+ handleGroupSummaryRemoved(key);
StatusBarNotification old = removeNotificationViews(key, ranking);
if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
@@ -1857,12 +1778,10 @@
*
* This also ensures that the animation looks nice and only consists of a single disappear
* animation instead of multiple.
+ * @param key the key of the notification was removed
*
- * @param key the key of the notification was removed
- * @param ranking the current ranking
*/
- private void handleGroupSummaryRemoved(String key,
- RankingMap ranking) {
+ private void handleGroupSummaryRemoved(String key) {
Entry entry = mNotificationData.get(key);
if (entry != null && entry.row != null
&& entry.row.isSummaryWithChildren()) {
@@ -1873,15 +1792,13 @@
}
List<ExpandableNotificationRow> notificationChildren =
entry.row.getNotificationChildren();
- ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
for (int i = 0; i < notificationChildren.size(); i++) {
ExpandableNotificationRow row = notificationChildren.get(i);
if ((row.getStatusBarNotification().getNotification().flags
& Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- // the child is a forground service notification which we can't remove!
+ // the child is a foreground service notification which we can't remove!
continue;
}
- toRemove.add(row);
row.setKeepInParent(true);
// we need to set this state earlier as otherwise we might generate some weird
// animations
@@ -1924,19 +1841,14 @@
// Do not modify the notifications during collapse.
if (isCollapsing()) {
- addPostCollapseAction(new Runnable() {
- @Override
- public void run() {
- updateNotificationShade();
- }
- });
+ addPostCollapseAction(this::updateNotificationShade);
return;
}
ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
final int N = activeNotifications.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
Entry ent = activeNotifications.get(i);
if (ent.row.isDismissed() || ent.row.isRemoved()) {
// we don't want to update removed notifications because they could
@@ -1980,7 +1892,8 @@
for (ExpandableNotificationRow remove : toRemove) {
if (mGroupManager.isChildInGroupWithSummary(remove.getStatusBarNotification())) {
- // we are only transfering this notification to its parent, don't generate an animation
+ // we are only transferring this notification to its parent, don't generate an
+ // animation
mStackScroller.setChildTransferInProgress(true);
}
if (remove.isSummaryWithChildren()) {
@@ -1992,7 +1905,7 @@
removeNotificationChildren();
- for (int i=0; i<toShow.size(); i++) {
+ for (int i = 0; i < toShow.size(); i++) {
View v = toShow.get(i);
if (v.getParent() == null) {
mVisualStabilityManager.notifyViewAddition(v);
@@ -2066,6 +1979,7 @@
mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned()
&& (mUserSetup || mUserSwitcherController == null
|| !mUserSwitcherController.isSimpleUserSwitcher())
+ && ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0)
&& ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
&& !mDozing
&& !ONLY_CORE_APPS);
@@ -2102,7 +2016,7 @@
}
}
- // Finally after removing and adding has been beformed we can apply the order.
+ // Finally after removing and adding has been performed we can apply the order.
orderChanged |= parent.applyChildOrder(orderedChildren, mVisualStabilityManager, this);
}
if (orderChanged) {
@@ -2275,10 +2189,11 @@
MediaController controller = null;
for (int i = 0; i < N; i++) {
final Entry entry = activeNotifications.get(i);
+
if (isMediaNotification(entry)) {
final MediaSession.Token token =
- entry.notification.getNotification().extras
- .getParcelable(Notification.EXTRA_MEDIA_SESSION);
+ entry.notification.getNotification().extras.getParcelable(
+ Notification.EXTRA_MEDIA_SESSION);
if (token != null) {
MediaController aController = new MediaController(mContext, token);
if (PlaybackState.STATE_PLAYING ==
@@ -2316,7 +2231,7 @@
if (entry.notification.getPackageName().equals(pkg)) {
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: found controller matching "
- + entry.notification.getKey());
+ + entry.notification.getKey());
}
controller = aController;
mediaNotification = entry;
@@ -2367,12 +2282,8 @@
}
private boolean isPlaybackActive(int state) {
- if (state != PlaybackState.STATE_STOPPED
- && state != PlaybackState.STATE_ERROR
- && state != PlaybackState.STATE_NONE) {
- return true;
- }
- return false;
+ return state != PlaybackState.STATE_STOPPED && state != PlaybackState.STATE_ERROR
+ && state != PlaybackState.STATE_NONE;
}
private void clearCurrentMediaNotification() {
@@ -2397,7 +2308,7 @@
/**
* Hide the album artwork that is fading out and release its bitmap.
*/
- protected Runnable mHideBackdropFront = new Runnable() {
+ protected final Runnable mHideBackdropFront = new Runnable() {
@Override
public void run() {
if (DEBUG_MEDIA) {
@@ -2549,14 +2460,11 @@
.setInterpolator(Interpolators.ACCELERATE_DECELERATE)
.setDuration(300)
.setStartDelay(0)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mBackdrop.setVisibility(View.GONE);
- mBackdropFront.animate().cancel();
- mBackdropBack.setImageDrawable(null);
- mHandler.post(mHideBackdropFront);
- }
+ .withEndAction(() -> {
+ mBackdrop.setVisibility(View.GONE);
+ mBackdropFront.animate().cancel();
+ mBackdropBack.setImageDrawable(null);
+ mHandler.post(mHideBackdropFront);
});
if (mKeyguardFadingAway) {
mBackdrop.animate()
@@ -2587,8 +2495,6 @@
@Override
public void disable(int state1, int state2, boolean animate) {
animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN;
- mDisabledUnmodified1 = state1;
- mDisabledUnmodified2 = state2;
final int old1 = mDisabled1;
final int diff1 = state1 ^ old1;
mDisabled1 = state1;
@@ -2624,8 +2530,13 @@
flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_CLOCK)) ? '!' : ' ');
flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SEARCH)) ? 'S' : 's');
flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SEARCH)) ? '!' : ' ');
+ flagdbg.append("> disable2<");
flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? 'Q' : 'q');
flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? '!' : ' ');
+ flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? 'I' : 'i');
+ flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? '!' : ' ');
+ flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? 'N' : 'n');
+ flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? '!' : ' ');
flagdbg.append('>');
Log.d(TAG, flagdbg.toString());
@@ -2652,6 +2563,13 @@
if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
updateQsExpansionEnabled();
}
+
+ if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+ updateQsExpansionEnabled();
+ if ((state1 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+ animateCollapsePanels();
+ }
+ }
}
/**
@@ -2739,11 +2657,8 @@
// make sure that the window stays small for one frame until the touchableRegion is set.
mNotificationPanel.requestLayout();
mStatusBarWindowManager.setForceWindowCollapsed(true);
- mNotificationPanel.post(new Runnable() {
- @Override
- public void run() {
- mStatusBarWindowManager.setForceWindowCollapsed(false);
- }
+ mNotificationPanel.post(() -> {
+ mStatusBarWindowManager.setForceWindowCollapsed(false);
});
}
} else {
@@ -2755,15 +2670,12 @@
// we need to keep the panel open artificially, let's wait until the animation
// is finished.
mHeadsUpManager.setHeadsUpGoingAway(true);
- mStackScroller.runAfterAnimationFinished(new Runnable() {
- @Override
- public void run() {
- if (!mHeadsUpManager.hasPinnedHeadsUp()) {
- mStatusBarWindowManager.setHeadsUpShowing(false);
- mHeadsUpManager.setHeadsUpGoingAway(false);
- }
- removeRemoteInputEntriesKeptUntilCollapsed();
+ mStackScroller.runAfterAnimationFinished(() -> {
+ if (!mHeadsUpManager.hasPinnedHeadsUp()) {
+ mStatusBarWindowManager.setHeadsUpShowing(false);
+ mHeadsUpManager.setHeadsUpGoingAway(false);
}
+ removeRemoteInputEntriesKeptUntilCollapsed();
});
}
}
@@ -3014,7 +2926,9 @@
}
boolean panelsEnabled() {
- return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0 && !ONLY_CORE_APPS;
+ return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0
+ && (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
+ && !ONLY_CORE_APPS;
}
void makeExpandedVisible(boolean force) {
@@ -3030,7 +2944,6 @@
mStatusBarWindowManager.setPanelVisible(true);
visibilityChanged(true);
- mWaitingForKeyguardExit = false;
recomputeDisableFlags(!force /* animate */);
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
}
@@ -3039,23 +2952,15 @@
animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
}
- private final Runnable mAnimateCollapsePanels = new Runnable() {
- @Override
- public void run() {
- animateCollapsePanels();
- }
- };
+ private final Runnable mAnimateCollapsePanels = this::animateCollapsePanels;
public void postAnimateCollapsePanels() {
mHandler.post(mAnimateCollapsePanels);
}
public void postAnimateForceCollapsePanels() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
- }
+ mHandler.post(() -> {
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
});
}
@@ -3105,6 +3010,9 @@
}
}
+ // TODO(b/62444020): remove when this bug is fixed
+ Log.v(TAG, "mStatusBarWindow: " + mStatusBarWindow + " canPanelBeCollapsed(): "
+ + mNotificationPanel.canPanelBeCollapsed());
if (mStatusBarWindow != null && mNotificationPanel.canPanelBeCollapsed()) {
// release focus immediately to kick off focus change transition
mStatusBarWindowManager.setStatusBarFocusable(false);
@@ -3210,7 +3118,7 @@
if (SPEW) {
Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled1="
- + mDisabled1 + " mDisabled2=" + mDisabled2 + " mTracking=" + mTracking);
+ + mDisabled1 + " mDisabled2=" + mDisabled2);
} else if (CHATTY) {
if (event.getAction() != MotionEvent.ACTION_MOVE) {
Log.d(TAG, String.format(
@@ -3295,10 +3203,8 @@
sbModeChanged = sbMode != -1;
if (sbModeChanged && sbMode != mStatusBarMode) {
- if (sbMode != mStatusBarMode) {
- mStatusBarMode = sbMode;
- checkBarModes();
- }
+ mStatusBarMode = sbMode;
+ checkBarModes();
touchAutoHide();
}
@@ -3385,12 +3291,7 @@
}
}
- private final Runnable mCheckBarModes = new Runnable() {
- @Override
- public void run() {
- checkBarModes();
- }
- };
+ private final Runnable mCheckBarModes = this::checkBarModes;
public void setInteracting(int barWindow, boolean interacting) {
final boolean changing = ((mInteractingWindows & barWindow) != 0) != interacting;
@@ -3449,7 +3350,7 @@
}
}
- void checkUserAutohide(View v, MotionEvent event) {
+ void checkUserAutohide(MotionEvent event) {
if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0 // a transient bar is revealed
&& event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
&& event.getX() == 0 && event.getY() == 0 // a touch outside both bars
@@ -3516,9 +3417,7 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mQueueLock) {
pw.println("Current Status Bar state:");
- pw.println(" mExpandedVisible=" + mExpandedVisible
- + ", mTrackingPosition=" + mTrackingPosition);
- pw.println(" mTracking=" + mTracking);
+ pw.println(" mExpandedVisible=" + mExpandedVisible);
pw.println(" mDisplayMetrics=" + mDisplayMetrics);
pw.println(" mStackScroller: " + viewInfo(mStackScroller));
pw.println(" mStackScroller: " + viewInfo(mStackScroller)
@@ -3606,16 +3505,12 @@
if (false) {
pw.println("see the logcat for a dump of the views we have created.");
// must happen on ui thread
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mStatusBarView.getLocationOnScreen(mAbsPos);
- Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
- + ") " + mStatusBarView.getWidth() + "x"
- + getStatusBarHeight());
- mStatusBarView.debug();
- }
- });
+ mHandler.post(() -> {
+ mStatusBarView.getLocationOnScreen(mAbsPos);
+ Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] +
+ ") " + mStatusBarView.getWidth() + "x" + getStatusBarHeight());
+ mStatusBarView.debug();
+ });
}
}
@@ -3695,49 +3590,43 @@
final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
mContext, intent, mCurrentUserId);
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- mAssistManager.hideAssist();
- intent.setFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- int result = ActivityManager.START_CANCELED;
- ActivityOptions options = new ActivityOptions(getActivityOptions());
- options.setDisallowEnterPictureInPictureWhileLaunching(
- disallowEnterPictureInPictureWhileLaunching);
- if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) {
- // Normally an activity will set it's requested rotation
- // animation on its window. However when launching an activity
- // causes the orientation to change this is too late. In these cases
- // the default animation is used. This doesn't look good for
- // the camera (as it rotates the camera contents out of sync
- // with physical reality). So, we ask the WindowManager to
- // force the crossfade animation if an orientation change
- // happens to occur during the launch.
- options.setRotationAnimationHint(
- WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
- }
- try {
- result = ActivityManager.getService().startActivityAsUser(
- null, mContext.getBasePackageName(),
- intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
- options.toBundle(), UserHandle.CURRENT.getIdentifier());
- } catch (RemoteException e) {
- Log.w(TAG, "Unable to start activity", e);
- }
- if (callback != null) {
- callback.onActivityStarted(result);
- }
+ Runnable runnable = () -> {
+ mAssistManager.hideAssist();
+ intent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ int result = ActivityManager.START_CANCELED;
+ ActivityOptions options = new ActivityOptions(getActivityOptions());
+ options.setDisallowEnterPictureInPictureWhileLaunching(
+ disallowEnterPictureInPictureWhileLaunching);
+ if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) {
+ // Normally an activity will set it's requested rotation
+ // animation on its window. However when launching an activity
+ // causes the orientation to change this is too late. In these cases
+ // the default animation is used. This doesn't look good for
+ // the camera (as it rotates the camera contents out of sync
+ // with physical reality). So, we ask the WindowManager to
+ // force the crossfade animation if an orientation change
+ // happens to occur during the launch.
+ options.setRotationAnimationHint(
+ WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
+ }
+ try {
+ result = ActivityManager.getService().startActivityAsUser(
+ null, mContext.getBasePackageName(),
+ intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
+ options.toBundle(), UserHandle.CURRENT.getIdentifier());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to start activity", e);
+ }
+ if (callback != null) {
+ callback.onActivityStarted(result);
}
};
- Runnable cancelRunnable = new Runnable() {
- @Override
- public void run() {
- if (callback != null) {
- callback.onActivityStarted(ActivityManager.START_CANCELED);
- }
+ Runnable cancelRunnable = () -> {
+ if (callback != null) {
+ callback.onActivityStarted(ActivityManager.START_CANCELED);
}
};
executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShade,
@@ -3782,7 +3671,7 @@
}, cancelAction, afterKeyguardGone);
}
- private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.v(TAG, "onReceive: " + intent);
@@ -3811,7 +3700,7 @@
}
};
- private BroadcastReceiver mDemoReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mDemoReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.v(TAG, "onReceive: " + intent);
@@ -3972,7 +3861,6 @@
* The LEDs are turned off when the notification panel is shown, even just a little bit.
* See also StatusBar.setPanelExpanded for another place where we attempt to do this.
*/
- // Old BaseStatusBar.handleVisibileToUserChanged
private void handleVisibleToUserChangedImpl(boolean visibleToUser) {
try {
if (visibleToUser) {
@@ -3997,8 +3885,8 @@
// Report all notifications as invisible and turn down the
// reporter.
if (!mCurrentlyVisibleNotifications.isEmpty()) {
- logNotificationVisibilityChanges(Collections.<NotificationVisibility>emptyList(),
- mCurrentlyVisibleNotifications);
+ logNotificationVisibilityChanges(
+ Collections.emptyList(), mCurrentlyVisibleNotifications);
recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
}
mHandler.removeCallbacks(mVisibilityReporter);
@@ -4105,7 +3993,7 @@
vib.vibrate(250, VIBRATION_ATTRIBUTES);
}
- Runnable mStartTracing = new Runnable() {
+ final Runnable mStartTracing = new Runnable() {
@Override
public void run() {
vibrate();
@@ -4116,13 +4004,10 @@
}
};
- Runnable mStopTracing = new Runnable() {
- @Override
- public void run() {
- android.os.Debug.stopMethodTracing();
- Log.d(TAG, "stopTracing");
- vibrate();
- }
+ final Runnable mStopTracing = () -> {
+ android.os.Debug.stopMethodTracing();
+ Log.d(TAG, "stopTracing");
+ vibrate();
};
@Override
@@ -4149,40 +4034,6 @@
startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */);
}
- private static class FastColorDrawable extends Drawable {
- private final int mColor;
-
- public FastColorDrawable(int color) {
- mColor = 0xff000000 | color;
- }
-
- @Override
- public void draw(Canvas canvas) {
- canvas.drawColor(mColor, PorterDuff.Mode.SRC);
- }
-
- @Override
- public void setAlpha(int alpha) {
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.OPAQUE;
- }
-
- @Override
- public void setBounds(int left, int top, int right, int bottom) {
- }
-
- @Override
- public void setBounds(Rect bounds) {
- }
- }
-
public void destroy() {
// Begin old BaseStatusBar.destroy().
mContext.unregisterReceiver(mBaseBroadcastReceiver);
@@ -4400,31 +4251,23 @@
Runnable endRunnable) {
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
mLaunchTransitionEndRunnable = endRunnable;
- Runnable hideRunnable = new Runnable() {
- @Override
- public void run() {
- mLaunchTransitionFadingAway = true;
- if (beforeFading != null) {
- beforeFading.run();
- }
- mScrimController.forceHideScrims(true /* hide */, false /* animated */);
- updateMediaMetaData(false, true);
- mNotificationPanel.setAlpha(1);
- mStackScroller.setParentNotFullyVisible(true);
- mNotificationPanel.animate()
- .alpha(0)
- .setStartDelay(FADE_KEYGUARD_START_DELAY)
- .setDuration(FADE_KEYGUARD_DURATION)
- .withLayer()
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- onLaunchTransitionFadingEnded();
- }
- });
- mCommandQueue.appTransitionStarting(SystemClock.uptimeMillis(),
- LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
+ Runnable hideRunnable = () -> {
+ mLaunchTransitionFadingAway = true;
+ if (beforeFading != null) {
+ beforeFading.run();
}
+ mScrimController.forceHideScrims(true /* hide */, false /* animated */);
+ updateMediaMetaData(false, true);
+ mNotificationPanel.setAlpha(1);
+ mStackScroller.setParentNotFullyVisible(true);
+ mNotificationPanel.animate()
+ .alpha(0)
+ .setStartDelay(FADE_KEYGUARD_START_DELAY)
+ .setDuration(FADE_KEYGUARD_DURATION)
+ .withLayer()
+ .withEndAction(this::onLaunchTransitionFadingEnded);
+ mCommandQueue.appTransitionStarting(SystemClock.uptimeMillis(),
+ LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
};
if (mNotificationPanel.isLaunchTransitionRunning()) {
mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable);
@@ -4553,7 +4396,6 @@
// Treat Keyguard exit animation as an app transition to achieve nice transition for status
// bar.
- mKeyguardGoingAway = true;
mKeyguardMonitor.notifyKeyguardGoingAway(true);
mCommandQueue.appTransitionPending(true);
}
@@ -4562,14 +4404,13 @@
* Notifies the status bar the Keyguard is fading away with the specified timings.
*
* @param startTime the start time of the animations in uptime millis
- * @param delay the precalculated animation delay in miliseconds
+ * @param delay the precalculated animation delay in milliseconds
* @param fadeoutDuration the duration of the exit animation, in milliseconds
*/
public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration) {
mKeyguardFadingAway = true;
mKeyguardFadingAwayDelay = delay;
mKeyguardFadingAwayDuration = fadeoutDuration;
- mWaitingForKeyguardExit = false;
mCommandQueue.appTransitionStarting(startTime + fadeoutDuration
- LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
@@ -4589,14 +4430,9 @@
*/
public void finishKeyguardFadingAway() {
mKeyguardFadingAway = false;
- mKeyguardGoingAway = false;
mKeyguardMonitor.notifyKeyguardDoneFading();
}
- public void stopWaitingForKeyguardExit() {
- mWaitingForKeyguardExit = false;
- }
-
private void updatePublicMode() {
final boolean showingKeyguard = mStatusBarKeyguardViewManager.isShowing();
final boolean devicePublic = showingKeyguard
@@ -4810,7 +4646,6 @@
}
protected void showBouncer() {
- mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
mStatusBarKeyguardViewManager.dismiss();
}
@@ -4827,7 +4662,7 @@
@Override
public void onActivated(ActivatableNotificationView view) {
- onActivated((View)view);
+ onActivated((View) view);
mStackScroller.setActivatedChild(view);
}
@@ -5022,6 +4857,10 @@
* @param expandView The view to expand after going to the shade.
*/
public void goToLockedShade(View expandView) {
+ if ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+ return;
+ }
+
int userId = mCurrentUserId;
ExpandableNotificationRow row = null;
if (expandView instanceof ExpandableNotificationRow) {
@@ -5129,51 +4968,41 @@
updateNotifications();
if (mPendingWorkRemoteInputView != null && !isAnyProfilePublicMode()) {
// Expand notification panel and the notification row, then click on remote input view
- final Runnable clickPendingViewRunnable = new Runnable() {
- @Override
- public void run() {
- final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView;
- if (pendingWorkRemoteInputView == null) {
+ final Runnable clickPendingViewRunnable = () -> {
+ final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView;
+ if (pendingWorkRemoteInputView == null) {
+ return;
+ }
+
+ // Climb up the hierarchy until we get to the container for this row.
+ ViewParent p = pendingWorkRemoteInputView.getParent();
+ while (!(p instanceof ExpandableNotificationRow)) {
+ if (p == null) {
return;
}
+ p = p.getParent();
+ }
- // Climb up the hierarchy until we get to the container for this row.
- ViewParent p = pendingWorkRemoteInputView.getParent();
- while (!(p instanceof ExpandableNotificationRow)) {
- if (p == null) {
- return;
+ final ExpandableNotificationRow row = (ExpandableNotificationRow) p;
+ ViewParent viewParent = row.getParent();
+ if (viewParent instanceof NotificationStackScrollLayout) {
+ final NotificationStackScrollLayout scrollLayout =
+ (NotificationStackScrollLayout) viewParent;
+ row.makeActionsVisibile();
+ row.post(() -> {
+ final Runnable finishScrollingCallback = () -> {
+ mPendingWorkRemoteInputView.callOnClick();
+ mPendingWorkRemoteInputView = null;
+ scrollLayout.setFinishScrollingCallback(null);
+ };
+ if (scrollLayout.scrollTo(row)) {
+ // It scrolls! So call it when it's finished.
+ scrollLayout.setFinishScrollingCallback(finishScrollingCallback);
+ } else {
+ // It does not scroll, so call it now!
+ finishScrollingCallback.run();
}
- p = p.getParent();
- }
-
- final ExpandableNotificationRow row = (ExpandableNotificationRow) p;
- ViewParent viewParent = row.getParent();
- if (viewParent instanceof NotificationStackScrollLayout) {
- final NotificationStackScrollLayout scrollLayout =
- (NotificationStackScrollLayout) viewParent;
- row.makeActionsVisibile();
- row.post(new Runnable() {
- @Override
- public void run() {
- final Runnable finishScrollingCallback = new Runnable() {
- @Override
- public void run() {
- mPendingWorkRemoteInputView.callOnClick();
- mPendingWorkRemoteInputView = null;
- scrollLayout.setFinishScrollingCallback(null);
- }
- };
- if (scrollLayout.scrollTo(row)) {
- // It scrolls! So call it when it's finished.
- scrollLayout.setFinishScrollingCallback(
- finishScrollingCallback);
- } else {
- // It does not scroll, so call it now!
- finishScrollingCallback.run();
- }
- }
- });
- }
+ });
}
};
mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener(
@@ -5236,7 +5065,7 @@
}
}
- WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
+ final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
@Override
public void onFinishedGoingToSleep() {
mNotificationPanel.onAffordanceLaunchEnded();
@@ -5259,12 +5088,7 @@
// This gets executed before we will show Keyguard, so post it in order that the state
// is correct.
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- onCameraLaunchGestureDetected(mLastCameraLaunchSource);
- }
- });
+ mHandler.post(() -> onCameraLaunchGestureDetected(mLastCameraLaunchSource));
}
updateIsKeyguard();
}
@@ -5290,7 +5114,7 @@
}
};
- ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
+ final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
public void onScreenTurningOn() {
mFalsingManager.onScreenTurningOn();
@@ -5487,7 +5311,7 @@
}
private final class DozeServiceHost implements DozeHost {
- private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+ private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private boolean mAnimateWakeup;
private boolean mIgnoreTouchWhilePulsing;
@@ -5700,7 +5524,7 @@
protected NotificationData mNotificationData;
protected NotificationStackScrollLayout mStackScroller;
- protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
+ protected final NotificationGroupManager mGroupManager = new NotificationGroupManager();
protected RemoteInputController mRemoteInputController;
@@ -5710,34 +5534,30 @@
private AboveShelfObserver mAboveShelfObserver;
// handling reordering
- protected VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager();
+ protected final VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager();
protected int mCurrentUserId = 0;
- final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
+ final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
- protected int mLayoutDirection = -1; // invalid
protected AccessibilityManager mAccessibilityManager;
protected boolean mDeviceInteractive;
protected boolean mVisible;
- protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
- protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
+ protected final ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
+ protected final ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
/**
* Notifications with keys in this set are not actually around anymore. We kept them around
* when they were canceled in response to a remote input interaction. This allows us to show
* what you replied and allows you to continue typing into it.
*/
- protected ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
+ protected final ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
// mScreenOnFromKeyguard && mVisible.
private boolean mVisibleToUser;
- private Locale mLocale;
-
protected boolean mUseHeadsUp = false;
- protected boolean mHeadsUpTicker = false;
protected boolean mDisableNotificationAlerts = false;
protected DevicePolicyManager mDevicePolicyManager;
@@ -5772,13 +5592,11 @@
private NotificationGuts mNotificationGutsExposed;
private MenuItem mGutsMenuItem;
- private KeyboardShortcuts mKeyboardShortcuts;
-
protected NotificationShelf mNotificationShelf;
protected DismissView mDismissView;
protected EmptyShadeView mEmptyShadeView;
- private NotificationClicker mNotificationClicker = new NotificationClicker();
+ private final NotificationClicker mNotificationClicker = new NotificationClicker();
protected AssistManager mAssistManager;
@@ -5838,15 +5656,14 @@
}
};
- private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
+ private final RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
@Override
public boolean onClickHandler(
final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
wakeUpIfDozing(SystemClock.uptimeMillis(), view);
-
- if (handleRemoteInput(view, pendingIntent, fillInIntent)) {
+ if (handleRemoteInput(view, pendingIntent)) {
return true;
}
@@ -5864,33 +5681,29 @@
}
final boolean isActivity = pendingIntent.isActivity();
if (isActivity) {
- final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
mContext, pendingIntent.getIntent(), mCurrentUserId);
- dismissKeyguardThenExecute(new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- try {
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
-
- boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
-
- // close the shade if it was open
- if (handled && !mNotificationPanel.isFullyCollapsed()) {
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */);
- visibilityChanged(false);
- mAssistManager.hideAssist();
-
- // Wait for activity start.
- return true;
- } else {
- return false;
- }
-
+ dismissKeyguardThenExecute(() -> {
+ try {
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
}
+
+ boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
+
+ // close the shade if it was open
+ if (handled && !mNotificationPanel.isFullyCollapsed()) {
+ animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
+ visibilityChanged(false);
+ mAssistManager.hideAssist();
+
+ // Wait for activity start.
+ return true;
+ } else {
+ return false;
+ }
+
}, afterKeyguardGone);
return true;
} else {
@@ -5935,7 +5748,12 @@
WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
}
- private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) {
+ private boolean handleRemoteInput(View view, PendingIntent pendingIntent) {
+ if ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+ // Skip remote input as doing so will expand the notification shade.
+ return true;
+ }
+
Object tag = view.getTag(com.android.internal.R.id.remote_input_tag);
RemoteInput[] inputs = null;
if (tag instanceof RemoteInput[]) {
@@ -6050,7 +5868,7 @@
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
updateCurrentProfilesCache();
- if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
+ Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
updateLockscreenNotificationSetting();
@@ -6073,8 +5891,7 @@
Toast toast = Toast.makeText(mContext,
R.string.managed_profile_foreground_toast,
Toast.LENGTH_SHORT);
- TextView text = (TextView) toast.getView().findViewById(
- android.R.id.message);
+ TextView text = toast.getView().findViewById(android.R.id.message);
text.setCompoundDrawablesRelativeWithIntrinsicBounds(
R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
int paddingPx = mContext.getResources().getDimensionPixelSize(
@@ -6150,15 +5967,12 @@
return;
}
final RankingMap currentRanking = getCurrentRanking();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- for (StatusBarNotification sbn : notifications) {
- try {
- addNotification(sbn, currentRanking);
- } catch (InflationException e) {
- handleInflationException(sbn, e);
- }
+ mHandler.post(() -> {
+ for (StatusBarNotification sbn : notifications) {
+ try {
+ addNotification(sbn, currentRanking);
+ } catch (InflationException e) {
+ handleInflationException(sbn, e);
}
}
});
@@ -6169,40 +5983,37 @@
final RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- processForRemoteInput(sbn.getNotification());
- String key = sbn.getKey();
- mKeysKeptForRemoteInput.remove(key);
- boolean isUpdate = mNotificationData.get(key) != null;
- // In case we don't allow child notifications, we ignore children of
- // notifications that have a summary, since we're not going to show them
- // anyway. This is true also when the summary is canceled,
- // because children are automatically canceled by NoMan in that case.
- if (!ENABLE_CHILD_NOTIFICATIONS
+ mHandler.post(() -> {
+ processForRemoteInput(sbn.getNotification());
+ String key = sbn.getKey();
+ mKeysKeptForRemoteInput.remove(key);
+ boolean isUpdate = mNotificationData.get(key) != null;
+ // In case we don't allow child notifications, we ignore children of
+ // notifications that have a summary, since we're not going to show them
+ // anyway. This is true also when the summary is canceled,
+ // because children are automatically canceled by NoMan in that case.
+ if (!ENABLE_CHILD_NOTIFICATIONS
&& mGroupManager.isChildInGroupWithSummary(sbn)) {
- if (DEBUG) {
- Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
- }
+ if (DEBUG) {
+ Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
+ }
- // Remove existing notification to avoid stale data.
- if (isUpdate) {
- removeNotification(key, rankingMap);
- } else {
- mNotificationData.updateRanking(rankingMap);
- }
- return;
+ // Remove existing notification to avoid stale data.
+ if (isUpdate) {
+ removeNotification(key, rankingMap);
+ } else {
+ mNotificationData.updateRanking(rankingMap);
}
- try {
- if (isUpdate) {
- updateNotification(sbn, rankingMap);
- } else {
- addNotification(sbn, rankingMap);
- }
- } catch (InflationException e) {
- handleInflationException(sbn, e);
+ return;
+ }
+ try {
+ if (isUpdate) {
+ updateNotification(sbn, rankingMap);
+ } else {
+ addNotification(sbn, rankingMap);
}
+ } catch (InflationException e) {
+ handleInflationException(sbn, e);
}
});
}
@@ -6250,7 +6061,7 @@
Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
return;
}
- Log.d(TAG, "disabling lockecreen notifications and alerting the user");
+ Log.d(TAG, "disabling lockscreen notifications and alerting the user");
// disable lockscreen notifications until user acts on the banner.
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
@@ -6291,11 +6102,10 @@
@Override // NotificationData.Environment
public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
- final int thisUserId = mCurrentUserId;
final int notificationUserId = n.getUserId();
if (DEBUG && MULTIUSER_DEBUG) {
- Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
- n, thisUserId, notificationUserId));
+ Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d", n,
+ mCurrentUserId, notificationUserId));
}
return isCurrentProfile(notificationUserId);
}
@@ -6343,21 +6153,15 @@
}
private void startNotificationGutsIntent(final Intent intent, final int appUid) {
- dismissKeyguardThenExecute(new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- TaskStackBuilder.create(mContext)
- .addNextIntentWithParentStack(intent)
- .startActivities(getActivityOptions(),
- new UserHandle(UserHandle.getUserId(appUid)));
- }
- });
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
- return true;
- }
+ dismissKeyguardThenExecute(() -> {
+ AsyncTask.execute(() -> {
+ TaskStackBuilder.create(mContext)
+ .addNextIntentWithParentStack(intent)
+ .startActivities(getActivityOptions(),
+ new UserHandle(UserHandle.getUserId(appUid)));
+ });
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
+ return true;
}, false /* afterKeyguardGone */);
}
@@ -6428,7 +6232,7 @@
startNotificationGutsIntent(intent, sbn.getUid());
};
final View.OnClickListener onDoneClick = (View v) -> {
- saveAndCloseNotificationMenu(info, row, guts, v);
+ saveAndCloseNotificationMenu(row, guts, v);
};
final NotificationInfo.CheckSaveListener checkSaveListener =
(Runnable saveImportance) -> {
@@ -6445,7 +6249,7 @@
}
};
- ArraySet<NotificationChannel> channels = new ArraySet<NotificationChannel>();
+ ArraySet<NotificationChannel> channels = new ArraySet<>();
channels.add(row.getEntry().channel);
if (row.isSummaryWithChildren()) {
// If this is a summary, then add in the children notification channels for the
@@ -6473,7 +6277,7 @@
}
}
- private void saveAndCloseNotificationMenu(NotificationInfo info,
+ private void saveAndCloseNotificationMenu(
ExpandableNotificationRow row, NotificationGuts guts, View done) {
guts.resetFalsingCheck();
int[] rowLocation = new int[2];
@@ -6642,13 +6446,6 @@
updateHideIconsForBouncer(true /* animate */);
}
- protected void sendCloseSystemWindows(String reason) {
- try {
- ActivityManager.getService().closeSystemDialogs(reason);
- } catch (RemoteException e) {
- }
- }
-
protected void toggleKeyboardShortcuts(int deviceId) {
KeyboardShortcuts.toggle(mContext, deviceId);
}
@@ -6750,18 +6547,6 @@
return isLockscreenPublicMode(userId);
}
- public void onNotificationClear(StatusBarNotification notification) {
- try {
- mBarService.onNotificationClear(
- notification.getPackageName(),
- notification.getTag(),
- notification.getId(),
- notification.getUserId());
- } catch (android.os.RemoteException ex) {
- // oh well
- }
- }
-
/**
* Called when the notification panel layouts
*/
@@ -6919,49 +6704,42 @@
public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
if (!isDeviceProvisioned()) return;
- final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
final boolean afterKeyguardGone = intent.isActivity()
&& PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
mCurrentUserId);
- dismissKeyguardThenExecute(new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- new Thread() {
- @Override
- public void run() {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- try {
- intent.send(null, 0, null, null, null, null, getActivityOptions());
- } catch (PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here.
- // Just log the exception message.
- Log.w(TAG, "Sending intent failed: " + e);
-
- // TODO: Dismiss Keyguard.
- }
- if (intent.isActivity()) {
- mAssistManager.hideAssist();
- }
- }
- }.start();
-
- if (!mNotificationPanel.isFullyCollapsed()) {
- // close the shade if it was open
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */, true /* delayed */);
- visibilityChanged(false);
-
- return true;
- } else {
- return false;
+ dismissKeyguardThenExecute(() -> {
+ new Thread(() -> {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
}
+ try {
+ intent.send(null, 0, null, null, null, null, getActivityOptions());
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here.
+ // Just log the exception message.
+ Log.w(TAG, "Sending intent failed: " + e);
+
+ // TODO: Dismiss Keyguard.
+ }
+ if (intent.isActivity()) {
+ mAssistManager.hideAssist();
+ }
+ }).start();
+
+ if (!mNotificationPanel.isFullyCollapsed()) {
+ // close the shade if it was open
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
+ true /* delayed */);
+ visibilityChanged(false);
+
+ return true;
+ } else {
+ return false;
}
}, afterKeyguardGone);
}
@@ -6999,130 +6777,110 @@
// Mark notification for one frame.
row.setJustClicked(true);
- DejankUtils.postAfterTraversal(new Runnable() {
- @Override
- public void run() {
- row.setJustClicked(false);
- }
- });
+ DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
final boolean afterKeyguardGone = intent.isActivity()
&& PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
mCurrentUserId);
- dismissKeyguardThenExecute(new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
- // Release the HUN notification to the shade.
+ dismissKeyguardThenExecute(() -> {
+ if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
+ // Release the HUN notification to the shade.
- if (isPanelFullyCollapsed()) {
- HeadsUpManager.setIsClickedNotification(row, true);
- }
- //
- // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
- // become canceled shortly by NoMan, but we can't assume that.
- mHeadsUpManager.releaseImmediately(notificationKey);
+ if (isPanelFullyCollapsed()) {
+ HeadsUpManager.setIsClickedNotification(row, true);
}
- StatusBarNotification parentToCancel = null;
- if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
- StatusBarNotification summarySbn = mGroupManager.getLogicalGroupSummary(sbn)
- .getStatusBarNotification();
- if (shouldAutoCancel(summarySbn)) {
- parentToCancel = summarySbn;
- }
+ //
+ // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
+ // become canceled shortly by NoMan, but we can't assume that.
+ mHeadsUpManager.releaseImmediately(notificationKey);
+ }
+ StatusBarNotification parentToCancel = null;
+ if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
+ StatusBarNotification summarySbn =
+ mGroupManager.getLogicalGroupSummary(sbn).getStatusBarNotification();
+ if (shouldAutoCancel(summarySbn)) {
+ parentToCancel = summarySbn;
}
- final StatusBarNotification parentToCancelFinal = parentToCancel;
- final Runnable runnable = new Runnable() {
- @Override
- public void run() {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- if (intent != null) {
- // If we are launching a work activity and require to launch
- // separate work challenge, we defer the activity action and cancel
- // notification until work challenge is unlocked.
- if (intent.isActivity()) {
- final int userId = intent.getCreatorUserHandle()
- .getIdentifier();
- if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
- && mKeyguardManager.isDeviceLocked(userId)) {
- // TODO(b/28935539): should allow certain activities to
- // bypass work challenge
- if (startWorkChallengeIfNecessary(userId,
- intent.getIntentSender(), notificationKey)) {
- // Show work challenge, do not run PendingIntent and
- // remove notification
- return;
- }
- }
- }
- try {
- intent.send(null, 0, null, null, null, null,
- getActivityOptions());
- } catch (PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here.
- // Just log the exception message.
- Log.w(TAG, "Sending contentIntent failed: " + e);
-
- // TODO: Dismiss Keyguard.
- }
- if (intent.isActivity()) {
- mAssistManager.hideAssist();
+ }
+ final StatusBarNotification parentToCancelFinal = parentToCancel;
+ final Runnable runnable = () -> {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+ if (intent != null) {
+ // If we are launching a work activity and require to launch
+ // separate work challenge, we defer the activity action and cancel
+ // notification until work challenge is unlocked.
+ if (intent.isActivity()) {
+ final int userId = intent.getCreatorUserHandle().getIdentifier();
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
+ && mKeyguardManager.isDeviceLocked(userId)) {
+ // TODO(b/28935539): should allow certain activities to
+ // bypass work challenge
+ if (startWorkChallengeIfNecessary(userId, intent.getIntentSender(),
+ notificationKey)) {
+ // Show work challenge, do not run PendingIntent and
+ // remove notification
+ return;
}
}
-
- try {
- mBarService.onNotificationClick(notificationKey);
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
- if (parentToCancelFinal != null) {
- // We have to post it to the UI thread for synchronization
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- Runnable removeRunnable = new Runnable() {
- @Override
- public void run() {
- performRemoveNotification(parentToCancelFinal);
- }
- };
- if (isCollapsing()) {
- // To avoid lags we're only performing the remove
- // after the shade was collapsed
- addPostCollapseAction(removeRunnable);
- } else {
- removeRunnable.run();
- }
- }
- });
- }
}
- };
+ try {
+ intent.send(null, 0, null, null, null, null, getActivityOptions());
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here.
+ // Just log the exception message.
+ Log.w(TAG, "Sending contentIntent failed: " + e);
- if (mStatusBarKeyguardViewManager.isShowing()
- && mStatusBarKeyguardViewManager.isOccluded()) {
- mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
- } else {
- new Thread(runnable).start();
+ // TODO: Dismiss Keyguard.
+ }
+ if (intent.isActivity()) {
+ mAssistManager.hideAssist();
+ }
}
- if (!mNotificationPanel.isFullyCollapsed()) {
- // close the shade if it was open
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */, true /* delayed */);
- visibilityChanged(false);
-
- return true;
- } else {
- return false;
+ try {
+ mBarService.onNotificationClick(notificationKey);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
}
+ if (parentToCancelFinal != null) {
+ // We have to post it to the UI thread for synchronization
+ mHandler.post(() -> {
+ Runnable removeRunnable =
+ () -> performRemoveNotification(parentToCancelFinal);
+ if (isCollapsing()) {
+ // To avoid lags we're only performing the remove
+ // after the shade was collapsed
+ addPostCollapseAction(removeRunnable);
+ } else {
+ removeRunnable.run();
+ }
+ });
+ }
+ };
+
+ if (mStatusBarKeyguardViewManager.isShowing()
+ && mStatusBarKeyguardViewManager.isOccluded()) {
+ mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
+ } else {
+ new Thread(runnable).start();
+ }
+
+ if (!mNotificationPanel.isFullyCollapsed()) {
+ // close the shade if it was open
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
+ true /* delayed */);
+ visibilityChanged(false);
+
+ return true;
+ } else {
+ return false;
}
}, afterKeyguardGone);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index c240765..bcda60e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -14,10 +14,10 @@
package com.android.systemui.statusbar.phone;
-import android.annotation.ColorInt;
+import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
+import static android.app.StatusBarManager.DISABLE_NONE;
+
import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -29,11 +29,11 @@
import android.widget.LinearLayout.LayoutParams;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.util.Utils.DisableStateTracker;
public interface StatusBarIconController {
@@ -149,6 +149,14 @@
mContext = group.getContext();
mIconSize = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_icon_size);
+
+ DisableStateTracker tracker =
+ new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS);
+ mGroup.addOnAttachStateChangeListener(tracker);
+ if (mGroup.isAttachedToWindow()) {
+ // In case we miss the first onAttachedToWindow event
+ tracker.onViewAttachedToWindow(mGroup);
+ }
}
protected void onIconAdded(int index, String slot, boolean blocked,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 68f8e06..1c3ee75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -33,7 +33,6 @@
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.IconLogger;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -50,21 +49,17 @@
public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,
ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {
- private final DarkIconDispatcher mDarkIconDispatcher;
+ private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
+ private final ArraySet<String> mIconBlacklist = new ArraySet<>();
+ private final IconLogger mIconLogger = Dependency.get(IconLogger.class);
private Context mContext;
private DemoStatusIcons mDemoStatusIcons;
- private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
-
- private final ArraySet<String> mIconBlacklist = new ArraySet<>();
- private final IconLogger mIconLogger = Dependency.get(IconLogger.class);
-
public StatusBarIconControllerImpl(Context context) {
super(context.getResources().getStringArray(
com.android.internal.R.array.config_statusBarIcons));
Dependency.get(ConfigurationController.class).addCallback(this);
- mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
mContext = context;
loadDimens();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index bbce751..09828dcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -225,7 +225,6 @@
if (mShowing) {
if (mOccluded && !mDozing) {
mStatusBar.hideKeyguard();
- mStatusBar.stopWaitingForKeyguardExit();
if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) {
hideBouncer(false /* destroyView */);
}
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 c0a6837..0d21c4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager.ActionListener;
-import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -59,13 +58,19 @@
private int mCurrentUser;
- public AccessPointControllerImpl(Context context, Looper bgLooper) {
+ public AccessPointControllerImpl(Context context) {
mContext = context;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mWifiTracker = new WifiTracker(context, this, bgLooper, false, true);
+ mWifiTracker = new WifiTracker(context, this, false, true);
mCurrentUser = ActivityManager.getCurrentUser();
}
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ mWifiTracker.onDestroy();
+ }
+
public boolean canConfigWifi() {
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI,
new UserHandle(mCurrentUser));
@@ -81,7 +86,7 @@
if (DEBUG) Log.d(TAG, "addCallback " + callback);
mCallbacks.add(callback);
if (mCallbacks.size() == 1) {
- mWifiTracker.startTracking();
+ mWifiTracker.onStart();
}
}
@@ -91,7 +96,7 @@
if (DEBUG) Log.d(TAG, "removeCallback " + callback);
mCallbacks.remove(callback);
if (mCallbacks.isEmpty()) {
- mWifiTracker.stopTracking();
+ mWifiTracker.onStop();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index a456786..5159e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -71,7 +71,7 @@
break;
case MSG_NO_SIM_VISIBLE_CHANGED:
for (SignalCallback signalCluster : mSignalCallbacks) {
- signalCluster.setNoSims(msg.arg1 != 0);
+ signalCluster.setNoSims(msg.arg1 != 0, msg.arg2 != 0);
}
break;
case MSG_ETHERNET_CHANGED:
@@ -144,8 +144,8 @@
}
@Override
- public void setNoSims(boolean show) {
- obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, 0).sendToTarget();
+ public void setNoSims(boolean show, boolean simDetected) {
+ obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, simDetected ? 1 : 0).sendToTarget();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 2771011..9eee906 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -52,7 +52,7 @@
int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
String description, boolean isWide, int subId, boolean roaming) {}
default void setSubs(List<SubscriptionInfo> subs) {}
- default void setNoSims(boolean show) {}
+ default void setNoSims(boolean show, boolean simDetected) {}
default void setEthernetIndicators(IconState icon) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index c217bda..d24e51c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -58,10 +58,8 @@
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.List;
import java.util.Locale;
-import java.util.Map;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
@@ -116,7 +114,7 @@
// States that don't belong to a subcontroller.
private boolean mAirplaneMode = false;
- private boolean mHasNoSims;
+ private boolean mHasNoSubs;
private Locale mLocale = null;
// This list holds our ordering.
private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>();
@@ -140,6 +138,7 @@
@VisibleForTesting
ServiceState mLastServiceState;
private boolean mUserSetup;
+ private boolean mSimDetected;
/**
* Construct this controller object and register for updates.
@@ -151,7 +150,7 @@
(WifiManager) context.getSystemService(Context.WIFI_SERVICE),
SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
new CallbackHandler(),
- new AccessPointControllerImpl(context, bgLooper),
+ new AccessPointControllerImpl(context),
new DataUsageController(context),
new SubscriptionDefaults(),
deviceProvisionedController);
@@ -363,7 +362,7 @@
cb.setSubs(mCurrentSubscriptions);
cb.setIsAirplaneMode(new IconState(mAirplaneMode,
TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
- cb.setNoSims(mHasNoSims);
+ cb.setNoSims(mHasNoSubs, mSimDetected);
mWifiSignalController.notifyListeners(cb);
mEthernetSignalController.notifyListeners(cb);
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
@@ -498,13 +497,27 @@
@VisibleForTesting
protected void updateNoSims() {
- boolean hasNoSims = mHasMobileDataFeature && mMobileSignalControllers.size() == 0;
- if (hasNoSims != mHasNoSims) {
- mHasNoSims = hasNoSims;
- mCallbackHandler.setNoSims(mHasNoSims);
+ boolean hasNoSubs = mHasMobileDataFeature && mMobileSignalControllers.size() == 0;
+ boolean simDetected = hasAnySim();
+ if (hasNoSubs != mHasNoSubs || simDetected != mSimDetected) {
+ mHasNoSubs = hasNoSubs;
+ mSimDetected = simDetected;
+ mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
}
}
+ private boolean hasAnySim() {
+ int simCount = mPhone.getSimCount();
+ for (int i = 0; i < simCount; i++) {
+ int state = mPhone.getSimState(i);
+ if (state != TelephonyManager.SIM_STATE_ABSENT
+ && state != TelephonyManager.SIM_STATE_UNKNOWN) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@VisibleForTesting
void setCurrentSubscriptions(List<SubscriptionInfo> subscriptions) {
Collections.sort(subscriptions, new Comparator<SubscriptionInfo>() {
@@ -631,7 +644,7 @@
private void notifyListeners() {
mCallbackHandler.setIsAirplaneMode(new IconState(mAirplaneMode,
TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
- mCallbackHandler.setNoSims(mHasNoSims);
+ mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
}
/**
@@ -804,6 +817,10 @@
} else {
mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_NONE);
}
+ String ssid = args.getString("ssid");
+ if (ssid != null) {
+ mDemoWifiState.ssid = ssid;
+ }
mDemoWifiState.enabled = show;
mWifiSignalController.notifyListeners();
}
@@ -822,8 +839,8 @@
}
String nosim = args.getString("nosim");
if (nosim != null) {
- mHasNoSims = nosim.equals("show");
- mCallbackHandler.setNoSims(mHasNoSims);
+ mHasNoSubs = nosim.equals("show");
+ mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
}
String mobile = args.getString("mobile");
if (mobile != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index f4aebae..eca6127 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -14,6 +14,11 @@
package com.android.systemui.util;
+import android.view.View;
+
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.statusbar.CommandQueue;
+
import java.util.List;
import java.util.function.Consumer;
@@ -28,4 +33,52 @@
c.accept(list.get(i));
}
}
+
+ /**
+ * Sets the visibility of an UI element according to the DISABLE_* flags in
+ * {@link android.app.StatusBarManager}.
+ */
+ public static class DisableStateTracker implements CommandQueue.Callbacks,
+ View.OnAttachStateChangeListener {
+ private final int mMask1;
+ private final int mMask2;
+ private View mView;
+ private boolean mDisabled;
+
+ public DisableStateTracker(int disableMask, int disable2Mask) {
+ mMask1 = disableMask;
+ mMask2 = disable2Mask;
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ mView = v;
+ SysUiServiceProvider.getComponent(v.getContext(), CommandQueue.class)
+ .addCallbacks(this);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ SysUiServiceProvider.getComponent(mView.getContext(), CommandQueue.class)
+ .removeCallbacks(this);
+ mView = null;
+ }
+
+ /**
+ * Sets visibility of this {@link View} given the states passed from
+ * {@link com.android.systemui.statusbar.CommandQueue.Callbacks#disable(int, int)}.
+ */
+ @Override
+ public void disable(int state1, int state2, boolean animate) {
+ final boolean disabled = ((state1 & mMask1) != 0) || ((state2 & mMask2) != 0);
+ if (disabled == mDisabled) return;
+ mDisabled = disabled;
+ mView.setVisibility(disabled ? View.GONE : View.VISIBLE);
+ }
+
+ /** @return {@code true} if and only if this {@link View} is currently disabled */
+ public boolean isDisabled() {
+ return mDisabled;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index a3aca6e..7bb987c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -524,18 +524,17 @@
bindGenericCountdown();
bindNextAlarm(getTimeUntilNextAlarmCondition());
} else if (isForever(c)) {
+
getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
bindGenericCountdown();
bindNextAlarm(getTimeUntilNextAlarmCondition());
} else {
if (isAlarm(c)) {
bindGenericCountdown();
-
bindNextAlarm(c);
getConditionTagAt(COUNTDOWN_ALARM_CONDITION_INDEX).rb.setChecked(true);
} else if (isCountdown(c)) {
bindNextAlarm(getTimeUntilNextAlarmCondition());
-
bind(c, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
COUNTDOWN_CONDITION_INDEX);
getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
@@ -568,8 +567,8 @@
tag = (ConditionTag) alarmContent.getTag();
boolean showAlarm = tag != null && tag.condition != null;
mZenRadioGroup.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(
- showAlarm ? View.VISIBLE : View.GONE);
- alarmContent.setVisibility(showAlarm ? View.VISIBLE : View.GONE);
+ showAlarm ? View.VISIBLE : View.INVISIBLE);
+ alarmContent.setVisibility(showAlarm ? View.VISIBLE : View.INVISIBLE);
}
private Condition forever() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AlphaControlledSignalTileViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AlphaControlledSignalTileViewTest.java
new file mode 100644
index 0000000..3e677c0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AlphaControlledSignalTileViewTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 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.systemui.qs;
+
+
+import static org.junit.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import android.test.suitebuilder.annotation.SmallTest;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.qs.AlphaControlledSignalTileView.AlphaControlledSlashDrawable;
+import com.android.systemui.qs.AlphaControlledSignalTileView.AlphaControlledSlashImageView;
+import org.junit.Test;
+
+@SmallTest
+public class AlphaControlledSignalTileViewTest extends SysuiTestCase {
+
+ private AlphaControlledSignalTileView mTileView;
+
+ @Test
+ public void testTileView_createsAlphaControlledSlashImageView() {
+ mTileView = new AlphaControlledSignalTileView(mContext);
+
+ assertTrue(mTileView.createSlashImageView(mContext)
+ instanceof AlphaControlledSlashImageView);
+ }
+
+ /// AlphaControlledSlashImageView tests
+ @Test
+ public void testSlashImageView_createsAlphaControlledSlashDrawable() {
+ TestableSlashImageView iv = new TestableSlashImageView(mContext);
+
+ iv.ensureSlashDrawable();
+ assertTrue(iv.getSlashDrawable() instanceof AlphaControlledSlashDrawable);
+ }
+
+ /// AlphaControlledSlashDrawable tests
+ @Test
+ public void testSlashDrawable_doesNotSetTintList() {
+ Drawable mockDrawable = mock(Drawable.class);
+ AlphaControlledSlashDrawable drawable = new AlphaControlledSlashDrawable(mockDrawable);
+ ColorStateList list = ColorStateList.valueOf(0xffffff);
+ drawable.setTintList(list);
+ verify(mockDrawable, never()).setTintList(any());
+ }
+
+ @Test
+ public void testSlashDrawable_setsFinalTintList() {
+ Drawable mockDrawable = mock(Drawable.class);
+ AlphaControlledSlashDrawable drawable = new AlphaControlledSlashDrawable(mockDrawable);
+ ColorStateList list = ColorStateList.valueOf(0xffffff);
+ drawable.setFinalTintList(list);
+ verify(mockDrawable, atLeastOnce()).setTintList(list);
+ }
+
+ // Expose getSlashDrawable
+ private static class TestableSlashImageView extends AlphaControlledSlashImageView {
+ TestableSlashImageView(Context c) {
+ super(c);
+ }
+
+ private SlashDrawable getSlashDrawable() {
+ return mSlash;
+ }
+
+ @Override
+ protected void setSlash(SlashDrawable slash) {
+ super.setSlash(slash);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
index 4f87b02..e023e87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
@@ -14,6 +14,7 @@
package com.android.systemui.qs.car;
import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
import android.content.Context;
import android.support.test.filters.SmallTest;
@@ -27,6 +28,7 @@
import com.android.keyguard.CarrierText;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.Clock;
import org.junit.Before;
@@ -54,7 +56,7 @@
.replace(CarrierText.class, View.class)
.replace(Clock.class, View.class)
.build());
-
+ mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
mDependency.injectTestDependency(Dependency.BG_LOOPER,
TestableLooper.get(this).getLooper());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
new file mode 100644
index 0000000..6b31c96
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2017 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.systemui.statusbar;
+
+import android.provider.Settings;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableResources;
+import android.testing.UiThreadTest;
+import android.util.KeyValueListParser;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@UiThreadTest
+public class NotificationSnoozeTest extends SysuiTestCase {
+ private static final int RES_DEFAULT = 2;
+ private static final int[] RES_OPTIONS = {1, 2, 3};
+ private NotificationSnooze mNotificationSnooze;
+ private KeyValueListParser mMockParser;
+
+ @Before
+ public void setUp() throws Exception {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.NOTIFICATION_SNOOZE_OPTIONS, null);
+ TestableResources resources = mContext.getOrCreateTestableResources();
+ resources.addOverride(R.integer.config_notification_snooze_time_default, RES_DEFAULT);
+ resources.addOverride(R.array.config_notification_snooze_times, RES_OPTIONS);
+ mNotificationSnooze = new NotificationSnooze(mContext, null);
+ mMockParser = mock(KeyValueListParser.class);
+ }
+
+ @Test
+ public void testParseIntArrayNull() throws Exception {
+ when(mMockParser.getString(anyString(), isNull())).thenReturn(null);
+ mNotificationSnooze.setKeyValueListParser(mMockParser);
+
+ int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
+ assertEquals(RES_OPTIONS, result);
+ }
+
+ @Test
+ public void testParseIntArrayLeadingSep() throws Exception {
+ when(mMockParser.getString(anyString(), isNull())).thenReturn(":4:5:6");
+ mNotificationSnooze.setKeyValueListParser(mMockParser);
+
+ int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
+ assertEquals(RES_OPTIONS, result);
+ }
+
+ @Test
+ public void testParseIntArrayEmptyItem() throws Exception {
+ when(mMockParser.getString(anyString(), isNull())).thenReturn("4::6");
+ mNotificationSnooze.setKeyValueListParser(mMockParser);
+
+ int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
+ assertEquals(RES_OPTIONS, result);
+ }
+
+ @Test
+ public void testParseIntArrayTrailingSep() throws Exception {
+ when(mMockParser.getString(anyString(), isNull())).thenReturn("4:5:6:");
+ mNotificationSnooze.setKeyValueListParser(mMockParser);
+
+ int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
+ assertEquals(3, result.length);
+ assertEquals(4, result[0]); // respect order
+ assertEquals(5, result[1]);
+ assertEquals(6, result[2]);
+ }
+
+ @Test
+ public void testParseIntArrayGoodData() throws Exception {
+ when(mMockParser.getString(anyString(), isNull())).thenReturn("4:5:6");
+ mNotificationSnooze.setKeyValueListParser(mMockParser);
+
+ int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
+ assertEquals(3, result.length);
+ assertEquals(4, result[0]); // respect order
+ assertEquals(5, result[1]);
+ assertEquals(6, result[2]);
+ }
+
+ @Test
+ public void testGetOptionsWithNoConfig() throws Exception {
+ ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+ assertEquals(3, result.size());
+ assertEquals(1, result.get(0).getMinutesToSnoozeFor()); // respect order
+ assertEquals(2, result.get(1).getMinutesToSnoozeFor());
+ assertEquals(3, result.get(2).getMinutesToSnoozeFor());
+ assertEquals(2, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+ }
+
+ @Test
+ public void testGetOptionsWithInvalidConfig() throws Exception {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
+ "this is garbage");
+ ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+ assertEquals(3, result.size());
+ assertEquals(1, result.get(0).getMinutesToSnoozeFor()); // respect order
+ assertEquals(2, result.get(1).getMinutesToSnoozeFor());
+ assertEquals(3, result.get(2).getMinutesToSnoozeFor());
+ assertEquals(2, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+ }
+
+ @Test
+ public void testGetOptionsWithValidDefault() throws Exception {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
+ "default=10,options_array=4:5:6:7");
+ ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+ assertNotNull(mNotificationSnooze.getDefaultOption()); // pick one
+ }
+
+ @Test
+ public void testGetOptionsWithValidConfig() throws Exception {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
+ "default=6,options_array=4:5:6:7");
+ ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+ assertEquals(4, result.size());
+ assertEquals(4, result.get(0).getMinutesToSnoozeFor()); // respect order
+ assertEquals(5, result.get(1).getMinutesToSnoozeFor());
+ assertEquals(6, result.get(2).getMinutesToSnoozeFor());
+ assertEquals(7, result.get(3).getMinutesToSnoozeFor());
+ assertEquals(6, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+ }
+
+ @Test
+ public void testGetOptionsWithLongConfig() throws Exception {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
+ "default=6,options_array=4:5:6:7:8:9:10:11:12:13:14:15:16:17");
+ ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+ assertTrue(result.size() > 3);
+ assertEquals(4, result.get(0).getMinutesToSnoozeFor()); // respect order
+ assertEquals(5, result.get(1).getMinutesToSnoozeFor());
+ assertEquals(6, result.get(2).getMinutesToSnoozeFor());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index c0de004..3b401a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -21,9 +21,11 @@
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import com.android.internal.app.NightDisplayController;
+import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
import com.android.systemui.SysuiTestCase;
@@ -45,6 +47,8 @@
@Before
public void setUp() throws Exception {
+ mDependency.injectTestDependency(Dependency.BG_LOOPER,
+ TestableLooper.get(this).getLooper());
Prefs.putBoolean(mContext, Key.QS_NIGHTDISPLAY_ADDED, false);
mQsTileHost = Mockito.mock(QSTileHost.class);
mAutoTileManager = new AutoTileManager(mContext, mQsTileHost);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index ac367d2..899e873 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -34,8 +34,8 @@
import static org.mockito.Mockito.when;
import android.app.Notification;
+import android.app.StatusBarManager;
import android.app.trust.TrustManager;
-import android.content.Context;
import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
import android.os.Binder;
@@ -50,21 +50,17 @@
import android.support.test.filters.SmallTest;
import android.support.test.metricshelper.MetricsAsserts;
import android.testing.AndroidTestingRunner;
-import android.testing.LayoutInflaterBuilder;
import android.testing.TestableLooper;
import android.testing.TestableLooper.MessageHandler;
import android.testing.TestableLooper.RunWithLooper;
import android.util.DisplayMetrics;
-import android.view.View;
import android.view.ViewGroup.LayoutParams;
-import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.keyguard.KeyguardHostView.OnDismissAction;
-import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
@@ -76,7 +72,6 @@
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.policy.DateView;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -496,6 +491,28 @@
}
@Test
+ public void testDisableExpandStatusBar() {
+ mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+ mStatusBar.setUserSetupForTest(true);
+ when(mStatusBar.isDeviceProvisioned()).thenReturn(true);
+
+ mStatusBar.disable(StatusBarManager.DISABLE_NONE,
+ StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
+ verify(mNotificationPanelView).setQsExpansionEnabled(false);
+ mStatusBar.animateExpandNotificationsPanel();
+ verify(mNotificationPanelView, never()).expand(anyBoolean());
+ mStatusBar.animateExpandSettingsPanel(null);
+ verify(mNotificationPanelView, never()).expand(anyBoolean());
+
+ mStatusBar.disable(StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NONE, false);
+ verify(mNotificationPanelView).setQsExpansionEnabled(true);
+ mStatusBar.animateExpandNotificationsPanel();
+ verify(mNotificationPanelView).expand(anyBoolean());
+ mStatusBar.animateExpandSettingsPanel(null);
+ verify(mNotificationPanelView).expand(anyBoolean());
+ }
+
+ @Test
public void testDump_DoesNotCrash() {
mStatusBar.dump(null, new PrintWriter(new ByteArrayOutputStream()), null);
}
@@ -546,5 +563,9 @@
public void setBarStateForTest(int state) {
mState = state;
}
+
+ public void setUserSetupForTest(boolean userSetup) {
+ mUserSetup = userSetup;
+ }
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index 51bd7bc..e3558d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -161,12 +161,11 @@
@Test
public void testSignalCallback_setNoSims() {
boolean noSims = true;
- mHandler.setNoSims(noSims);
+ boolean simDetected = false;
+ mHandler.setNoSims(noSims, simDetected);
waitForCallbacks();
- ArgumentCaptor<Boolean> noSimsArg = ArgumentCaptor.forClass(Boolean.class);
- Mockito.verify(mSignalCallback).setNoSims(noSimsArg.capture());
- assertEquals(noSims, (boolean) noSimsArg.getValue());
+ Mockito.verify(mSignalCallback).setNoSims(eq(noSims), eq(simDetected));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index b7e6a40..f685b1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -292,10 +292,8 @@
}
protected void verifyHasNoSims(boolean hasNoSimsVisible) {
- ArgumentCaptor<Boolean> hasNoSimsArg = ArgumentCaptor.forClass(Boolean.class);
-
- Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setNoSims(hasNoSimsArg.capture());
- assertEquals("No sims", hasNoSimsVisible, (boolean) hasNoSimsArg.getValue());
+ Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setNoSims(
+ eq(hasNoSimsVisible), eq(false));
}
protected void verifyLastQsMobileDataIndicators(boolean visible, int icon, int typeIcon,
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 523e6b2..e17a6a6 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3851,7 +3851,7 @@
// Tag of a field for the length of the filter text
FIELD_AUTOFILL_FILTERTEXT_LEN = 911;
- // An autofill authentification succeeded
+ // An autofill authentication succeeded
// Package: Package of app that was autofilled
AUTOFILL_AUTHENTICATED = 912;
@@ -4462,19 +4462,19 @@
// OS: O MR
FIELD_AUTOFILL_PREVIOUS_LENGTH = 1125;
- // An autofill dataset authentification succeeded
+ // An autofill dataset authentication succeeded
// Package: Package of app that was autofilled
// OS: O MR
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
AUTOFILL_DATASET_AUTHENTICATED = 1126;
- // An autofill service provided an invalid dataset authentification
+ // An autofill service provided an invalid dataset authentication
// Package: Package of app that was autofilled
// OS: O MR
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
AUTOFILL_INVALID_DATASET_AUTHENTICATION = 1127;
- // An autofill service provided an invalid authentification extra
+ // An autofill service provided an invalid authentication extra
// Package: Package of app that was autofilled
// OS: O MR
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
@@ -4527,93 +4527,143 @@
// Type TYPE_FAILURE: An invalid opperation was reported by the app's AutofillManager
AUTOFILL_PENDING_SAVE_UI_OPERATION = 1134;
+ // Autofill service called API that disables itself
+ // Package: Package of the autofill service
+ // OS: O MR
+ AUTOFILL_SERVICE_DISABLED_SELF = 1135;
+
+ // Counter showing how long it took (in ms) to show the autofill UI after a field was focused
+ // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ // Package: Package of the autofill service
+ // OS: O MR
+ AUTOFILL_UI_LATENCY = 1136;
+
+ // Action: the snooze leave-behind was shown after the user clicked the snooze icon
+ // OS: O MR
+ NOTIFICATION_SNOOZE_CLICKED = 1137;
+
+ // Action: user selected a notification snooze duration from the drop down
+ // OS: O MR
+ NOTIFICATION_SELECT_SNOOZE = 1138;
+
+ // attached to NOTIFICATION_SNOOZED and NOTIFICATION_SELECT_SNOOZE events
+ // OS: O MR
+ FIELD_NOTIFICATION_SNOOZE_DURATION_MS = 1139;
+
+ // attached to NOTIFICATION_SELECT_SNOOZE events to indicate the option selected
+ // OS: O MR
+ FIELD_NOTIFICATION_SNOOZE_INDEX = 1140;
+
+ // Action: user tapped undo on the notification snooze leave-behind
+ // OS: O MR
+ NOTIFICATION_UNDO_SNOOZE = 1141;
+
+ // Action: user togged the visibility of the notification snooze options drop down
+ // OS: O MR
+ NOTIFICATION_SNOOZE_OPTIONS = 1142;
+
// ---- End O-MR1 Constants, all O-MR1 constants go above this line ----
// OPEN: Settings > Network & Internet > Mobile network
// CATEGORY: SETTINGS
- SETTINGS_MOBILE_NETWORK_CATEGORY = 1139;
+ SETTINGS_MOBILE_NETWORK_CATEGORY = 1200;
// ACTION: Settings > Network & Internet > Mobile network > Roaming
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_DATA_ROAMING_TOGGLE = 1140;
+ ACTION_MOBILE_NETWORK_DATA_ROAMING_TOGGLE = 1201;
// ACTION: Settings > Network & Internet > Mobile network > Advanced
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_EXPAND_ADVANCED_FIELDS = 1141;
+ ACTION_MOBILE_NETWORK_EXPAND_ADVANCED_FIELDS = 1202;
// ACTION: Settings > Network & Internet > Mobile network > Enhanced 4G LTE Mode
// CATEGORY: SETTINGS
- ACTION_MOBILE_ENHANCED_4G_LTE_MODE_TOGGLE = 1142;
+ ACTION_MOBILE_ENHANCED_4G_LTE_MODE_TOGGLE = 1203;
// ACTION: Settings > Network & Internet > Mobile network > Preferred network type
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_SELECT_PREFERRED_NETWORK = 1143;
+ ACTION_MOBILE_NETWORK_SELECT_PREFERRED_NETWORK = 1204;
// ACTION: Settings > Network & Internet > Mobile network > Preferred network type (enabled networks)
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_SELECT_ENABLED_NETWORK = 1144;
+ ACTION_MOBILE_NETWORK_SELECT_ENABLED_NETWORK = 1205;
// OPEN: Settings > Network & Internet > Mobile network > Carrier
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_EUICC_SETTING = 1145;
+ ACTION_MOBILE_NETWORK_EUICC_SETTING = 1206;
// OPEN: Settings > Network & Internet > Mobile network > Wi-Fi calling
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_WIFI_CALLING = 1146;
+ ACTION_MOBILE_NETWORK_WIFI_CALLING = 1207;
// ACTION: Settings > Network & Internet > Mobile network > Carrier video calling
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_VIDEO_CALLING_TOGGLE = 1147;
+ ACTION_MOBILE_NETWORK_VIDEO_CALLING_TOGGLE = 1208;
// ACTION: Settings > Network & Internet > Mobile network > Automatically select network
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_AUTO_SELECT_NETWORK_TOGGLE = 1148;
+ ACTION_MOBILE_NETWORK_AUTO_SELECT_NETWORK_TOGGLE = 1209;
// ACTION: Settings > Network & Internet > Mobile network > Network
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK = 1149;
+ ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK = 1210;
// FIELD - Manually selected mobile network
- FIELD_MOBILE_NETWORK = 1150;
+ FIELD_MOBILE_NETWORK = 1211;
// OPEN: Settings > Network & Internet > Mobile network > Access Point Names
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_APN_SETTINGS = 1151;
+ ACTION_MOBILE_NETWORK_APN_SETTINGS = 1212;
// OPEN: Settings > Network & Internet > Mobile network > Carrier settings
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_CARRIER_SETTINGS = 1152;
+ ACTION_MOBILE_NETWORK_CARRIER_SETTINGS = 1213;
// OPEN: Settings > Network & Internet > Mobile network > System select
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_CDMA_SYSTEM_SELECT = 1153;
+ ACTION_MOBILE_NETWORK_CDMA_SYSTEM_SELECT = 1214;
// OPEN: Settings > Network & Internet > Mobile network > CDMA subscription
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_CDMA_SUBSCRIPTION_SELECT = 1154;
+ ACTION_MOBILE_NETWORK_CDMA_SUBSCRIPTION_SELECT = 1215;
// ACTION: Settings > Network & Internet > Mobile network > Set up data service
// CATEGORY: SETTINGS
- ACTION_MOBILE_NETWORK_SET_UP_DATA_SERVICE = 1155;
+ ACTION_MOBILE_NETWORK_SET_UP_DATA_SERVICE = 1216;
// OPEN: Settings > Developer Options > Experiment dashboard
// CATEGORY: SETTINGS
- SETTINGS_FEATURE_FLAGS_DASHBOARD = 1156;
+ SETTINGS_FEATURE_FLAGS_DASHBOARD = 1217;
// OPEN: Settings > Notifications > [App] > Topic Notifications
// CATEGORY: SETTINGS
// OS: P
- NOTIFICATION_CHANNEL_GROUP = 1157;
+ NOTIFICATION_CHANNEL_GROUP = 1218;
// OPEN: Settings > Developer options > Enable > Info dialog
// CATEGORY: SETTINGS
// OS: P
- DIALOG_ENABLE_DEVELOPMENT_OPTIONS = 1158;
+ DIALOG_ENABLE_DEVELOPMENT_OPTIONS = 1219;
// OPEN: Settings > Developer options > OEM unlocking > Info dialog
// CATEGORY: SETTINGS
// OS: P
- DIALOG_ENABLE_OEM_UNLOCKING = 1159;
+ DIALOG_ENABLE_OEM_UNLOCKING = 1220;
+
+ // OPEN: Settings > Security > Nexus Imprint > [Fingerprint]
+ // CATEGORY: SETTINGS
+ // OS: P
+ FINGERPRINT_AUTHENTICATE_SIDECAR = 1221;
+
+ // OPEN: Settings > Developer options > USB debugging > Info dialog
+ // CATEGORY: SETTINGS
+ // OS: P
+ DIALOG_ENABLE_ADB = 1222;
+
+ // OPEN: Settings > Developer options > Revoke USB debugging authorizations > Info dialog
+ // CATEGORY: SETTINGS
+ // OS: P
+ DIALOG_CLEAR_ADB_KEYS = 1223;
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 28bf856..54eba2b 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -315,6 +315,27 @@
// Pno scan metrics
optional PnoScanMetrics pno_scan_metrics = 76;
+
+ // Histogram of "Connect to Network" notifications.
+ // The notification Action should be unset.
+ repeated ConnectToNetworkNotificationAndActionCount connect_to_network_notification_count = 77;
+
+ // Histogram of "Connect to Network" notification user actions.
+ repeated ConnectToNetworkNotificationAndActionCount connect_to_network_notification_action_count = 78;
+
+ // The number of SSIDs blacklisted from recommendation by the open network
+ // notification recommender
+ optional int32 open_network_recommender_blacklist_size = 79;
+
+ // Is the available network notification feature turned on
+ optional bool is_wifi_networks_available_notification_on = 80;
+
+ // Count of recommendation updates made by the open network notification
+ // recommender
+ optional int32 num_open_network_recommendation_updates = 81;
+
+ // Count of connection attempts that were initiated unsuccessfully
+ optional int32 num_open_network_connect_message_failed_to_send = 82;
}
// Information that gets logged for every WiFi connection.
@@ -950,3 +971,68 @@
// Total number of pno scans that found any network
optional int32 num_pno_found_network_events = 5;
}
+
+// Number of occurrences for a particular "Connect to Network" Notification or
+// notification Action.
+message ConnectToNetworkNotificationAndActionCount {
+
+ // "Connect to Network" notifications
+ enum Notification {
+
+ // Default
+ NOTIFICATION_UNKNOWN = 0;
+
+ // Initial notification with a recommended network.
+ NOTIFICATION_RECOMMEND_NETWORK = 1;
+
+ // Notification when connecting to the recommended network.
+ NOTIFICATION_CONNECTING_TO_NETWORK = 2;
+
+ // Notification when successfully connected to the network.
+ NOTIFICATION_CONNECTED_TO_NETWORK = 3;
+
+ // Notification when failed to connect to network.
+ NOTIFICATION_FAILED_TO_CONNECT = 4;
+ }
+
+ // "Connect to Network" notification actions
+ enum Action {
+
+ // Default
+ ACTION_UNKNOWN = 0;
+
+ // User dismissed the "Connect to Network" notification.
+ ACTION_USER_DISMISSED_NOTIFICATION = 1;
+
+ // User tapped action button to connect to recommended network.
+ ACTION_CONNECT_TO_NETWORK = 2;
+
+ // User tapped action button to open Wi-Fi Settings.
+ ACTION_PICK_WIFI_NETWORK = 3;
+
+ // User tapped "Failed to connect" notification to open Wi-Fi Settings.
+ ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE = 4;
+ }
+
+ // Recommenders of the "Connect to Network" notification
+ enum Recommender {
+
+ // Default.
+ RECOMMENDER_UNKNOWN = 0;
+
+ // Open Network Available recommender.
+ RECOMMENDER_OPEN = 1;
+ }
+
+ // Notification Type.
+ optional Notification notification = 1;
+
+ // Action Type.
+ optional Action action = 2;
+
+ // Recommender Type.
+ optional Recommender recommender = 3;
+
+ // Occurrences of this action.
+ optional int32 count = 4;
+}
diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk
index 21438e0..0854b95 100644
--- a/rs/jni/Android.mk
+++ b/rs/jni/Android.mk
@@ -12,7 +12,7 @@
libRS \
libcutils \
liblog \
- libskia \
+ libhwui \
libutils \
libui \
libgui \
@@ -23,9 +23,7 @@
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
- frameworks/rs \
- frameworks/base/core/jni \
- frameworks/base/libs/hwui
+ frameworks/rs
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 80b5477..a6aaaa67 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -2427,14 +2427,14 @@
out.attribute(null, "p", Integer.toHexString(widget.provider.tag));
}
if (widget.options != null) {
- out.attribute(null, "min_width", Integer.toHexString(widget.options.getInt(
- AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
- out.attribute(null, "min_height", Integer.toHexString(widget.options.getInt(
- AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
- out.attribute(null, "max_width", Integer.toHexString(widget.options.getInt(
- AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
- out.attribute(null, "max_height", Integer.toHexString(widget.options.getInt(
- AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
+ int minWidth = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
+ int minHeight = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);
+ int maxWidth = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH);
+ int maxHeight = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);
+ out.attribute(null, "min_width", Integer.toHexString((minWidth > 0) ? minWidth : 0));
+ out.attribute(null, "min_height", Integer.toHexString((minHeight > 0) ? minHeight : 0));
+ out.attribute(null, "max_width", Integer.toHexString((maxWidth > 0) ? maxWidth : 0));
+ out.attribute(null, "max_height", Integer.toHexString((maxHeight > 0) ? maxHeight : 0));
out.attribute(null, "host_category", Integer.toHexString(widget.options.getInt(
AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
}
diff --git a/services/art-profile b/services/art-profile
index ae5c909..ac5ecaf 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -2134,23 +2134,15 @@
HPLcom/android/server/autofill/ui/AutoFillUI$AutoFillUiCallback;->requestShowFillUi(Landroid/view/autofill/AutofillId;IILandroid/view/autofill/IAutofillWindowPresenter;)V
HPLcom/android/server/autofill/ui/AutoFillUI$AutoFillUiCallback;->save()V
HPLcom/android/server/autofill/ui/AutoFillUI$AutoFillUiCallback;->startIntentSender(Landroid/content/IntentSender;)V
-HPLcom/android/server/backup/BackupManagerService$1;->run()V
-HPLcom/android/server/backup/BackupManagerService$3;->onReceive(Landroid/content/Context;Landroid/content/Intent;)V
-HPLcom/android/server/backup/BackupManagerService$7;-><init>(Lcom/android/server/backup/BackupManagerService;Ljava/lang/String;Ljava/util/HashSet;)V
-HPLcom/android/server/backup/BackupManagerService$7;->run()V
-HPLcom/android/server/backup/BackupManagerService$BackupRequest;-><init>(Lcom/android/server/backup/BackupManagerService;Ljava/lang/String;)V
-HPLcom/android/server/backup/BackupManagerService;->-wrap18(Lcom/android/server/backup/BackupManagerService;Ljava/lang/String;Ljava/util/HashSet;)V
-HPLcom/android/server/backup/BackupManagerService;->addPackageParticipantsLockedInner(Ljava/lang/String;Ljava/util/List;)V
-HPLcom/android/server/backup/BackupManagerService;->allAgentPackages()Ljava/util/List;
-HPLcom/android/server/backup/BackupManagerService;->appIsDisabled(Landroid/content/pm/ApplicationInfo;Landroid/content/pm/PackageManager;)Z
-HPLcom/android/server/backup/BackupManagerService;->appIsEligibleForBackup(Landroid/content/pm/ApplicationInfo;Landroid/content/pm/PackageManager;)Z
-HPLcom/android/server/backup/BackupManagerService;->dataChanged(Ljava/lang/String;)V
-HPLcom/android/server/backup/BackupManagerService;->dataChangedImpl(Ljava/lang/String;)V
-HPLcom/android/server/backup/BackupManagerService;->dataChangedImpl(Ljava/lang/String;Ljava/util/HashSet;)V
-HPLcom/android/server/backup/BackupManagerService;->dataChangedTargets(Ljava/lang/String;)Ljava/util/HashSet;
-HPLcom/android/server/backup/BackupManagerService;->dequeueFullBackupLocked(Ljava/lang/String;)V
-HPLcom/android/server/backup/BackupManagerService;->readFullBackupSchedule()Ljava/util/ArrayList;
-HPLcom/android/server/backup/BackupManagerService;->writeToJournalLocked(Ljava/lang/String;)V
+HPLcom/android/server/backup/RefactoredBackupManagerService;->addPackageParticipantsLockedInner(Ljava/lang/String;Ljava/util/List;)V
+HPLcom/android/server/backup/RefactoredBackupManagerService;->allAgentPackages()Ljava/util/List;
+HPLcom/android/server/backup/RefactoredBackupManagerService;->dataChanged(Ljava/lang/String;)V
+HPLcom/android/server/backup/RefactoredBackupManagerService;->dataChangedImpl(Ljava/lang/String;)V
+HPLcom/android/server/backup/RefactoredBackupManagerService;->dataChangedImpl(Ljava/lang/String;Ljava/util/HashSet;)V
+HPLcom/android/server/backup/RefactoredBackupManagerService;->dataChangedTargets(Ljava/lang/String;)Ljava/util/HashSet;
+HPLcom/android/server/backup/RefactoredBackupManagerService;->dequeueFullBackupLocked(Ljava/lang/String;)V
+HPLcom/android/server/backup/RefactoredBackupManagerService;->readFullBackupSchedule()Ljava/util/ArrayList;
+HPLcom/android/server/backup/RefactoredBackupManagerService;->writeToJournalLocked(Ljava/lang/String;)V
HPLcom/android/server/backup/BackupManagerServiceInterface;->acknowledgeAdbBackupOrRestore(IZLjava/lang/String;Ljava/lang/String;Landroid/app/backup/IFullBackupRestoreObserver;)V
HPLcom/android/server/backup/BackupManagerServiceInterface;->adbBackup(Landroid/os/ParcelFileDescriptor;ZZZZZZZZ[Ljava/lang/String;)V
HPLcom/android/server/backup/BackupManagerServiceInterface;->adbRestore(Landroid/os/ParcelFileDescriptor;)V
@@ -2200,6 +2192,8 @@
HPLcom/android/server/backup/Trampoline;->dataChanged(Ljava/lang/String;)V
HPLcom/android/server/backup/TransportManager$TransportBoundListener;->onTransportBound(Lcom/android/internal/backup/IBackupTransport;)Z
HPLcom/android/server/backup/TransportManager;->onPackageRemoved(Ljava/lang/String;)V
+HPLcom/android/server/backup/utils/AppBackupUtils;->appIsDisabled(Landroid/content/pm/ApplicationInfo;Landroid/content/pm/PackageManager;)Z
+HPLcom/android/server/backup/utils/AppBackupUtils;->appIsEligibleForBackup(Landroid/content/pm/ApplicationInfo;Landroid/content/pm/PackageManager;)Z
HPLcom/android/server/clipboard/ClipboardService$ClipboardImpl;->hasPrimaryClip(Ljava/lang/String;)Z
HPLcom/android/server/clipboard/ClipboardService;->clipboardAccessAllowed(ILjava/lang/String;I)Z
HPLcom/android/server/companion/CompanionDeviceManagerService$CompanionDeviceManagerImpl;->checkCallerIsSystemOr(Ljava/lang/String;I)V
@@ -9364,61 +9358,30 @@
PLcom/android/server/autofill/ui/AutoFillUI;->lambda$-com_android_server_autofill_ui_AutoFillUI_4826(Lcom/android/server/autofill/ui/AutoFillUI$AutoFillUiCallback;Ljava/lang/String;)V
PLcom/android/server/autofill/ui/AutoFillUI;->setCallback(Lcom/android/server/autofill/ui/AutoFillUI$AutoFillUiCallback;)V
PLcom/android/server/autofill/ui/OverlayControl;-><init>(Landroid/content/Context;)V
-PLcom/android/server/backup/-$Lambda$UGPbw6RN8_4TeqlxQ94PEo_ieak;->$m$0()V
-PLcom/android/server/backup/-$Lambda$UGPbw6RN8_4TeqlxQ94PEo_ieak;-><init>(BLjava/lang/Object;)V
-PLcom/android/server/backup/-$Lambda$UGPbw6RN8_4TeqlxQ94PEo_ieak;->run()V
-PLcom/android/server/backup/BackupManagerService$1;-><init>(Lcom/android/server/backup/BackupManagerService;)V
-PLcom/android/server/backup/BackupManagerService$2;-><init>(Lcom/android/server/backup/BackupManagerService;)V
-PLcom/android/server/backup/BackupManagerService$2;->onTransportBound(Lcom/android/internal/backup/IBackupTransport;)Z
-PLcom/android/server/backup/BackupManagerService$3;-><init>(Lcom/android/server/backup/BackupManagerService;)V
-PLcom/android/server/backup/BackupManagerService$4;-><init>(Lcom/android/server/backup/BackupManagerService;J)V
-PLcom/android/server/backup/BackupManagerService$4;->run()V
-PLcom/android/server/backup/BackupManagerService$8;-><init>(Lcom/android/server/backup/BackupManagerService;Landroid/content/ComponentName;Landroid/app/backup/ISelectBackupTransportCallback;)V
-PLcom/android/server/backup/BackupManagerService$8;->onSuccess(Ljava/lang/String;)V
-PLcom/android/server/backup/BackupManagerService$BackupHandler;-><init>(Lcom/android/server/backup/BackupManagerService;Landroid/os/Looper;)V
-PLcom/android/server/backup/BackupManagerService$BackupHandler;->handleMessage(Landroid/os/Message;)V
-PLcom/android/server/backup/BackupManagerService$FullBackupEntry;-><init>(Lcom/android/server/backup/BackupManagerService;Ljava/lang/String;J)V
-PLcom/android/server/backup/BackupManagerService$FullBackupEntry;->compareTo(Lcom/android/server/backup/BackupManagerService$FullBackupEntry;)I
-PLcom/android/server/backup/BackupManagerService$FullBackupEntry;->compareTo(Ljava/lang/Object;)I
-PLcom/android/server/backup/BackupManagerService$Lifecycle;-><init>(Landroid/content/Context;)V
-PLcom/android/server/backup/BackupManagerService$Lifecycle;->onStart()V
-PLcom/android/server/backup/BackupManagerService$Lifecycle;->onUnlockUser(I)V
-PLcom/android/server/backup/BackupManagerService$ProvisionedObserver;-><init>(Lcom/android/server/backup/BackupManagerService;Landroid/os/Handler;)V
-PLcom/android/server/backup/BackupManagerService$RunBackupReceiver;-><init>(Lcom/android/server/backup/BackupManagerService;)V
-PLcom/android/server/backup/BackupManagerService$RunBackupReceiver;-><init>(Lcom/android/server/backup/BackupManagerService;Lcom/android/server/backup/BackupManagerService$RunBackupReceiver;)V
-PLcom/android/server/backup/BackupManagerService$RunInitializeReceiver;-><init>(Lcom/android/server/backup/BackupManagerService;)V
-PLcom/android/server/backup/BackupManagerService$RunInitializeReceiver;-><init>(Lcom/android/server/backup/BackupManagerService;Lcom/android/server/backup/BackupManagerService$RunInitializeReceiver;)V
-PLcom/android/server/backup/BackupManagerService;->-get3(Lcom/android/server/backup/BackupManagerService;)Landroid/content/pm/PackageManager;
-PLcom/android/server/backup/BackupManagerService;->-get6(Lcom/android/server/backup/BackupManagerService;)Lcom/android/server/backup/TransportManager;
-PLcom/android/server/backup/BackupManagerService;->-wrap11(I)Z
-PLcom/android/server/backup/BackupManagerService;->-wrap17(Lcom/android/server/backup/BackupManagerService;Ljava/lang/String;)V
-PLcom/android/server/backup/BackupManagerService;->-wrap23(Lcom/android/server/backup/BackupManagerService;Ljava/lang/String;)V
-PLcom/android/server/backup/BackupManagerService;->-wrap25(Lcom/android/server/backup/BackupManagerService;)V
-PLcom/android/server/backup/BackupManagerService;->-wrap5(Landroid/content/pm/PackageInfo;)Z
-PLcom/android/server/backup/BackupManagerService;->-wrap9(I)Z
-PLcom/android/server/backup/BackupManagerService;-><init>(Landroid/content/Context;Lcom/android/server/backup/Trampoline;)V
-PLcom/android/server/backup/BackupManagerService;->addPackageParticipantsLocked([Ljava/lang/String;)V
-PLcom/android/server/backup/BackupManagerService;->appGetsFullBackup(Landroid/content/pm/PackageInfo;)Z
-PLcom/android/server/backup/BackupManagerService;->backupSettingMigrated(I)Z
-PLcom/android/server/backup/BackupManagerService;->enqueueFullBackup(Ljava/lang/String;J)V
-PLcom/android/server/backup/BackupManagerService;->getCurrentTransport()Ljava/lang/String;
-PLcom/android/server/backup/BackupManagerService;->initPackageTracking()V
-PLcom/android/server/backup/BackupManagerService;->isBackupEnabled()Z
-PLcom/android/server/backup/BackupManagerService;->lambda$-com_android_server_backup_BackupManagerService_56585()V
-PLcom/android/server/backup/BackupManagerService;->parseLeftoverJournals()V
-PLcom/android/server/backup/BackupManagerService;->readBackupEnableState(I)Z
-PLcom/android/server/backup/BackupManagerService;->removePackageFromSetLocked(Ljava/util/HashSet;Ljava/lang/String;)V
-PLcom/android/server/backup/BackupManagerService;->removePackageParticipantsLocked([Ljava/lang/String;I)V
-PLcom/android/server/backup/BackupManagerService;->scheduleNextFullBackupJob(J)V
-PLcom/android/server/backup/BackupManagerService;->selectBackupTransportAsync(Landroid/content/ComponentName;Landroid/app/backup/ISelectBackupTransportCallback;)V
-PLcom/android/server/backup/BackupManagerService;->setBackupEnabled(Z)V
-PLcom/android/server/backup/BackupManagerService;->updateStateForTransport(Ljava/lang/String;)V
-PLcom/android/server/backup/BackupManagerService;->writeBackupEnableState(ZI)V
-PLcom/android/server/backup/BackupManagerService;->writeFullBackupScheduleAsync()V
+PLcom/android/server/backup/RefactoredBackupManagerService$Lifecycle;-><init>(Landroid/content/Context;)V
+PLcom/android/server/backup/RefactoredBackupManagerService$Lifecycle;->onStart()V
+PLcom/android/server/backup/RefactoredBackupManagerService$Lifecycle;->onUnlockUser(I)V
+PLcom/android/server/backup/RefactoredBackupManagerService;-><init>(Landroid/content/Context;Lcom/android/server/backup/Trampoline;)V
+PLcom/android/server/backup/RefactoredBackupManagerService;->addPackageParticipantsLocked([Ljava/lang/String;)V
+PLcom/android/server/backup/RefactoredBackupManagerService;->backupSettingMigrated(I)Z
+PLcom/android/server/backup/RefactoredBackupManagerService;->enqueueFullBackup(Ljava/lang/String;J)V
+PLcom/android/server/backup/RefactoredBackupManagerService;->getCurrentTransport()Ljava/lang/String;
+PLcom/android/server/backup/RefactoredBackupManagerService;->initPackageTracking()V
+PLcom/android/server/backup/RefactoredBackupManagerService;->isBackupEnabled()Z
+PLcom/android/server/backup/RefactoredBackupManagerService;->parseLeftoverJournals()V
+PLcom/android/server/backup/RefactoredBackupManagerService;->readBackupEnableState(I)Z
+PLcom/android/server/backup/RefactoredBackupManagerService;->removePackageFromSetLocked(Ljava/util/HashSet;Ljava/lang/String;)V
+PLcom/android/server/backup/RefactoredBackupManagerService;->removePackageParticipantsLocked([Ljava/lang/String;I)V
+PLcom/android/server/backup/RefactoredBackupManagerService;->scheduleNextFullBackupJob(J)V
+PLcom/android/server/backup/RefactoredBackupManagerService;->selectBackupTransportAsync(Landroid/content/ComponentName;Landroid/app/backup/ISelectBackupTransportCallback;)V
+PLcom/android/server/backup/RefactoredBackupManagerService;->setBackupEnabled(Z)V
+PLcom/android/server/backup/RefactoredBackupManagerService;->updateStateForTransport(Ljava/lang/String;)V
+PLcom/android/server/backup/RefactoredBackupManagerService;->writeBackupEnableState(ZI)V
+PLcom/android/server/backup/RefactoredBackupManagerService;->writeFullBackupScheduleAsync()V
PLcom/android/server/backup/FullBackupJob;->schedule(Landroid/content/Context;J)V
PLcom/android/server/backup/KeyValueBackupJob;->cancel(Landroid/content/Context;)V
PLcom/android/server/backup/Trampoline;-><init>(Landroid/content/Context;)V
-PLcom/android/server/backup/Trampoline;->createBackupManagerService()Lcom/android/server/backup/BackupManagerServiceInterface;
+PLcom/android/server/backup/Trampoline;->createRefactoredBackupManagerService()Lcom/android/server/backup/BackupManagerServiceInterface;
PLcom/android/server/backup/Trampoline;->createService()Lcom/android/server/backup/BackupManagerServiceInterface;
PLcom/android/server/backup/Trampoline;->getCurrentTransport()Ljava/lang/String;
PLcom/android/server/backup/Trampoline;->getSuppressFile()Ljava/io/File;
@@ -9459,6 +9422,14 @@
PLcom/android/server/backup/TransportManager;->registerAllTransports()V
PLcom/android/server/backup/TransportManager;->selectTransport(Ljava/lang/String;)Ljava/lang/String;
PLcom/android/server/backup/TransportManager;->tryBindTransport(Landroid/content/ComponentName;)V
+PLcom/android/server/backup/internal/BackupHandler;-><init>(Lcom/android/server/backup/RefactoredBackupManagerService;Landroid/os/Looper;)V
+PLcom/android/server/backup/fullbackup/FullBackupEntry;->compareTo(Lcom/android/server/backup/fullbackup/FullBackupEntry;)I
+PLcom/android/server/backup/fullbackup/FullBackupEntry;->compareTo(Ljava/lang/Object;)I
+PLcom/android/server/backup/internal/BackupHandler;->handleMessage(Landroid/os/Message;)V
+PLcom/android/server/backup/internal/ProvisionedObserver;-><init>(Lcom/android/server/backup/RefactoredBackupManagerService;Landroid/os/Handler;)V
+PLcom/android/server/backup/internal/RunBackupReceiver;-><init>(Lcom/android/server/backup/RefactoredBackupManagerService;)V
+PLcom/android/server/backup/internal/RunInitializeReceiver;-><init>(Lcom/android/server/backup/RefactoredBackupManagerService;)V
+PLcom/android/server/backup/utils/AppBackupUtils;->appGetsFullBackup(Landroid/content/pm/PackageInfo;)Z
PLcom/android/server/camera/CameraServiceProxy$1;-><init>(Lcom/android/server/camera/CameraServiceProxy;)V
PLcom/android/server/camera/CameraServiceProxy$2;-><init>(Lcom/android/server/camera/CameraServiceProxy;)V
PLcom/android/server/camera/CameraServiceProxy$2;->notifyCameraState(Ljava/lang/String;IILjava/lang/String;)V
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index a1c75bf..1f4161a 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -115,11 +115,24 @@
private final SparseBooleanArray mDisabledUsers = new SparseBooleanArray();
private final LocalLog mRequestsHistory = new LocalLog(20);
+ private final LocalLog mUiLatencyHistory = new LocalLog(20);
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
+ if (sDebug) Slog.d(TAG, "Close system dialogs");
+
+ // TODO(b/64940307): we need to destroy all sessions that are finished but showing
+ // Save UI because there is no way to show the Save UI back when the activity
+ // beneath it is brought back to top. Ideally, we should just hide the UI and
+ // bring it back when the activity resumes.
+ synchronized (mLock) {
+ for (int i = 0; i < mServicesCache.size(); i++) {
+ mServicesCache.valueAt(i).destroyFinishedSessionsLocked();
+ }
+ }
+
mUi.hideAll(null);
}
}
@@ -294,7 +307,7 @@
AutofillManagerServiceImpl service = mServicesCache.get(resolvedUserId);
if (service == null) {
service = new AutofillManagerServiceImpl(mContext, mLock, mRequestsHistory,
- resolvedUserId, mUi, mDisabledUsers.get(resolvedUserId));
+ mUiLatencyHistory, resolvedUserId, mUi, mDisabledUsers.get(resolvedUserId));
mServicesCache.put(userId, service);
}
return service;
@@ -724,6 +737,8 @@
if (showHistory) {
pw.println("Requests history:");
mRequestsHistory.reverseDump(fd, pw, args);
+ pw.println("UI latency history:");
+ mUiLatencyHistory.reverseDump(fd, pw, args);
}
} finally {
setDebugLocked(oldDebug);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 7212b23..862070a 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -35,6 +35,7 @@
import android.content.pm.ServiceInfo;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.metrics.LogMaker;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
@@ -63,6 +64,8 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.HandlerCaller;
import com.android.server.autofill.ui.AutoFillUI;
@@ -89,6 +92,7 @@
private final Context mContext;
private final Object mLock;
private final AutoFillUI mUi;
+ private final MetricsLogger mMetricsLogger = new MetricsLogger();
private RemoteCallbackList<IAutoFillManagerClient> mClients;
private AutofillServiceInfo mInfo;
@@ -96,6 +100,8 @@
private static final Random sRandom = new Random();
private final LocalLog mRequestsHistory;
+ private final LocalLog mUiLatencyHistory;
+
/**
* Whether service was disabled for user due to {@link UserManager} restrictions.
*/
@@ -137,10 +143,11 @@
private long mLastPrune = 0;
AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
- int userId, AutoFillUI ui, boolean disabled) {
+ LocalLog uiLatencyHistory, int userId, AutoFillUI ui, boolean disabled) {
mContext = context;
mLock = lock;
mRequestsHistory = requestsHistory;
+ mUiLatencyHistory = uiLatencyHistory;
mUserId = userId;
mUi = ui;
updateLocked(disabled);
@@ -218,8 +225,10 @@
if (serviceInfo != null) {
mInfo = new AutofillServiceInfo(mContext.getPackageManager(),
serviceComponent, mUserId);
+ if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo);
} else {
mInfo = null;
+ if (sDebug) Slog.d(TAG, "Reset component for user " + mUserId);
}
final boolean isEnabled = isEnabled();
if (wasEnabled != isEnabled) {
@@ -345,17 +354,31 @@
}
void disableOwnedAutofillServicesLocked(int uid) {
- if (mInfo == null || mInfo.getServiceInfo().applicationInfo.uid != uid) {
+ Slog.i(TAG, "disableOwnedServices(" + uid + "): " + mInfo);
+ if (mInfo == null) return;
+
+ final ServiceInfo serviceInfo = mInfo.getServiceInfo();
+ if (serviceInfo.applicationInfo.uid != uid) {
+ Slog.w(TAG, "disableOwnedServices(): ignored when called by UID " + uid
+ + " instead of " + serviceInfo.applicationInfo.uid
+ + " for service " + mInfo);
return;
}
+
+
final long identity = Binder.clearCallingIdentity();
try {
final String autoFillService = getComponentNameFromSettings();
- if (mInfo.getServiceInfo().getComponentName().equals(
- ComponentName.unflattenFromString(autoFillService))) {
+ final ComponentName componentName = serviceInfo.getComponentName();
+ if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) {
+ mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF,
+ componentName.getPackageName());
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.AUTOFILL_SERVICE, null, mUserId);
destroySessionsLocked();
+ } else {
+ Slog.w(TAG, "disableOwnedServices(): ignored because current service ("
+ + serviceInfo + ") does not match Settings (" + autoFillService + ")");
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -379,7 +402,7 @@
final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock,
sessionId, uid, activityToken, appCallbackToken, hasCallback,
- mInfo.getServiceInfo().getComponentName(), packageName);
+ mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), packageName);
mSessions.put(newSession.id, newSession);
return newSession;
@@ -452,7 +475,7 @@
final int sessionCount = mSessions.size();
for (int i = sessionCount - 1; i >= 0; i--) {
final Session session = mSessions.valueAt(i);
- if (session.isSaveUiPendingForToken(token)) {
+ if (session.isSaveUiPendingForTokenLocked(token)) {
session.onPendingSaveUi(operation, token);
return;
}
@@ -646,6 +669,18 @@
}
}
+ // TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities
+ void destroyFinishedSessionsLocked() {
+ final int sessionCount = mSessions.size();
+ for (int i = sessionCount - 1; i >= 0; i--) {
+ final Session session = mSessions.valueAt(i);
+ if (session.isSavingLocked()) {
+ if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id);
+ session.forceRemoveSelfLocked();
+ }
+ }
+ }
+
void listSessionsLocked(ArrayList<String> output) {
final int numSessions = mSessions.size();
for (int i = 0; i < numSessions; i++) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ed3441f..ed00ffe 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -50,19 +50,24 @@
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.service.autofill.AutofillService;
import android.service.autofill.Dataset;
import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
+import android.service.autofill.InternalSanitizer;
import android.service.autofill.InternalValidator;
import android.service.autofill.SaveInfo;
import android.service.autofill.SaveRequest;
+import android.service.autofill.Transformation;
import android.service.autofill.ValueFinder;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.LocalLog;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TimeUtils;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
@@ -183,6 +188,20 @@
private ArrayList<String> mSelectedDatasetIds;
/**
+ * When the session started (using elapsed time since boot).
+ */
+ private final long mStartTime;
+
+ /**
+ * When the UI was shown for the first time (using elapsed time since boot).
+ */
+ @GuardedBy("mLock")
+ private long mUiShownTime;
+
+ @GuardedBy("mLock")
+ private final LocalLog mUiLatencyHistory;
+
+ /**
* Receiver of assist data from the app's {@link Activity}.
*/
private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
@@ -403,10 +422,11 @@
Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui,
@NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId,
@NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken,
- @NonNull IBinder client, boolean hasCallback,
+ @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory,
@NonNull ComponentName componentName, @NonNull String packageName) {
id = sessionId;
this.uid = uid;
+ mStartTime = SystemClock.elapsedRealtime();
mService = service;
mLock = lock;
mUi = ui;
@@ -414,6 +434,7 @@
mRemoteFillService = new RemoteFillService(context, componentName, userId, this);
mActivityToken = activityToken;
mHasCallback = hasCallback;
+ mUiLatencyHistory = uiLatencyHistory;
mPackageName = packageName;
mClient = IAutoFillManagerClient.Stub.asInterface(client);
@@ -837,6 +858,8 @@
return true;
}
+ final ArrayMap<AutofillId, InternalSanitizer> sanitizers = createSanitizers(saveInfo);
+
// Cache used to make sure changed fields do not belong to a dataset.
final ArrayMap<AutofillId, AutofillValue> currentValues = new ArrayMap<>();
final ArraySet<AutofillId> allIds = new ArraySet<>();
@@ -876,6 +899,7 @@
break;
}
}
+ value = getSanitizedValue(sanitizers, id, value);
currentValues.put(id, value);
final AutofillValue filledValue = viewState.getAutofilledValue();
@@ -1018,6 +1042,48 @@
return true;
}
+ @Nullable
+ private ArrayMap<AutofillId, InternalSanitizer> createSanitizers(@Nullable SaveInfo saveInfo) {
+ if (saveInfo == null) return null;
+
+ final InternalSanitizer[] sanitizerKeys = saveInfo.getSanitizerKeys();
+ if (sanitizerKeys == null) return null;
+
+ final int size = sanitizerKeys.length ;
+ final ArrayMap<AutofillId, InternalSanitizer> sanitizers = new ArrayMap<>(size);
+ if (sDebug) Slog.d(TAG, "Service provided " + size + " sanitizers");
+ final AutofillId[][] sanitizerValues = saveInfo.getSanitizerValues();
+ for (int i = 0; i < size; i++) {
+ final InternalSanitizer sanitizer = sanitizerKeys[i];
+ final AutofillId[] ids = sanitizerValues[i];
+ if (sDebug) {
+ Slog.d(TAG, "sanitizer #" + i + " (" + sanitizer + ") for ids "
+ + Arrays.toString(ids));
+ }
+ for (AutofillId id : ids) {
+ sanitizers.put(id, sanitizer);
+ }
+ }
+ return sanitizers;
+ }
+
+ @NonNull
+ private AutofillValue getSanitizedValue(
+ @Nullable ArrayMap<AutofillId, InternalSanitizer> sanitizers,
+ @NonNull AutofillId id,
+ @NonNull AutofillValue value) {
+ if (sanitizers == null) return value;
+
+ final InternalSanitizer sanitizer = sanitizers.get(id);
+ if (sanitizer == null) {
+ return value;
+ }
+
+ final AutofillValue sanitized = sanitizer.sanitize(value);
+ if (sDebug) Slog.d(TAG, "Value for " + id + "(" + value + ") sanitized to " + sanitized);
+ return sanitized;
+ }
+
/**
* Returns whether the session is currently showing the save UI
*/
@@ -1081,6 +1147,9 @@
return;
}
+ final ArrayMap<AutofillId, InternalSanitizer> sanitizers =
+ createSanitizers(getSaveInfoLocked());
+
final int numContexts = mContexts.size();
for (int contextNum = 0; contextNum < numContexts; contextNum++) {
@@ -1107,7 +1176,9 @@
}
if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value);
- node.updateAutofillValue(value);
+ final AutofillValue sanitizedValue = getSanitizedValue(sanitizers, id, value);
+
+ node.updateAutofillValue(sanitizedValue);
}
// Sanitize structure before it's sent to service.
@@ -1355,6 +1426,31 @@
getUiForShowing().showFillUi(filledId, response, filterText,
mService.getServicePackageName(), mPackageName, this);
+
+ synchronized (mLock) {
+ if (mUiShownTime == 0) {
+ // Log first time UI is shown.
+ mUiShownTime = SystemClock.elapsedRealtime();
+ final long duration = mUiShownTime - mStartTime;
+ if (sDebug) {
+ final StringBuilder msg = new StringBuilder("1st UI for ")
+ .append(mActivityToken)
+ .append(" shown in ");
+ TimeUtils.formatDuration(duration, msg);
+ Slog.d(TAG, msg.toString());
+ }
+ final StringBuilder historyLog = new StringBuilder("id=").append(id)
+ .append(" app=").append(mActivityToken)
+ .append(" svc=").append(mService.getServicePackageName())
+ .append(" latency=");
+ TimeUtils.formatDuration(duration, historyLog);
+ mUiLatencyHistory.log(historyLog.toString());
+
+ final LogMaker metricsLog = newLogMaker(MetricsEvent.AUTOFILL_UI_LATENCY)
+ .setCounterValue((int) duration);
+ mMetricsLogger.write(metricsLog);
+ }
+ }
}
boolean isDestroyed() {
@@ -1376,7 +1472,7 @@
if (mHasCallback) {
mClient.notifyNoFillUi(id, mCurrentViewId, sessionFinished);
} else if (sessionFinished) {
- mClient.setSessionFinished();
+ mClient.setSessionFinished(AutofillManager.STATE_FINISHED);
}
} catch (RemoteException e) {
Slog.e(TAG, "Error notifying client no fill UI: id=" + mCurrentViewId, e);
@@ -1663,6 +1759,14 @@
pw.print(prefix); pw.print("uid: "); pw.println(uid);
pw.print(prefix); pw.print("mPackagename: "); pw.println(mPackageName);
pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
+ pw.print(prefix); pw.print("mStartTime: "); pw.println(mStartTime);
+ pw.print(prefix); pw.print("Time to show UI: ");
+ if (mUiShownTime == 0) {
+ pw.println("N/A");
+ } else {
+ TimeUtils.formatDuration(mUiShownTime - mStartTime, pw);
+ pw.println();
+ }
pw.print(prefix); pw.print("mResponses: ");
if (mResponses == null) {
pw.println("null");
@@ -1795,18 +1899,17 @@
void forceRemoveSelfLocked() {
if (sVerbose) Slog.v(TAG, "forceRemoveSelfLocked(): " + mPendingSaveUi);
+ final boolean isPendingSaveUi = isSaveUiPendingLocked();
mPendingSaveUi = null;
removeSelfLocked();
-
- mHandlerCaller.getHandler().post(() -> {
- try {
- mClient.setState(mService.isEnabled(), true, false);
- } catch (RemoteException e) {
- Slog.w(TAG, "error updating client state: " + e);
- }
- });
-
mUi.destroyAll(mPendingSaveUi, this, false);
+ if (!isPendingSaveUi) {
+ try {
+ mClient.setSessionFinished(AutofillManager.STATE_UNKNOWN);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error notifying client to finish session", e);
+ }
+ }
}
/**
@@ -1829,7 +1932,7 @@
+ id + " destroyed");
return;
}
- if (isSaveUiPending()) {
+ if (isSaveUiPendingLocked()) {
Slog.i(TAG, "removeSelfLocked() ignored, waiting for pending save ui");
return;
}
@@ -1850,14 +1953,14 @@
* a specific {@code token} created by
* {@link PendingUi#PendingUi(IBinder, int, IAutoFillManagerClient)}.
*/
- boolean isSaveUiPendingForToken(@NonNull IBinder token) {
- return isSaveUiPending() && token.equals(mPendingSaveUi.getToken());
+ boolean isSaveUiPendingForTokenLocked(@NonNull IBinder token) {
+ return isSaveUiPendingLocked() && token.equals(mPendingSaveUi.getToken());
}
/**
* Checks whether this session is hiding the Save UI to handle a custom description link.
*/
- private boolean isSaveUiPending() {
+ private boolean isSaveUiPendingLocked() {
return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING;
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 371e74d..bf442dc 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -55,6 +55,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.regex.Pattern;
final class FillUi {
private static final String TAG = "FillUi";
@@ -164,15 +165,18 @@
Slog.e(TAG, "Error inflating remote views", e);
continue;
}
- final AutofillValue value = dataset.getFieldValues().get(index);
+ final Pattern filter = dataset.getFilter(index);
String valueText = null;
- // If the dataset needs auth - don't add its text to allow guessing
- // its content based on how filtering behaves.
- if (value != null && value.isText() && dataset.getAuthentication() == null) {
- valueText = value.getTextValue().toString().toLowerCase();
+ if (filter == null) {
+ final AutofillValue value = dataset.getFieldValues().get(index);
+ // If the dataset needs auth - don't add its text to allow guessing
+ // its content based on how filtering behaves.
+ if (value != null && value.isText() && dataset.getAuthentication() == null) {
+ valueText = value.getTextValue().toString().toLowerCase();
+ }
}
- items.add(new ViewItem(dataset, valueText, view));
+ items.add(new ViewItem(dataset, filter, valueText, view));
}
}
@@ -331,11 +335,17 @@
private final String mValue;
private final Dataset mDataset;
private final View mView;
+ private final Pattern mFilter;
- ViewItem(Dataset dataset, String value, View view) {
+ ViewItem(Dataset dataset, Pattern filter, String value, View view) {
mDataset = dataset;
mValue = value;
mView = view;
+ mFilter = filter;
+ }
+
+ public Pattern getFilter() {
+ return mFilter;
}
public View getView() {
@@ -349,12 +359,6 @@
public String getValue() {
return mValue;
}
-
- @Override
- public String toString() {
- // Used for filtering in the adapter
- return mValue;
- }
}
private final class AutofillWindowPresenter extends IAutofillWindowPresenter.Stub {
@@ -516,10 +520,16 @@
for (int i = 0; i < itemCount; i++) {
final ViewItem item = mAllItems.get(i);
final String value = item.getValue();
- // No value, i.e. null, matches any filter
- if ((value == null && item.mDataset.getAuthentication() == null)
- || (value != null
- && value.toLowerCase().startsWith(constraintLowerCase))) {
+ final Pattern filter = item.getFilter();
+ final boolean matches;
+ if (filter != null) {
+ matches = filter.matcher(constraintLowerCase).matches();
+ } else {
+ matches = (value == null)
+ ? (item.mDataset.getAuthentication() == null)
+ : value.toLowerCase().startsWith(constraintLowerCase);
+ }
+ if (matches) {
filteredItems.add(item);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 32f4d69..d48f23c 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -263,9 +263,7 @@
} else {
noButton.setText(R.string.autofill_save_no);
}
- final View.OnClickListener cancelListener =
- (v) -> mListener.onCancel(info.getNegativeActionListener());
- noButton.setOnClickListener(cancelListener);
+ noButton.setOnClickListener((v) -> mListener.onCancel(info.getNegativeActionListener()));
final View yesButton = view.findViewById(R.id.autofill_save_yes);
yesButton.setOnClickListener((v) -> mListener.onSave());
@@ -273,8 +271,9 @@
mDialog = new Dialog(context, R.style.Theme_DeviceDefault_Light_Panel);
mDialog.setContentView(view);
- // Dialog can be dismissed when touched outside.
- mDialog.setOnDismissListener((d) -> mListener.onCancel(info.getNegativeActionListener()));
+ // Dialog can be dismissed when touched outside, but the negative listener should not be
+ // notified (hence the null argument).
+ mDialog.setOnDismissListener((d) -> mListener.onCancel(null));
final Window window = mDialog.getWindow();
window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
@@ -305,7 +304,7 @@
if (actualWidth <= maxWidth && actualHeight <= maxHeight) {
if (sDebug) {
- Slog.d(TAG, "Addingservice icon "
+ Slog.d(TAG, "Adding service icon "
+ "(" + actualWidth + "x" + actualHeight + ") as it's less than maximum "
+ "(" + maxWidth + "x" + maxHeight + ").");
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerConstants.java b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
index cd60182..245241c 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerConstants.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
@@ -123,7 +123,7 @@
// group the calls of these methods in a block syncrhonized on
// a reference of this object.
public synchronized long getKeyValueBackupIntervalMilliseconds() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "getKeyValueBackupIntervalMilliseconds(...) returns "
+ mKeyValueBackupIntervalMilliseconds);
}
@@ -131,7 +131,7 @@
}
public synchronized long getKeyValueBackupFuzzMilliseconds() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "getKeyValueBackupFuzzMilliseconds(...) returns "
+ mKeyValueBackupFuzzMilliseconds);
}
@@ -139,7 +139,7 @@
}
public synchronized boolean getKeyValueBackupRequireCharging() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "getKeyValueBackupRequireCharging(...) returns "
+ mKeyValueBackupRequireCharging);
}
@@ -147,7 +147,7 @@
}
public synchronized int getKeyValueBackupRequiredNetworkType() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "getKeyValueBackupRequiredNetworkType(...) returns "
+ mKeyValueBackupRequiredNetworkType);
}
@@ -155,7 +155,7 @@
}
public synchronized long getFullBackupIntervalMilliseconds() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "getFullBackupIntervalMilliseconds(...) returns "
+ mFullBackupIntervalMilliseconds);
}
@@ -163,7 +163,7 @@
}
public synchronized boolean getFullBackupRequireCharging() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "getFullBackupRequireCharging(...) returns " + mFullBackupRequireCharging);
}
return mFullBackupRequireCharging;
@@ -171,7 +171,7 @@
}
public synchronized int getFullBackupRequiredNetworkType() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "getFullBackupRequiredNetworkType(...) returns "
+ mFullBackupRequiredNetworkType);
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index f525797..f4dbb5a 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -192,6 +192,10 @@
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
+/**
+ * @Deprecated Use RefactoredBackupManagerService instead. This class is only
+ * kept for fallback and archeology reasons and will be removed soon.
+ */
public class BackupManagerService implements BackupManagerServiceInterface {
private static final String TAG = "BackupManagerService";
@@ -1983,7 +1987,7 @@
if (uri == null) {
return;
}
- String pkgName = uri.getSchemeSpecificPart();
+ final String pkgName = uri.getSchemeSpecificPart();
if (pkgName != null) {
pkgList = new String[] { pkgName };
}
@@ -1991,7 +1995,7 @@
// At package-changed we only care about looking at new transport states
if (changed) {
- String[] components =
+ final String[] components =
intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
if (MORE_DEBUG) {
@@ -2001,7 +2005,8 @@
}
}
- mTransportManager.onPackageChanged(pkgName, components);
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageChanged(pkgName, components));
return; // nothing more to do in the PACKAGE_CHANGED case
}
@@ -2033,7 +2038,7 @@
}
// If they're full-backup candidates, add them there instead
final long now = System.currentTimeMillis();
- for (String packageName : pkgList) {
+ for (final String packageName : pkgList) {
try {
PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
if (appGetsFullBackup(app)
@@ -2050,7 +2055,8 @@
writeFullBackupScheduleAsync();
}
- mTransportManager.onPackageAdded(packageName);
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageAdded(packageName));
} catch (NameNotFoundException e) {
// doesn't really exist; ignore it
@@ -2074,8 +2080,9 @@
removePackageParticipantsLocked(pkgList, uid);
}
}
- for (String pkgName : pkgList) {
- mTransportManager.onPackageRemoved(pkgName);
+ for (final String pkgName : pkgList) {
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageRemoved(pkgName));
}
}
}
diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java
index 82638b4..b81a54d 100644
--- a/services/backup/java/com/android/server/backup/FullBackupJob.java
+++ b/services/backup/java/com/android/server/backup/FullBackupJob.java
@@ -61,7 +61,7 @@
@Override
public boolean onStartJob(JobParameters params) {
mParams = params;
- Trampoline service = BackupManagerService.getInstance();
+ Trampoline service = RefactoredBackupManagerService.getInstance();
return service.beginFullBackup(this);
}
@@ -69,7 +69,7 @@
public boolean onStopJob(JobParameters params) {
if (mParams != null) {
mParams = null;
- Trampoline service = BackupManagerService.getInstance();
+ Trampoline service = RefactoredBackupManagerService.getInstance();
service.endFullBackup();
}
return false;
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 279c828..b38b25a 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -4,8 +4,8 @@
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL;
+import static com.android.server.backup.RefactoredBackupManagerService.OP_TYPE_BACKUP_WAIT;
+import static com.android.server.backup.RefactoredBackupManagerService.TIMEOUT_BACKUP_INTERVAL;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
@@ -19,6 +19,8 @@
import android.os.SELinux;
import android.util.Slog;
+import com.android.server.backup.utils.FullBackupUtils;
+
import libcore.io.IoUtils;
import java.io.File;
@@ -78,7 +80,7 @@
mNewStateName = new File(mStateDir,
pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX);
- mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME);
+ mManifestFile = new File(mDataDir, RefactoredBackupManagerService.BACKUP_MANIFEST_FILENAME);
}
public void backupOnePackage() throws IOException {
@@ -188,7 +190,7 @@
if (DEBUG) {
Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
}
- BackupManagerService.writeAppManifest(
+ FullBackupUtils.writeAppManifest(
mPackage, mPackageManager, mManifestFile, false, false);
FullBackup.backupToTar(mPackage.packageName, FullBackup.KEY_VALUE_DATA_TOKEN, null,
mDataDir.getAbsolutePath(),
@@ -251,7 +253,7 @@
t.start();
// Now pull data from the app and stuff it into the output
- BackupManagerService.routeSocketDataToOutput(pipes[0], mOutput);
+ FullBackupUtils.routeSocketDataToOutput(pipes[0], mOutput);
if (!mBackupManagerService.waitUntilOperationComplete(token)) {
Slog.e(TAG, "Full backup failed on package " + mCurrentPackage.packageName);
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
index b62bb5c..a2de8e7 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -13,6 +13,8 @@
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.backup.restore.PerformAdbRestoreTask;
+
import libcore.io.IoUtils;
import java.io.File;
@@ -41,7 +43,7 @@
private final File mDataDir;
FileMetadata mInfo;
- BackupManagerService.PerformAdbRestoreTask mRestoreTask;
+ PerformAdbRestoreTask mRestoreTask;
ParcelFileDescriptor mInFD;
IBackupAgent mAgent;
int mToken;
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index d8411e2..5dfb0bc 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -71,7 +71,7 @@
if (delay <= 0) {
delay = interval + new Random().nextInt((int) fuzz);
}
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes");
}
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService)
@@ -110,7 +110,7 @@
}
// Time to run a key/value backup!
- Trampoline service = BackupManagerService.getInstance();
+ Trampoline service = RefactoredBackupManagerService.getInstance();
try {
service.backupNow();
} catch (RemoteException e) {}
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index 8d91e0d..f658f22 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -30,6 +30,8 @@
import android.os.ParcelFileDescriptor;
import android.util.Slog;
+import com.android.server.backup.utils.AppBackupUtils;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -140,7 +142,7 @@
int N = pkgs.size();
for (int a = N-1; a >= 0; a--) {
PackageInfo pkg = pkgs.get(a);
- if (!BackupManagerService.appIsEligibleForBackup(pkg.applicationInfo, pm)) {
+ if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo, pm)) {
pkgs.remove(a);
}
}
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 8b4cc7f..fe4902f 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -653,7 +653,7 @@
// Persistently track the need to do a full init
private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
- private ArraySet<String> mPendingInits = new ArraySet<>(); // transport names
+ private final ArraySet<String> mPendingInits = new ArraySet<>(); // transport names
// Round-robin queue for scheduling full backup passes
private static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file
@@ -1188,7 +1188,7 @@
if (uri == null) {
return;
}
- String pkgName = uri.getSchemeSpecificPart();
+ final String pkgName = uri.getSchemeSpecificPart();
if (pkgName != null) {
pkgList = new String[]{pkgName};
}
@@ -1196,7 +1196,7 @@
// At package-changed we only care about looking at new transport states
if (changed) {
- String[] components =
+ final String[] components =
intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
if (MORE_DEBUG) {
@@ -1206,7 +1206,8 @@
}
}
- mTransportManager.onPackageChanged(pkgName, components);
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageChanged(pkgName, components));
return; // nothing more to do in the PACKAGE_CHANGED case
}
@@ -1238,7 +1239,7 @@
}
// If they're full-backup candidates, add them there instead
final long now = System.currentTimeMillis();
- for (String packageName : pkgList) {
+ for (final String packageName : pkgList) {
try {
PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
if (AppBackupUtils.appGetsFullBackup(app)
@@ -1256,7 +1257,8 @@
writeFullBackupScheduleAsync();
}
- mTransportManager.onPackageAdded(packageName);
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageAdded(packageName));
} catch (NameNotFoundException e) {
// doesn't really exist; ignore it
@@ -1280,8 +1282,9 @@
removePackageParticipantsLocked(pkgList, uid);
}
}
- for (String pkgName : pkgList) {
- mTransportManager.onPackageRemoved(pkgName);
+ for (final String pkgName : pkgList) {
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageRemoved(pkgName));
}
}
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index fcd929a..245bc1d 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -98,7 +98,7 @@
protected boolean isRefactoredServiceEnabled() {
return Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.BACKUP_REFACTORED_SERVICE_DISABLED, 1) == 0;
+ Settings.Global.BACKUP_REFACTORED_SERVICE_DISABLED, 0) == 0;
}
protected int binderGetCallingUid() {
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index 9aae384..7a0173f 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -341,9 +341,9 @@
private class TransportConnection implements ServiceConnection {
// Hold mTransportsLock to access these fields so as to provide a consistent view of them.
- private IBackupTransport mBinder;
+ private volatile IBackupTransport mBinder;
private final List<TransportReadyCallback> mListeners = new ArrayList<>();
- private String mTransportName;
+ private volatile String mTransportName;
private final ComponentName mTransportComponent;
@@ -426,25 +426,24 @@
+ rebindTimeout + "ms");
}
+ // Intentionally not synchronized -- the variable is volatile and changes to its value
+ // are inside synchronized blocks, providing a memory sync barrier; and this method
+ // does not touch any other state protected by that lock.
private IBackupTransport getBinder() {
- synchronized (mTransportLock) {
- return mBinder;
- }
+ return mBinder;
}
+ // Intentionally not synchronized; same as getBinder()
private String getName() {
- synchronized (mTransportLock) {
- return mTransportName;
- }
+ return mTransportName;
}
+ // Intentionally not synchronized; same as getBinder()
private void bindIfUnbound() {
- synchronized (mTransportLock) {
- if (mBinder == null) {
- Slog.d(TAG,
- "Rebinding to transport " + mTransportComponent.flattenToShortString());
- bindToTransport(mTransportComponent, this);
- }
+ if (mBinder == null) {
+ Slog.d(TAG,
+ "Rebinding to transport " + mTransportComponent.flattenToShortString());
+ bindToTransport(mTransportComponent, this);
}
}
diff --git a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
index a6897d0..1df0bf0 100644
--- a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
@@ -23,6 +23,7 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.server.backup.RefactoredBackupManagerService;
@@ -38,19 +39,22 @@
public void onReceive(Context context, Intent intent) {
if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
synchronized (backupManagerService.getQueueLock()) {
+ final ArraySet<String> pendingInits = backupManagerService.getPendingInits();
if (DEBUG) {
- Slog.v(TAG, "Running a device init");
+ Slog.v(TAG, "Running a device init; " + pendingInits.size() + " pending");
}
- String[] pendingInits = (String[]) backupManagerService.getPendingInits().toArray();
- backupManagerService.clearPendingInits();
- PerformInitializeTask initTask = new PerformInitializeTask(backupManagerService,
- pendingInits, null);
+ if (pendingInits.size() > 0) {
+ final String[] transports = pendingInits.toArray(new String[pendingInits.size()]);
+ PerformInitializeTask initTask = new PerformInitializeTask(backupManagerService,
+ transports, null);
- // Acquire the wakelock and pass it to the init thread. it will
- // be released once init concludes.
- backupManagerService.getWakelock().acquire();
- backupManagerService.getBackupHandler().post(initTask);
+ // Acquire the wakelock and pass it to the init thread. it will
+ // be released once init concludes.
+ backupManagerService.clearPendingInits();
+ backupManagerService.getWakelock().acquire();
+ backupManagerService.getBackupHandler().post(initTask);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 83bd9eb..5106c8d 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -20,6 +20,7 @@
import android.database.ContentObserver;
import android.os.BatteryStats;
+import android.os.PowerManager;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
@@ -291,6 +292,8 @@
if (mActivityManagerInternal.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
+ intent.putExtra(Intent.EXTRA_REASON,
+ PowerManager.SHUTDOWN_LOW_BATTERY);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
@@ -310,6 +313,8 @@
if (mActivityManagerInternal.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
+ intent.putExtra(Intent.EXTRA_REASON,
+ PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bfe5040..348c799 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.NETID_UNSET;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.getNetworkTypeName;
@@ -90,6 +91,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -128,7 +130,6 @@
import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.LingerMonitor;
import com.android.server.connectivity.MockableSystemProperties;
-import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkDiagnostics;
import com.android.server.connectivity.NetworkMonitor;
@@ -781,6 +782,13 @@
mNetworksDefined++; // used only in the log() statement below.
}
+ // Do the same for Ethernet, since it's often not specified in the configs, although many
+ // devices can use it via USB host adapters.
+ if (mNetConfigs[TYPE_ETHERNET] == null && hasService(Context.ETHERNET_SERVICE)) {
+ mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET);
+ mNetworksDefined++;
+ }
+
if (VDBG) log("mNetworksDefined=" + mNetworksDefined);
mProtectedNetworks = new ArrayList<Integer>();
@@ -2205,7 +2213,7 @@
// A network factory has connected. Send it all current NetworkRequests.
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
if (nri.request.isListen()) continue;
- NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
+ NetworkAgentInfo nai = getNetworkForRequest(nri.request.requestId);
ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK,
(nai != null ? nai.getCurrentScore() : 0), 0, nri.request);
}
@@ -2282,9 +2290,9 @@
// Remove all previously satisfied requests.
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest request = nai.requestAt(i);
- NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId);
+ NetworkAgentInfo currentNetwork = getNetworkForRequest(request.requestId);
if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
- mNetworkForRequestId.remove(request.requestId);
+ clearNetworkForRequest(request.requestId);
sendUpdatedScoreToFactories(request, 0);
}
}
@@ -2360,7 +2368,7 @@
}
}
rematchAllNetworksAndRequests(null, 0);
- if (nri.request.isRequest() && mNetworkForRequestId.get(nri.request.requestId) == null) {
+ if (nri.request.isRequest() && getNetworkForRequest(nri.request.requestId) == null) {
sendUpdatedScoreToFactories(nri.request, 0);
}
}
@@ -2415,7 +2423,7 @@
// 2. Unvalidated WiFi will not be reaped when validated cellular
// is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular.
- mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() <
+ getNetworkForRequest(nri.request.requestId).getCurrentScore() <
nai.getCurrentScoreAsValidated())) {
return false;
}
@@ -2442,7 +2450,7 @@
if (mNetworkRequests.get(nri.request) == null) {
return;
}
- if (mNetworkForRequestId.get(nri.request.requestId) != null) {
+ if (getNetworkForRequest(nri.request.requestId) != null) {
return;
}
if (VDBG || (DBG && nri.request.isRequest())) {
@@ -2482,7 +2490,7 @@
mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (nri.request.isRequest()) {
boolean wasKept = false;
- NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
+ NetworkAgentInfo nai = getNetworkForRequest(nri.request.requestId);
if (nai != null) {
boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
nai.removeRequest(nri.request.requestId);
@@ -2499,7 +2507,7 @@
} else {
wasKept = true;
}
- mNetworkForRequestId.remove(nri.request.requestId);
+ clearNetworkForRequest(nri.request.requestId);
if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
// Went from foreground to background.
updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
@@ -4296,7 +4304,8 @@
* and the are the highest scored network available.
* the are keyed off the Requests requestId.
*/
- // TODO: Yikes, this is accessed on multiple threads: add synchronization.
+ // NOTE: Accessed on multiple threads, must be synchronized on itself.
+ @GuardedBy("mNetworkForRequestId")
private final SparseArray<NetworkAgentInfo> mNetworkForRequestId =
new SparseArray<NetworkAgentInfo>();
@@ -4326,8 +4335,26 @@
// priority networks like Wi-Fi are active.
private final NetworkRequest mDefaultMobileDataRequest;
+ private NetworkAgentInfo getNetworkForRequest(int requestId) {
+ synchronized (mNetworkForRequestId) {
+ return mNetworkForRequestId.get(requestId);
+ }
+ }
+
+ private void clearNetworkForRequest(int requestId) {
+ synchronized (mNetworkForRequestId) {
+ mNetworkForRequestId.remove(requestId);
+ }
+ }
+
+ private void setNetworkForRequest(int requestId, NetworkAgentInfo nai) {
+ synchronized (mNetworkForRequestId) {
+ mNetworkForRequestId.put(requestId, nai);
+ }
+ }
+
private NetworkAgentInfo getDefaultNetwork() {
- return mNetworkForRequestId.get(mDefaultRequest.requestId);
+ return getNetworkForRequest(mDefaultRequest.requestId);
}
private boolean isDefaultNetwork(NetworkAgentInfo nai) {
@@ -4881,7 +4908,7 @@
// requests or not, and doesn't affect the network's score.
if (nri.request.isListen()) continue;
- final NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
+ final NetworkAgentInfo currentNetwork = getNetworkForRequest(nri.request.requestId);
final boolean satisfies = newNetwork.satisfies(nri.request);
if (newNetwork == currentNetwork && satisfies) {
if (VDBG) {
@@ -4913,7 +4940,7 @@
if (VDBG) log(" accepting network in place of null");
}
newNetwork.unlingerRequest(nri.request);
- mNetworkForRequestId.put(nri.request.requestId, newNetwork);
+ setNetworkForRequest(nri.request.requestId, newNetwork);
if (!newNetwork.addRequest(nri.request)) {
Slog.wtf(TAG, "BUG: " + newNetwork.name() + " already has " + nri.request);
}
@@ -4947,7 +4974,7 @@
}
newNetwork.removeRequest(nri.request.requestId);
if (currentNetwork == newNetwork) {
- mNetworkForRequestId.remove(nri.request.requestId);
+ clearNetworkForRequest(nri.request.requestId);
sendUpdatedScoreToFactories(nri.request, 0);
} else {
Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
@@ -5522,6 +5549,11 @@
return new WakeupMessage(c, h, s, cmd, 0, 0, obj);
}
+ @VisibleForTesting
+ public boolean hasService(String name) {
+ return ServiceManager.checkService(name) != null;
+ }
+
private void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
int newNetid = NETID_UNSET;
int prevNetid = NETID_UNSET;
diff --git a/services/core/java/com/android/server/DiskStatsService.java b/services/core/java/com/android/server/DiskStatsService.java
index 800081e..2d2c6b0 100644
--- a/services/core/java/com/android/server/DiskStatsService.java
+++ b/services/core/java/com/android/server/DiskStatsService.java
@@ -202,6 +202,8 @@
JSONObject json = new JSONObject(jsonString);
pw.print("App Size: ");
pw.println(json.getLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY));
+ pw.print("App Data Size: ");
+ pw.println(json.getLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY));
pw.print("App Cache Size: ");
pw.println(json.getLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY));
pw.print("Photos Size: ");
@@ -220,6 +222,8 @@
pw.println(json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY));
pw.print("App Sizes: ");
pw.println(json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY));
+ pw.print("App Data Sizes: ");
+ pw.println(json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY));
pw.print("Cache Sizes: ");
pw.println(json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY));
} catch (IOException | JSONException e) {
@@ -235,6 +239,8 @@
proto.write(DiskStatsCachedValuesProto.AGG_APPS_SIZE,
json.getLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY));
+ proto.write(DiskStatsCachedValuesProto.AGG_APPS_DATA_SIZE,
+ json.getLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY));
proto.write(DiskStatsCachedValuesProto.AGG_APPS_CACHE_SIZE,
json.getLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY));
proto.write(DiskStatsCachedValuesProto.PHOTOS_SIZE,
@@ -252,22 +258,26 @@
JSONArray packageNamesArray = json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY);
JSONArray appSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY);
+ JSONArray appDataSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY);
JSONArray cacheSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY);
final int len = packageNamesArray.length();
- if (len == appSizesArray.length() && len == cacheSizesArray.length()) {
+ if (len == appSizesArray.length()
+ && len == appDataSizesArray.length()
+ && len == cacheSizesArray.length()) {
for (int i = 0; i < len; i++) {
long packageToken = proto.start(DiskStatsCachedValuesProto.APP_SIZES);
proto.write(DiskStatsAppSizesProto.PACKAGE_NAME,
packageNamesArray.getString(i));
proto.write(DiskStatsAppSizesProto.APP_SIZE, appSizesArray.getLong(i));
+ proto.write(DiskStatsAppSizesProto.APP_DATA_SIZE, appDataSizesArray.getLong(i));
proto.write(DiskStatsAppSizesProto.CACHE_SIZE, cacheSizesArray.getLong(i));
proto.end(packageToken);
}
} else {
- Slog.wtf(TAG, "Sizes of packageNamesArray, appSizesArray and cacheSizesArray "
- + "are not the same");
+ Slog.wtf(TAG, "Sizes of packageNamesArray, appSizesArray, appDataSizesArray "
+ + " and cacheSizesArray are not the same");
}
proto.end(cachedValuesToken);
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 3056831..2e1f142 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -33,6 +33,7 @@
import android.net.IpSecTransform;
import android.net.IpSecTransformResponse;
import android.net.IpSecUdpEncapResponse;
+import android.net.NetworkUtils;
import android.net.util.NetdService;
import android.os.Binder;
import android.os.IBinder;
@@ -42,11 +43,14 @@
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -54,6 +58,7 @@
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.concurrent.atomic.AtomicInteger;
+
import libcore.io.IoUtils;
/** @hide */
@@ -252,7 +257,11 @@
return (mReferenceCount.get() > 0);
}
- public void checkOwnerOrSystemAndThrow() {
+ /**
+ * Ensures that the caller is either the owner of this resource or has the system UID and
+ * throws a SecurityException otherwise.
+ */
+ public void checkOwnerOrSystem() {
if (uid != Binder.getCallingUid()
&& android.os.Process.SYSTEM_UID != Binder.getCallingUid()) {
throw new SecurityException("Only the owner may access managed resources!");
@@ -335,12 +344,12 @@
private class ManagedResourceArray<T extends ManagedResource> {
SparseArray<T> mArray = new SparseArray<>();
- T get(int key) {
+ T getAndCheckOwner(int key) {
T val = mArray.get(key);
// The value should never be null unless the resource doesn't exist
// (since we do not allow null resources to be added).
if (val != null) {
- val.checkOwnerOrSystemAndThrow();
+ val.checkOwnerOrSystem();
}
return val;
}
@@ -405,12 +414,8 @@
.ipSecDeleteSecurityAssociation(
mResourceId,
direction,
- (mConfig.getLocalAddress() != null)
- ? mConfig.getLocalAddress().getHostAddress()
- : "",
- (mConfig.getRemoteAddress() != null)
- ? mConfig.getRemoteAddress().getHostAddress()
- : "",
+ mConfig.getLocalAddress(),
+ mConfig.getRemoteAddress(),
spi);
} catch (ServiceSpecificException e) {
// FIXME: get the error code and throw is at an IOException from Errno Exception
@@ -638,11 +643,45 @@
}
}
+ /**
+ * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
+ * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
+ */
+ private static void checkInetAddress(String inetAddress) {
+ if (TextUtils.isEmpty(inetAddress)) {
+ throw new IllegalArgumentException("Unspecified address");
+ }
+
+ InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress);
+
+ if (checkAddr.isAnyLocalAddress()) {
+ throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
+ }
+ }
+
+ /**
+ * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
+ * DIRECTION_IN or DIRECTION_OUT
+ */
+ private static void checkDirection(int direction) {
+ switch (direction) {
+ case IpSecTransform.DIRECTION_OUT:
+ case IpSecTransform.DIRECTION_IN:
+ return;
+ }
+ throw new IllegalArgumentException("Invalid Direction: " + direction);
+ }
+
@Override
/** Get a new SPI and maintain the reservation in the system server */
public synchronized IpSecSpiResponse reserveSecurityParameterIndex(
int direction, String remoteAddress, int requestedSpi, IBinder binder)
throws RemoteException {
+ checkDirection(direction);
+ checkInetAddress(remoteAddress);
+ /* requestedSpi can be anything in the int range, so no check is needed. */
+ checkNotNull(binder, "Null Binder passed to reserveSecurityParameterIndex");
+
int resourceId = mNextResourceId.getAndIncrement();
int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
@@ -651,9 +690,7 @@
try {
if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).spi.isAvailable()) {
return new IpSecSpiResponse(
- IpSecManager.Status.RESOURCE_UNAVAILABLE,
- INVALID_RESOURCE_ID,
- spi);
+ IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
}
spi =
mSrvConfig
@@ -686,7 +723,7 @@
throws RemoteException {
// We want to non-destructively get so that we can check credentials before removing
// this from the records.
- T record = resArray.get(resourceId);
+ T record = resArray.getAndCheckOwner(resourceId);
if (record == null) {
throw new IllegalArgumentException(
@@ -751,6 +788,8 @@
throw new IllegalArgumentException(
"Specified port number must be a valid non-reserved UDP port");
}
+ checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
+
int resourceId = mNextResourceId.getAndIncrement();
FileDescriptor sockFd = null;
try {
@@ -792,6 +831,68 @@
}
/**
+ * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
+ * IllegalArgumentException if they are not.
+ */
+ private void checkIpSecConfig(IpSecConfig config) {
+ if (config.getLocalAddress() == null) {
+ throw new IllegalArgumentException("Invalid null Local InetAddress");
+ }
+
+ if (config.getRemoteAddress() == null) {
+ throw new IllegalArgumentException("Invalid null Remote InetAddress");
+ }
+
+ switch (config.getMode()) {
+ case IpSecTransform.MODE_TRANSPORT:
+ if (!config.getLocalAddress().isEmpty()) {
+ throw new IllegalArgumentException("Non-empty Local Address");
+ }
+ // Must be valid, and not a wildcard
+ checkInetAddress(config.getRemoteAddress());
+ break;
+ case IpSecTransform.MODE_TUNNEL:
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid IpSecTransform.mode: " + config.getMode());
+ }
+
+ switch (config.getEncapType()) {
+ case IpSecTransform.ENCAP_NONE:
+ break;
+ case IpSecTransform.ENCAP_ESPINUDP:
+ case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
+ if (mUdpSocketRecords.getAndCheckOwner(
+ config.getEncapSocketResourceId()) == null) {
+ throw new IllegalStateException(
+ "No Encapsulation socket for Resource Id: "
+ + config.getEncapSocketResourceId());
+ }
+
+ int port = config.getEncapRemotePort();
+ if (port <= 0 || port > 0xFFFF) {
+ throw new IllegalArgumentException("Invalid remote UDP port: " + port);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
+ }
+
+ for (int direction : DIRECTIONS) {
+ IpSecAlgorithm crypt = config.getEncryption(direction);
+ IpSecAlgorithm auth = config.getAuthentication(direction);
+ if (crypt == null && auth == null) {
+ throw new IllegalArgumentException("Encryption and Authentication are both null");
+ }
+
+ if (mSpiRecords.getAndCheckOwner(config.getSpiResourceId(direction)) == null) {
+ throw new IllegalStateException("No SPI for specified Resource Id");
+ }
+ }
+ }
+
+ /**
* Create a transport mode transform, which represent two security associations (one in each
* direction) in the kernel. The transform will be cached by the system server and must be freed
* when no longer needed. It is possible to free one, deleting the SA from underneath sockets
@@ -801,17 +902,19 @@
@Override
public synchronized IpSecTransformResponse createTransportModeTransform(
IpSecConfig c, IBinder binder) throws RemoteException {
+ checkIpSecConfig(c);
+ checkNotNull(binder, "Null Binder passed to createTransportModeTransform");
int resourceId = mNextResourceId.getAndIncrement();
if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).transform.isAvailable()) {
return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
SpiRecord[] spis = new SpiRecord[DIRECTIONS.length];
- // TODO: Basic input validation here since it's coming over the Binder
+
int encapType, encapLocalPort = 0, encapRemotePort = 0;
UdpSocketRecord socketRecord = null;
encapType = c.getEncapType();
if (encapType != IpSecTransform.ENCAP_NONE) {
- socketRecord = mUdpSocketRecords.get(c.getEncapLocalResourceId());
+ socketRecord = mUdpSocketRecords.getAndCheckOwner(c.getEncapSocketResourceId());
encapLocalPort = socketRecord.getPort();
encapRemotePort = c.getEncapRemotePort();
}
@@ -820,23 +923,18 @@
IpSecAlgorithm auth = c.getAuthentication(direction);
IpSecAlgorithm crypt = c.getEncryption(direction);
- spis[direction] = mSpiRecords.get(c.getSpiResourceId(direction));
+ spis[direction] = mSpiRecords.getAndCheckOwner(c.getSpiResourceId(direction));
int spi = spis[direction].getSpi();
try {
- mSrvConfig.getNetdInstance()
+ mSrvConfig
+ .getNetdInstance()
.ipSecAddSecurityAssociation(
resourceId,
c.getMode(),
direction,
- (c.getLocalAddress() != null)
- ? c.getLocalAddress().getHostAddress()
- : "",
- (c.getRemoteAddress() != null)
- ? c.getRemoteAddress().getHostAddress()
- : "",
- (c.getNetwork() != null)
- ? c.getNetwork().getNetworkHandle()
- : 0,
+ c.getLocalAddress(),
+ c.getRemoteAddress(),
+ (c.getNetwork() != null) ? c.getNetwork().getNetworkHandle() : 0,
spi,
(auth != null) ? auth.getName() : "",
(auth != null) ? auth.getKey() : null,
@@ -879,7 +977,7 @@
// Synchronize liberally here because we are using ManagedResources in this block
TransformRecord info;
// FIXME: this code should be factored out into a security check + getter
- info = mTransformRecords.get(resourceId);
+ info = mTransformRecords.getAndCheckOwner(resourceId);
if (info == null) {
throw new IllegalArgumentException("Transform " + resourceId + " is not active");
@@ -899,12 +997,8 @@
socket.getFileDescriptor(),
resourceId,
direction,
- (c.getLocalAddress() != null)
- ? c.getLocalAddress().getHostAddress()
- : "",
- (c.getRemoteAddress() != null)
- ? c.getRemoteAddress().getHostAddress()
- : "",
+ c.getLocalAddress(),
+ c.getRemoteAddress(),
info.getSpiRecord(direction).getSpi());
}
} catch (ServiceSpecificException e) {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 340d672..0fd59ea 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -18,7 +18,6 @@
import android.app.ActivityManager;
import android.annotation.NonNull;
-import android.content.pm.PackageManagerInternal;
import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.content.PackageMonitor;
@@ -56,6 +55,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 2f95aa2..ba3afc3 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1140,17 +1140,6 @@
}
@Override
- public void setInterfaceIpv6NdOffload(String iface, boolean enable) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute(
- "interface", "ipv6ndoffload", iface, (enable ? "enable" : "disable"));
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
-
- @Override
public void addRoute(int netId, RouteInfo route) {
modifyRoute("add", "" + netId, route);
}
@@ -1991,8 +1980,12 @@
final String[] domainStrs = domains == null ? new String[0] : domains.split(" ");
final int[] params = { sampleValidity, successThreshold, minSamples, maxSamples };
+ final boolean useTls = false;
+ final String tlsHostname = "";
+ final String[] tlsFingerprints = new String[0];
try {
- mNetdService.setResolverConfiguration(netId, servers, domainStrs, params);
+ mNetdService.setResolverConfiguration(netId, servers, domainStrs, params,
+ useTls, tlsHostname, tlsFingerprints);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index c7e22be..55391b3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -386,17 +386,6 @@
}
}
- private static String escapeNull(String arg) {
- if (TextUtils.isEmpty(arg)) {
- return "!";
- } else {
- if (arg.indexOf('\0') != -1 || arg.indexOf(' ') != -1) {
- throw new IllegalArgumentException(arg);
- }
- return arg;
- }
- }
-
/** List of crypto types.
* These must match CRYPT_TYPE_XXX in cryptfs.h AND their
* corresponding commands in CommandListener.cpp */
@@ -416,10 +405,6 @@
private final Callbacks mCallbacks;
private final LockPatternUtils mLockPatternUtils;
- private final Object mUnmountLock = new Object();
- @GuardedBy("mUnmountLock")
- private CountDownLatch mUnmountSignal;
-
/**
* The size of the crypto algorithm key in bits for OBB files. Currently
* Twofish is used which takes 128-bit keys.
@@ -699,18 +684,6 @@
}
};
- @Override
- public void waitForAsecScan() {
- throw new UnsupportedOperationException();
- }
-
- private void waitForLatch(CountDownLatch latch, String condition) {
- try {
- waitForLatch(latch, condition, -1);
- } catch (TimeoutException ignored) {
- }
- }
-
private void waitForLatch(CountDownLatch latch, String condition, long timeoutMillis)
throws TimeoutException {
final long startMillis = SystemClock.elapsedRealtime();
@@ -1533,48 +1506,6 @@
}
@Override
- public boolean isUsbMassStorageConnected() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setUsbMassStorageEnabled(boolean enable) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isUsbMassStorageEnabled() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String getVolumeState(String mountPoint) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isExternalStorageEmulated() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int mountVolume(String path) {
- mount(findVolumeIdForPathOrThrow(path));
- return 0;
- }
-
- @Override
- public void unmountVolume(String path, boolean force, boolean removeEncryption) {
- unmount(findVolumeIdForPathOrThrow(path));
- }
-
- @Override
- public int formatVolume(String path) {
- format(findVolumeIdForPathOrThrow(path));
- return 0;
- }
-
- @Override
public void mount(String volId) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
@@ -1594,22 +1525,6 @@
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
final VolumeInfo vol = findVolumeByIdOrThrow(volId);
-
- // TODO: expand PMS to know about multiple volumes
- if (vol.isPrimaryPhysical()) {
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mUnmountLock) {
- mUnmountSignal = new CountDownLatch(1);
- mPms.updateExternalMediaStatus(false, true);
- waitForLatch(mUnmountSignal, "mUnmountSignal");
- mUnmountSignal = null;
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
try {
mVold.unmount(vol.id);
} catch (Exception e) {
@@ -2004,11 +1919,6 @@
}
}
- @Override
- public int[] getStorageUsers(String path) {
- throw new UnsupportedOperationException();
- }
-
private void warnOnNotMounted() {
synchronized (mLock) {
for (int i = 0; i < mVolumes.size(); i++) {
@@ -2023,79 +1933,6 @@
Slog.w(TAG, "No primary storage mounted!");
}
- @Override
- public String[] getSecureContainerList() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int createSecureContainer(String id, int sizeMb, String fstype, String key,
- int ownerUid, boolean external) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int resizeSecureContainer(String id, int sizeMb, String key) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int finalizeSecureContainer(String id) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int fixPermissionsSecureContainer(String id, int gid, String filename) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int destroySecureContainer(String id, boolean force) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int unmountSecureContainer(String id, boolean force) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isSecureContainerMounted(String id) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int renameSecureContainer(String oldId, String newId) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String getSecureContainerPath(String id) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String getSecureContainerFilesystemPath(String id) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void finishMediaUpdate() {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("no permission to call finishMediaUpdate()");
- }
- if (mUnmountSignal != null) {
- mUnmountSignal.countDown();
- } else {
- Slog.w(TAG, "Odd, nobody asked to unmount?");
- }
- }
-
private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
if (callerUid == android.os.Process.SYSTEM_UID) {
return true;
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 8ae592f..4e15e5d 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2969,9 +2969,13 @@
* have users launching arbitrary activities by tricking users to
* interact with malicious notifications.
*/
- checkKeyIntent(
+ if (!checkKeyIntent(
Binder.getCallingUid(),
- intent);
+ intent)) {
+ onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+ "invalid intent in bundle returned");
+ return;
+ }
doNotification(
mAccounts,
account,
@@ -3366,9 +3370,13 @@
Intent intent = null;
if (result != null
&& (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
- checkKeyIntent(
+ if (!checkKeyIntent(
Binder.getCallingUid(),
- intent);
+ intent)) {
+ onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+ "invalid intent in bundle returned");
+ return;
+ }
}
IAccountManagerResponse response;
if (mExpectActivityLaunch && result != null
@@ -4716,9 +4724,7 @@
* into launching arbitrary intents on the device via by tricking to click authenticator
* supplied entries in the system Settings app.
*/
- protected void checkKeyIntent(
- int authUid,
- Intent intent) throws SecurityException {
+ protected boolean checkKeyIntent(int authUid, Intent intent) {
intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
@@ -4727,6 +4733,9 @@
try {
PackageManager pm = mContext.getPackageManager();
ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
+ if (resolveInfo == null) {
+ return false;
+ }
ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
int targetUid = targetActivityInfo.applicationInfo.uid;
if (!isExportedSystemActivity(targetActivityInfo)
@@ -4736,9 +4745,10 @@
String activityName = targetActivityInfo.name;
String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
+ "does not share a signature with the supplying authenticator (%s).";
- throw new SecurityException(
- String.format(tmpl, activityName, pkgName, mAccountType));
+ Log.e(TAG, String.format(tmpl, activityName, pkgName, mAccountType));
+ return false;
}
+ return true;
} finally {
Binder.restoreCallingIdentity(bid);
}
@@ -4888,9 +4898,13 @@
}
if (result != null
&& (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
- checkKeyIntent(
+ if (!checkKeyIntent(
Binder.getCallingUid(),
- intent);
+ intent)) {
+ onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+ "invalid intent in bundle returned");
+ return;
+ }
}
if (result != null
&& !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
@@ -5285,7 +5299,7 @@
== PackageManager.PERMISSION_GRANTED) {
// Checks runtime permission revocation.
final int opCode = AppOpsManager.permissionToOpCode(perm);
- if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
+ if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
opCode, uid, packageName) == AppOpsManager.MODE_ALLOWED) {
return true;
}
@@ -5306,7 +5320,7 @@
Log.v(TAG, " caller uid " + callingUid + " has " + perm);
}
final int opCode = AppOpsManager.permissionToOpCode(perm);
- if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
+ if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
return true;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 90ad8a5..2131731 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3150,7 +3150,7 @@
sr.userId, sr.crashCount, sr.shortName, app.pid);
bringDownServiceLocked(sr);
} else if (!allowRestart
- || !mAm.mUserController.isUserRunningLocked(sr.userId, 0)) {
+ || !mAm.mUserController.isUserRunning(sr.userId, 0)) {
bringDownServiceLocked(sr);
} else {
boolean canceled = scheduleServiceRestartLocked(sr, true);
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
new file mode 100644
index 0000000..8bcbfbe
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2017 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 static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.getStackIdForWindowingMode;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_PRIVATE;
+import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.proto.ActivityDisplayProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.proto.ActivityDisplayProto.STACKS;
+import static com.android.server.am.proto.ActivityDisplayProto.ID;
+
+import android.app.ActivityManagerInternal;
+import android.app.WindowConfiguration;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wm.ConfigurationContainer;
+
+import java.util.ArrayList;
+
+/**
+ * Exactly one of these classes per Display in the system. Capable of holding zero or more
+ * attached {@link ActivityStack}s.
+ */
+class ActivityDisplay extends ConfigurationContainer {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_AM;
+ private static final String TAG_STACK = TAG + POSTFIX_STACK;
+
+ static final int POSITION_TOP = Integer.MAX_VALUE;
+ static final int POSITION_BOTTOM = Integer.MIN_VALUE;
+
+ private ActivityStackSupervisor mSupervisor;
+ /** Actual Display this object tracks. */
+ int mDisplayId;
+ Display mDisplay;
+
+ /** All of the stacks on this display. Order matters, topmost stack is in front of all other
+ * stacks, bottommost behind. Accessed directly by ActivityManager package classes */
+ final ArrayList<ActivityStack> mStacks = new ArrayList<>();
+
+ /** Array of all UIDs that are present on the display. */
+ private IntArray mDisplayAccessUIDs = new IntArray();
+
+ /** All tokens used to put activities on this stack to sleep (including mOffToken) */
+ final ArrayList<ActivityManagerInternal.SleepToken> mAllSleepTokens = new ArrayList<>();
+ /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
+ ActivityManagerInternal.SleepToken mOffToken;
+
+ private boolean mSleeping;
+
+ ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
+ mSupervisor = supervisor;
+ mDisplayId = displayId;
+ final Display display = supervisor.mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ throw new IllegalStateException("Display does not exist displayId=" + displayId);
+ }
+ mDisplay = display;
+ }
+
+ void addChild(ActivityStack stack, int position) {
+ if (position == POSITION_BOTTOM) {
+ position = 0;
+ } else if (position == POSITION_TOP) {
+ position = mStacks.size();
+ }
+ if (DEBUG_STACK) Slog.v(TAG_STACK, "addChild: attaching " + stack
+ + " to displayId=" + mDisplayId + " position=" + position);
+ positionChildAt(stack, position);
+ mSupervisor.mService.updateSleepIfNeededLocked();
+ }
+
+ void removeChild(ActivityStack stack) {
+ if (DEBUG_STACK) Slog.v(TAG_STACK, "removeChild: detaching " + stack
+ + " from displayId=" + mDisplayId);
+ mStacks.remove(stack);
+ mSupervisor.mService.updateSleepIfNeededLocked();
+ }
+
+ void positionChildAtTop(ActivityStack stack) {
+ positionChildAt(stack, mStacks.size());
+ }
+
+ void positionChildAtBottom(ActivityStack stack) {
+ positionChildAt(stack, 0);
+ }
+
+ private void positionChildAt(ActivityStack stack, int position) {
+ mStacks.remove(stack);
+ mStacks.add(getTopInsertPosition(stack, position), stack);
+ }
+
+ private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
+ int position = mStacks.size();
+ if (position > 0) {
+ final ActivityStack topStack = mStacks.get(position - 1);
+ if (topStack.getWindowConfiguration().isAlwaysOnTop() && topStack != stack) {
+ // If the top stack is always on top, we move this stack just below it.
+ position--;
+ }
+ }
+ return Math.min(position, candidatePosition);
+ }
+
+ <T extends ActivityStack> T getStack(int stackId) {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ if (stack.mStackId == stackId) {
+ return (T) stack;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the topmost stack on the display that is compatible with the input windowing mode and
+ * activity type. {@code null} means no compatible stack on the display.
+ * @see ConfigurationContainer#isCompatible(int, int)
+ */
+ <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ // TODO: Should undefined windowing and activity type be compatible with standard type?
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return (T) stack;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @see #getStack(int, int)
+ * @see #createStack(int, int, boolean)
+ */
+ <T extends ActivityStack> T getOrCreateStack(int windowingMode, int activityType,
+ boolean onTop) {
+ T stack = getStack(windowingMode, activityType);
+ if (stack != null) {
+ return stack;
+ }
+ return createStack(windowingMode, activityType, onTop);
+ }
+
+ /**
+ * Creates a stack matching the input windowing mode and activity type on this display.
+ * @param windowingMode The windowing mode the stack should be created in. If
+ * {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack will
+ * be created in {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}.
+ * @param activityType The activityType the stack should be created in. If
+ * {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will
+ * be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
+ * @param onTop If true the stack will be created at the top of the display, else at the bottom.
+ * @return The newly created stack.
+ */
+ <T extends ActivityStack> T createStack(int windowingMode, int activityType, boolean onTop) {
+
+ if (activityType == ACTIVITY_TYPE_UNDEFINED) {
+ // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
+ // anything else should be passing it in anyways...
+ activityType = ACTIVITY_TYPE_STANDARD;
+ }
+
+ if (activityType != ACTIVITY_TYPE_STANDARD) {
+ // For now there can be only one stack of a particular non-standard activity type on a
+ // display. So, get that ignoring whatever windowing mode it is currently in.
+ T stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+ if (stack != null) {
+ throw new IllegalArgumentException("Stack=" + stack + " of activityType="
+ + activityType + " already on display=" + this + ". Can't have multiple.");
+ }
+ }
+
+ final ActivityManagerService service = mSupervisor.mService;
+ if (!mSupervisor.isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
+ service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement,
+ service.mSupportsPictureInPicture, activityType)) {
+ throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
+ + windowingMode);
+ }
+
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ // TODO: Should be okay to have stacks with with undefined windowing mode long term, but
+ // have to set them to something for now due to logic that depending on them.
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
+ }
+
+ final boolean inSplitScreenMode = hasSplitScreenStack();
+ if (!inSplitScreenMode
+ && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
+ // Switch to fullscreen windowing mode if we are not in split-screen mode and we are
+ // trying to launch in split-screen secondary.
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
+ } else if (inSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
+ && WindowConfiguration.supportSplitScreenWindowingMode(
+ windowingMode, activityType)) {
+ windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ }
+
+ int stackId = INVALID_STACK_ID;
+ if (mDisplayId == DEFAULT_DISPLAY && (activityType == ACTIVITY_TYPE_STANDARD
+ || activityType == ACTIVITY_TYPE_UNDEFINED)) {
+ // TODO: Will be removed once we are no longer using static stack ids.
+ stackId = getStackIdForWindowingMode(windowingMode);
+ if (stackId == INVALID_STACK_ID) {
+ // Whatever...put in fullscreen stack for now.
+ stackId = FULLSCREEN_WORKSPACE_STACK_ID;
+ }
+ final T stack = getStack(stackId);
+ if (stack != null) {
+ return stack;
+ }
+ }
+
+ if (stackId == INVALID_STACK_ID) {
+ stackId = mSupervisor.getNextStackId();
+ }
+
+ final T stack = createStackUnchecked(windowingMode, activityType, stackId, onTop);
+
+ if (mDisplayId == DEFAULT_DISPLAY && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ // Make sure recents stack exist when creating a dock stack as it normally need to be on
+ // the other side of the docked stack and we make visibility decisions based on that.
+ // TODO: Not sure if this is needed after we change to calculate visibility based on
+ // stack z-order vs. id.
+ getOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS, onTop);
+ }
+
+ return stack;
+ }
+
+ @VisibleForTesting
+ <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
+ int stackId, boolean onTop) {
+ switch (windowingMode) {
+ case WINDOWING_MODE_PINNED:
+ return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
+ default:
+ return (T) new ActivityStack(
+ this, stackId, mSupervisor, windowingMode, activityType, onTop);
+ }
+ }
+
+ /**
+ * Removes stacks in the input windowing modes from the system if they are of activity type
+ * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+ */
+ void removeStacksInWindowingModes(int... windowingModes) {
+ if (windowingModes == null || windowingModes.length == 0) {
+ return;
+ }
+
+ for (int j = windowingModes.length - 1 ; j >= 0; --j) {
+ final int windowingMode = windowingModes[j];
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ if (!stack.isActivityTypeStandardOrUndefined()) {
+ continue;
+ }
+ if (stack.getWindowingMode() != windowingMode) {
+ continue;
+ }
+ mSupervisor.removeStackLocked(stack.mStackId);
+ }
+ }
+ }
+
+ void removeStacksWithActivityTypes(int... activityTypes) {
+ if (activityTypes == null || activityTypes.length == 0) {
+ return;
+ }
+
+ for (int j = activityTypes.length - 1 ; j >= 0; --j) {
+ final int activityType = activityTypes[j];
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ if (stack.getActivityType() == activityType) {
+ mSupervisor.removeStackLocked(stack.mStackId);
+ }
+ }
+ }
+ }
+
+ /** Returns the top visible stack activity type that isn't in the exclude windowing mode. */
+ int getTopVisibleStackActivityType(int excludeWindowingMode) {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ if (stack.getWindowingMode() == excludeWindowingMode) {
+ continue;
+ }
+ if (stack.shouldBeVisible(null /* starting */)) {
+ return stack.getActivityType();
+ }
+ }
+ return ACTIVITY_TYPE_UNDEFINED;
+ }
+
+ ActivityStack getSplitScreenStack() {
+ return getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
+ }
+
+ boolean hasSplitScreenStack() {
+ return getSplitScreenStack() != null;
+ }
+
+ PinnedActivityStack getPinnedStack() {
+ return getStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
+ }
+
+ boolean hasPinnedStack() {
+ return getPinnedStack() != null;
+ }
+
+ @Override
+ public String toString() {
+ return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
+ }
+
+ @Override
+ protected int getChildCount() {
+ return mStacks.size();
+ }
+
+ @Override
+ protected ConfigurationContainer getChildAt(int index) {
+ return mStacks.get(index);
+ }
+
+ @Override
+ protected ConfigurationContainer getParent() {
+ return mSupervisor;
+ }
+
+ boolean isPrivate() {
+ return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
+ }
+
+ boolean isUidPresent(int uid) {
+ for (ActivityStack stack : mStacks) {
+ if (stack.isUidPresent(uid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Update and get all UIDs that are present on the display and have access to it. */
+ IntArray getPresentUIDs() {
+ mDisplayAccessUIDs.clear();
+ for (ActivityStack stack : mStacks) {
+ stack.getPresentUIDs(mDisplayAccessUIDs);
+ }
+ return mDisplayAccessUIDs;
+ }
+
+ boolean shouldDestroyContentOnRemove() {
+ return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
+ }
+
+ boolean shouldSleep() {
+ return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
+ && (mSupervisor.mService.mRunningVoice == null);
+ }
+
+ boolean isSleeping() {
+ return mSleeping;
+ }
+
+ void setIsSleeping(boolean asleep) {
+ mSleeping = asleep;
+ }
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ super.writeToProto(proto, CONFIGURATION_CONTAINER);
+ proto.write(ID, mDisplayId);
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ stack.writeToProto(proto, STACKS);
+ }
+ proto.end(token);
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 12778d8..36cdb66 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -33,6 +33,13 @@
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.getWindowingModeForStackId;
+import static android.app.ActivityManager.StackId.isStaticStack;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
@@ -164,7 +171,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
@@ -177,7 +183,6 @@
import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.am.proto.ActivityManagerServiceProto.ACTIVITIES;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_RELAUNCH;
import static com.android.server.wm.AppTransition.TRANSIT_NONE;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
@@ -258,8 +263,8 @@
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.PathPermission;
import android.content.pm.PermissionInfo;
@@ -1711,6 +1716,7 @@
static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
+ static final int TOP_APP_KILLED_BY_LMK_MSG = 73;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1938,6 +1944,17 @@
dispatchProcessDied(pid, uid);
break;
}
+ case TOP_APP_KILLED_BY_LMK_MSG: {
+ final String appName = (String) msg.obj;
+ final AlertDialog d = new BaseErrorDialog(mUiContext);
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+ d.setTitle(mUiContext.getText(R.string.top_app_killed_title));
+ d.setMessage(mUiContext.getString(R.string.top_app_killed_message, appName));
+ d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.close),
+ obtainMessage(DISMISS_DIALOG_UI_MSG, d));
+ d.show();
+ break;
+ }
case DISPATCH_UIDS_CHANGED_UI_MSG: {
dispatchUidsChanged();
} break;
@@ -2360,11 +2377,7 @@
if (disableNonVrUi) {
// If we are in a VR mode where Picture-in-Picture mode is unsupported,
// then remove the pinned stack.
- final PinnedActivityStack pinnedStack = mStackSupervisor.getStack(
- PINNED_STACK_ID);
- if (pinnedStack != null) {
- mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
- }
+ mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
}
}
} break;
@@ -2519,10 +2532,12 @@
}
public void setWindowManager(WindowManagerService wm) {
- mWindowManager = wm;
- mStackSupervisor.setWindowManager(wm);
- mActivityStarter.setWindowManager(wm);
- mLockTaskController.setWindowManager(wm);
+ synchronized (this) {
+ mWindowManager = wm;
+ mStackSupervisor.setWindowManager(wm);
+ mActivityStarter.setWindowManager(wm);
+ mLockTaskController.setWindowManager(wm);
+ }
}
public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
@@ -5394,6 +5409,7 @@
boolean doLowMem = app.instr == null;
boolean doOomAdj = doLowMem;
if (!app.killedByAm) {
+ maybeNotifyTopAppKilled(app);
Slog.i(TAG, "Process " + app.processName + " (pid " + pid + ") has died: "
+ ProcessList.makeOomAdjString(app.setAdj)
+ ProcessList.makeProcStateString(app.setProcState));
@@ -5427,6 +5443,23 @@
}
}
+ /** Show system error dialog when a top app is killed by LMK */
+ void maybeNotifyTopAppKilled(ProcessRecord app) {
+ if (!shouldNotifyTopAppKilled(app)) {
+ return;
+ }
+
+ Message msg = mHandler.obtainMessage(TOP_APP_KILLED_BY_LMK_MSG);
+ msg.obj = mContext.getPackageManager().getApplicationLabel(app.info);
+ mUiHandler.sendMessage(msg);
+ }
+
+ /** Only show notification when the top app is killed on low ram devices */
+ private boolean shouldNotifyTopAppKilled(ProcessRecord app) {
+ return app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
+ ActivityManager.isLowRamDeviceStatic();
+ }
+
/**
* If a stack trace dump file is configured, dump process stack traces.
* @param clearTraces causes the dump file to be erased prior to the new
@@ -6150,7 +6183,7 @@
Slog.w(TAG, "Failed trying to unstop package "
+ packageName + ": " + e);
}
- if (mUserController.isUserRunningLocked(user, 0)) {
+ if (mUserController.isUserRunning(user, 0)) {
forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);
finishForceStopPackageLocked(packageName, pkgUid);
}
@@ -7308,33 +7341,32 @@
startProcessLocked(procs.get(ip), "on-hold", null);
}
}
-
- if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
- // Start looking for apps that are abusing wake locks.
- Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
- mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
- // Tell anyone interested that we are done booting!
- SystemProperties.set("sys.boot_completed", "1");
-
- // And trigger dev.bootcomplete if we are not showing encryption progress
- if (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt"))
- || "".equals(SystemProperties.get("vold.encrypt_progress"))) {
- SystemProperties.set("dev.bootcomplete", "1");
- }
- mUserController.sendBootCompletedLocked(
- new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser) {
- synchronized (ActivityManagerService.this) {
- requestPssAllProcsLocked(SystemClock.uptimeMillis(),
- true, false);
- }
- }
- });
- mUserController.scheduleStartProfilesLocked();
+ if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
+ return;
}
+ // Start looking for apps that are abusing wake locks.
+ Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
+ mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
+ // Tell anyone interested that we are done booting!
+ SystemProperties.set("sys.boot_completed", "1");
+
+ // And trigger dev.bootcomplete if we are not showing encryption progress
+ if (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt"))
+ || "".equals(SystemProperties.get("vold.encrypt_progress"))) {
+ SystemProperties.set("dev.bootcomplete", "1");
+ }
+ mUserController.sendBootCompleted(
+ new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ synchronized (ActivityManagerService.this) {
+ requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
+ }
+ }
+ });
+ mUserController.scheduleStartProfiles();
}
}
@@ -8074,7 +8106,7 @@
final Rect sourceBounds = new Rect(r.pictureInPictureArgs.getSourceRectHint());
mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio,
true /* moveHomeStackToFront */, "enterPictureInPictureMode");
- final PinnedActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
+ final PinnedActivityStack stack = r.getStack();
stack.setPictureInPictureAspectRatio(aspectRatio);
stack.setPictureInPictureActions(actions);
@@ -8189,11 +8221,6 @@
+ ": Current activity does not support picture-in-picture.");
}
- if (!StackId.isAllowedToEnterPictureInPicture(r.getStack().getStackId())) {
- throw new IllegalStateException(caller
- + ": Activities on the home, assistant, or recents stack not supported");
- }
-
if (params.hasSetAspectRatio()
&& !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
params.getAspectRatio())) {
@@ -9831,7 +9858,7 @@
if (tr.mBounds != null) {
rti.bounds = new Rect(tr.mBounds);
}
- rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreen();
+ rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode();
rti.resizeMode = tr.mResizeMode;
ActivityRecord base = null;
@@ -10150,21 +10177,23 @@
// - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
// that task to freeform
// - otherwise the task is not moved
- int stackId = task.getStackId();
+ ActivityStack stack = task.getStack();
if (!task.getWindowConfiguration().canResizeTask()) {
throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
}
- if (bounds == null && stackId == FREEFORM_WORKSPACE_STACK_ID) {
- stackId = FULLSCREEN_WORKSPACE_STACK_ID;
- } else if (bounds != null && stackId != FREEFORM_WORKSPACE_STACK_ID ) {
- stackId = FREEFORM_WORKSPACE_STACK_ID;
+ if (bounds == null && stack.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ stack = stack.getDisplay().getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
+ } else if (bounds != null && stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+ stack = stack.getDisplay().getOrCreateStack(
+ WINDOWING_MODE_FREEFORM, stack.getActivityType(), ON_TOP);
}
// Reparent the task to the right stack if necessary
boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
- if (stackId != task.getStackId()) {
+ if (stack != task.getStack()) {
// Defer resume until the task is resized below
- task.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
+ task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
DEFER_RESUME, "resizeTask");
preserveWindow = false;
}
@@ -10337,13 +10366,14 @@
@Override
public void removeStack(int stackId) {
enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, "removeStack()");
- if (StackId.isHomeOrRecentsStack(stackId)) {
- throw new IllegalArgumentException("Removing home or recents stack is not allowed.");
- }
-
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
+ final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ if (stack != null && !stack.isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException(
+ "Removing non-standard stack is not allowed.");
+ }
mStackSupervisor.removeStackLocked(stackId);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -10351,6 +10381,36 @@
}
}
+ /**
+ * Removes stacks in the input windowing modes from the system if they are of activity type
+ * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+ */
+ @Override
+ public void removeStacksInWindowingModes(int[] windowingModes) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "removeStacksInWindowingModes()");
+ synchronized (this) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mStackSupervisor.removeStacksInWindowingModes(windowingModes);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @Override
+ public void removeStacksWithActivityTypes(int[] activityTypes) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "removeStacksWithActivityTypes()");
+ synchronized (this) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mStackSupervisor.removeStacksWithActivityTypes(activityTypes);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
@Override
public void moveStackToDisplay(int stackId, int displayId) {
enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()");
@@ -10494,13 +10554,15 @@
public int createStackOnDisplay(int displayId) throws RemoteException {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createStackOnDisplay()");
synchronized (this) {
- final int stackId = mStackSupervisor.getNextStackId();
- final ActivityStack stack =
- mStackSupervisor.createStackOnDisplay(stackId, displayId, true /*onTop*/);
- if (stack == null) {
+ final ActivityDisplay display =
+ mStackSupervisor.getActivityDisplayOrCreateLocked(displayId);
+ if (display == null) {
return INVALID_STACK_ID;
}
- return stack.mStackId;
+ // TODO(multi-display): Have the caller pass in the windowing mode and activity type.
+ final ActivityStack stack = display.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /*onTop*/);
+ return (stack != null) ? stack.mStackId : INVALID_STACK_ID;
}
}
@@ -10532,8 +10594,12 @@
"exitFreeformMode: You can only go fullscreen from freeform.");
}
+ final ActivityStack fullscreenStack = stack.getDisplay().getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
+
if (DEBUG_STACK) Slog.d(TAG_STACK, "exitFreeformMode: " + r);
- r.getTask().reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP,
+ // TODO: Should just change windowing mode vs. re-parenting...
+ r.getTask().reparent(fullscreenStack, ON_TOP,
REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "exitFreeformMode");
} finally {
Binder.restoreCallingIdentity(ident);
@@ -10544,10 +10610,6 @@
@Override
public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
- if (StackId.isHomeOrRecentsStack(stackId)) {
- throw new IllegalArgumentException(
- "moveTaskToStack: Attempt to move task " + taskId + " to stack " + stackId);
- }
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
@@ -10563,8 +10625,25 @@
mWindowManager.setDockedStackCreateState(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
null /* initialBounds */);
}
- task.reparent(stackId, toTop,
- REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "moveTaskToStack");
+
+ ActivityStack stack = mStackSupervisor.getStack(stackId);
+ if (stack == null) {
+ if (!isStaticStack(stackId)) {
+ throw new IllegalStateException(
+ "moveTaskToStack: No stack for stackId=" + stackId);
+ }
+ final ActivityDisplay display = task.getStack().getDisplay();
+ final int windowingMode =
+ getWindowingModeForStackId(stackId, display.hasSplitScreenStack());
+ stack = display.getOrCreateStack(windowingMode,
+ task.getStack().getActivityType(), toTop);
+ }
+ if (!stack.isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException("moveTaskToStack: Attempt to move task "
+ + taskId + " to stack " + stackId);
+ }
+ task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
+ "moveTaskToStack");
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -10597,13 +10676,18 @@
Slog.w(TAG, "moveTaskToDockedStack: No task for id=" + taskId);
return false;
}
-
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
+ " to createMode=" + createMode + " toTop=" + toTop);
mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+ final ActivityDisplay display = task.getStack().getDisplay();
+ final ActivityStack stack = display.getOrCreateStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, task.getStack().getActivityType(),
+ toTop);
+
// Defer resuming until we move the home stack to the front below
- final boolean moved = task.reparent(DOCKED_STACK_ID, toTop,
+ // TODO: Should just change windowing mode vs. re-parenting...
+ final boolean moved = task.reparent(stack, toTop,
REPARENT_KEEP_STACK_AT_FRONT, animate, !DEFER_RESUME,
"moveTaskToDockedStack");
if (moved) {
@@ -10651,17 +10735,16 @@
try {
synchronized (this) {
if (animate) {
- if (stackId == PINNED_STACK_ID) {
- final PinnedActivityStack pinnedStack =
- mStackSupervisor.getStack(PINNED_STACK_ID);
- if (pinnedStack != null) {
- pinnedStack.animateResizePinnedStack(null /* sourceHintBounds */,
- destBounds, animationDuration, false /* fromFullscreen */);
- }
- } else {
+ final PinnedActivityStack stack = mStackSupervisor.getStack(stackId);
+ if (stack == null) {
+ return;
+ }
+ if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
throw new IllegalArgumentException("Stack: " + stackId
+ " doesn't support animated resize.");
}
+ stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
+ animationDuration, false /* fromFullscreen */);
} else {
mStackSupervisor.resizeStackLocked(stackId, destBounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, preserveWindows,
@@ -10713,11 +10796,6 @@
@Override
public void positionTaskInStack(int taskId, int stackId, int position) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
- if (StackId.isHomeOrRecentsStack(stackId)) {
- throw new IllegalArgumentException(
- "positionTaskInStack: Attempt to change the position of task "
- + taskId + " in/to home/recents stack");
- }
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
@@ -10729,8 +10807,16 @@
+ taskId);
}
- final ActivityStack stack = mStackSupervisor.getStack(stackId, CREATE_IF_NEEDED,
- !ON_TOP);
+ final ActivityStack stack = mStackSupervisor.getStack(stackId);
+
+ if (stack == null) {
+ throw new IllegalArgumentException("positionTaskInStack: no stack for id="
+ + stackId);
+ }
+ if (!stack.isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException("positionTaskInStack: Attempt to change"
+ + " the position of task " + taskId + " in/to non-standard stack");
+ }
// TODO: Have the callers of this API call a separate reparent method if that is
// what they intended to do vs. having this method also do reparenting.
@@ -10739,8 +10825,8 @@
stack.positionChildAt(task, position);
} else {
// Reparent to new stack.
- task.reparent(stackId, position, REPARENT_LEAVE_STACK_IN_PLACE,
- !ANIMATE, !DEFER_RESUME, "positionTaskInStack");
+ task.reparent(stack, position, REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE,
+ !DEFER_RESUME, "positionTaskInStack");
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -10762,12 +10848,12 @@
}
@Override
- public StackInfo getStackInfo(int stackId) {
+ public StackInfo getStackInfo(int windowingMode, int activityType) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
- return mStackSupervisor.getStackInfoLocked(stackId);
+ return mStackSupervisor.getStackInfo(windowingMode, activityType);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -10818,11 +10904,7 @@
}
// When a task is locked, dismiss the pinned stack if it exists
- final PinnedActivityStack pinnedStack = mStackSupervisor.getStack(
- PINNED_STACK_ID);
- if (pinnedStack != null) {
- mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
- }
+ mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
// isAppPinning is used to distinguish between locked and pinned mode, as pinned mode
// is initiated by system after the pinning request was shown and locked mode is initiated
@@ -11049,7 +11131,7 @@
boolean checkedGrants = false;
if (checkUser) {
// Looking for cross-user grants before enforcing the typical cross-users permissions
- int tmpTargetUserId = mUserController.unsafeConvertIncomingUserLocked(userId);
+ int tmpTargetUserId = mUserController.unsafeConvertIncomingUser(userId);
if (tmpTargetUserId != UserHandle.getUserId(callingUid)) {
if (checkAuthorityGrants(callingUid, cpi, tmpTargetUserId, checkUser)) {
return null;
@@ -11437,7 +11519,7 @@
// Make sure that the user who owns this provider is running. If not,
// we don't want to allow it to run.
- if (!mUserController.isUserRunningLocked(userId, 0)) {
+ if (!mUserController.isUserRunning(userId, 0)) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
@@ -12067,9 +12149,7 @@
int callingPid = Binder.getCallingPid();
long ident = 0;
boolean clearedIdentity = false;
- synchronized (this) {
- userId = mUserController.unsafeConvertIncomingUserLocked(userId);
- }
+ userId = mUserController.unsafeConvertIncomingUser(userId);
if (canClearIdentity(callingPid, callingUid, userId)) {
clearedIdentity = true;
ident = Binder.clearCallingIdentity();
@@ -12487,7 +12567,7 @@
if (mUserController.shouldConfirmCredentials(userId)) {
if (mKeyguardController.isKeyguardLocked()) {
// Showing launcher to avoid user entering credential twice.
- final int currentUserId = mUserController.getCurrentUserIdLocked();
+ final int currentUserId = mUserController.getCurrentUserId();
startHomeActivityLocked(currentUserId, "notifyLockedProfile");
}
mStackSupervisor.lockAllProfileTasks(userId);
@@ -14067,9 +14147,8 @@
}
retrieveSettings();
- final int currentUserId;
+ final int currentUserId = mUserController.getCurrentUserId();
synchronized (this) {
- currentUserId = mUserController.getCurrentUserIdLocked();
readGrantedUriPermissionsLocked();
}
@@ -14148,7 +14227,7 @@
Binder.restoreCallingIdentity(ident);
}
mStackSupervisor.resumeFocusedStackTopActivityLocked();
- mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId);
+ mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
traceLog.traceEnd(); // ActivityManagerStartApps
traceLog.traceEnd(); // PhaseActivityManagerReady
}
@@ -14500,6 +14579,9 @@
}
sb.append("\n");
}
+ if (process.info.isInstantApp()) {
+ sb.append("Instant-App: true\n");
+ }
}
}
@@ -18976,7 +19058,7 @@
// If not, we will just skip it. Make an exception for shutdown broadcasts
// and upgrade steps.
- if (userId != UserHandle.USER_ALL && !mUserController.isUserRunningLocked(userId, 0)) {
+ if (userId != UserHandle.USER_ALL && !mUserController.isUserRunning(userId, 0)) {
if ((callingUid != SYSTEM_UID
|| (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
&& !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
@@ -19395,7 +19477,7 @@
int[] users;
if (userId == UserHandle.USER_ALL) {
// Caller wants broadcast to go to all started users.
- users = mUserController.getStartedUserArrayLocked();
+ users = mUserController.getStartedUserArray();
} else {
// Caller wants broadcast to go to one specific user.
users = new int[] {userId};
@@ -20021,12 +20103,20 @@
}
@Override
- public int getFocusedStackId() throws RemoteException {
- ActivityStack focusedStack = getFocusedStack();
- if (focusedStack != null) {
- return focusedStack.getStackId();
+ public StackInfo getFocusedStackInfo() throws RemoteException {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+ long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ ActivityStack focusedStack = getFocusedStack();
+ if (focusedStack != null) {
+ return mStackSupervisor.getStackInfo(focusedStack.mStackId);
+ }
+ return null;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- return -1;
}
public Configuration getConfiguration() {
@@ -20052,15 +20142,20 @@
* activity and clearing the task at the same time.
*/
@Override
+ // TODO: API should just be about changing windowing modes...
public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTasksToFullscreenStack()");
- if (StackId.isHomeOrRecentsStack(fromStackId)) {
- throw new IllegalArgumentException("You can't move tasks from the home/recents stack.");
- }
synchronized (this) {
final long origId = Binder.clearCallingIdentity();
try {
- mStackSupervisor.moveTasksToFullscreenStackLocked(fromStackId, onTop);
+ final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
+ if (stack != null){
+ if (!stack.isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException(
+ "You can't move tasks from non-standard stacks.");
+ }
+ mStackSupervisor.moveTasksToFullscreenStackLocked(stack, onTop);
+ }
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -20158,7 +20253,7 @@
void updateUserConfigurationLocked() {
final Configuration configuration = new Configuration(getGlobalConfiguration());
- final int currentUserId = mUserController.getCurrentUserIdLocked();
+ final int currentUserId = mUserController.getCurrentUserId();
Settings.System.adjustConfigurationForUser(mContext.getContentResolver(), configuration,
currentUserId, Settings.System.canWrite(mContext));
updateConfigurationLocked(configuration, null /* starting */, false /* initLocale */,
@@ -20269,7 +20364,7 @@
Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
// TODO(multi-display): Update UsageEvents#Event to include displayId.
mUsageStatsService.reportConfigurationChange(mTempConfig,
- mUserController.getCurrentUserIdLocked());
+ mUserController.getCurrentUserId());
// TODO: If our config changes, should we auto dismiss any currently showing dialogs?
mShowDialogs = shouldShowDialogs(mTempConfig);
@@ -22200,7 +22295,7 @@
String authority) {
if (app == null) return;
if (app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
- UserState userState = mUserController.getStartedUserStateLocked(app.userId);
+ UserState userState = mUserController.getStartedUserState(app.userId);
if (userState == null) return;
final long now = SystemClock.elapsedRealtime();
Long lastReported = userState.mProviderLastReportedFg.get(authority);
@@ -23509,10 +23604,8 @@
}
String getStartedUserState(int userId) {
- synchronized (this) {
- final UserState userState = mUserController.getStartedUserStateLocked(userId);
- return UserState.stateToString(userState.state);
- }
+ final UserState userState = mUserController.getStartedUserState(userId);
+ return UserState.stateToString(userState.state);
}
@Override
@@ -23527,9 +23620,7 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- synchronized (this) {
- return mUserController.isUserRunningLocked(userId, flags);
- }
+ return mUserController.isUserRunning(userId, flags);
}
@Override
@@ -23543,9 +23634,7 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- synchronized (this) {
- return mUserController.getStartedUserArrayLocked();
- }
+ return mUserController.getStartedUserArray();
}
@Override
@@ -23566,9 +23655,7 @@
}
public boolean isUserStopped(int userId) {
- synchronized (this) {
- return mUserController.getStartedUserStateLocked(userId) == null;
- }
+ return mUserController.getStartedUserState(userId) == null;
}
ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
@@ -24156,7 +24243,7 @@
permission.INTERACT_ACROSS_USERS_FULL, "getLastResumedActivityUserId()");
synchronized (this) {
if (mLastResumedActivity == null) {
- return mUserController.getCurrentUserIdLocked();
+ return mUserController.getCurrentUserId();
}
return mLastResumedActivity.userId;
}
@@ -24363,7 +24450,7 @@
if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
try {
final ApplicationInfo ai = AppGlobals.getPackageManager()
- .getApplicationInfo(packageName, 0 /*flags*/, app.userId);
+ .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
if (ai != null) {
app.thread.scheduleApplicationInfoChanged(ai);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 5e0724e..4c93423 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -76,6 +76,7 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
@@ -2113,9 +2114,9 @@
}
int runStackInfo(PrintWriter pw) throws RemoteException {
- String stackIdStr = getNextArgRequired();
- int stackId = Integer.parseInt(stackIdStr);
- ActivityManager.StackInfo info = mInterface.getStackInfo(stackId);
+ int windowingMode = Integer.parseInt(getNextArgRequired());
+ int activityType = Integer.parseInt(getNextArgRequired());
+ ActivityManager.StackInfo info = mInterface.getStackInfo(windowingMode, activityType);
pw.println(info);
return 0;
}
@@ -2149,7 +2150,8 @@
final String delayStr = getNextArg();
final int delayMs = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
- ActivityManager.StackInfo info = mInterface.getStackInfo(DOCKED_STACK_ID);
+ ActivityManager.StackInfo info = mInterface.getStackInfo(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
if (info == null) {
err.println("Docked stack doesn't exist");
return -1;
@@ -2252,10 +2254,6 @@
return runTaskResizeable(pw);
} else if (op.equals("resize")) {
return runTaskResize(pw);
- } else if (op.equals("drag-task-test")) {
- return runTaskDragTaskTest(pw);
- } else if (op.equals("size-task-test")) {
- return runTaskSizeTaskTest(pw);
} else if (op.equals("focus")) {
return runTaskFocus(pw);
} else {
@@ -2308,58 +2306,6 @@
}
}
- int runTaskDragTaskTest(PrintWriter pw) throws RemoteException {
- final int taskId = Integer.parseInt(getNextArgRequired());
- final int stepSize = Integer.parseInt(getNextArgRequired());
- final String delayStr = getNextArg();
- final int delay_ms = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
- final ActivityManager.StackInfo stackInfo;
- Rect taskBounds;
- stackInfo = mInterface.getStackInfo(mInterface.getFocusedStackId());
- taskBounds = mInterface.getTaskBounds(taskId);
- final Rect stackBounds = stackInfo.bounds;
- int travelRight = stackBounds.width() - taskBounds.width();
- int travelLeft = -travelRight;
- int travelDown = stackBounds.height() - taskBounds.height();
- int travelUp = -travelDown;
- int passes = 0;
-
- // We do 2 passes to get back to the original location of the task.
- while (passes < 2) {
- // Move right
- pw.println("Moving right...");
- pw.flush();
- travelRight = moveTask(taskId, taskBounds, stackBounds, stepSize,
- travelRight, MOVING_FORWARD, MOVING_HORIZONTALLY, delay_ms);
- pw.println("Still need to travel right by " + travelRight);
-
- // Move down
- pw.println("Moving down...");
- pw.flush();
- travelDown = moveTask(taskId, taskBounds, stackBounds, stepSize,
- travelDown, MOVING_FORWARD, !MOVING_HORIZONTALLY, delay_ms);
- pw.println("Still need to travel down by " + travelDown);
-
- // Move left
- pw.println("Moving left...");
- pw.flush();
- travelLeft = moveTask(taskId, taskBounds, stackBounds, stepSize,
- travelLeft, !MOVING_FORWARD, MOVING_HORIZONTALLY, delay_ms);
- pw.println("Still need to travel left by " + travelLeft);
-
- // Move up
- pw.println("Moving up...");
- pw.flush();
- travelUp = moveTask(taskId, taskBounds, stackBounds, stepSize,
- travelUp, !MOVING_FORWARD, !MOVING_HORIZONTALLY, delay_ms);
- pw.println("Still need to travel up by " + travelUp);
-
- taskBounds = mInterface.getTaskBounds(taskId);
- passes++;
- }
- return 0;
- }
-
int moveTask(int taskId, Rect taskRect, Rect stackRect, int stepSize,
int maxToTravel, boolean movingForward, boolean horizontal, int delay_ms)
throws RemoteException {
@@ -2422,133 +2368,6 @@
return stepSize;
}
- int runTaskSizeTaskTest(PrintWriter pw) throws RemoteException {
- final int taskId = Integer.parseInt(getNextArgRequired());
- final int stepSize = Integer.parseInt(getNextArgRequired());
- final String delayStr = getNextArg();
- final int delay_ms = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
- final ActivityManager.StackInfo stackInfo;
- final Rect initialTaskBounds;
- stackInfo = mInterface.getStackInfo(mInterface.getFocusedStackId());
- initialTaskBounds = mInterface.getTaskBounds(taskId);
- final Rect stackBounds = stackInfo.bounds;
- stackBounds.inset(STACK_BOUNDS_INSET, STACK_BOUNDS_INSET);
- final Rect currentTaskBounds = new Rect(initialTaskBounds);
-
- // Size by top-left
- pw.println("Growing top-left");
- pw.flush();
- do {
- currentTaskBounds.top -= getStepSize(
- currentTaskBounds.top, stackBounds.top, stepSize, GREATER_THAN_TARGET);
-
- currentTaskBounds.left -= getStepSize(
- currentTaskBounds.left, stackBounds.left, stepSize, GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (stackBounds.top < currentTaskBounds.top
- || stackBounds.left < currentTaskBounds.left);
-
- // Back to original size
- pw.println("Shrinking top-left");
- pw.flush();
- do {
- currentTaskBounds.top += getStepSize(
- currentTaskBounds.top, initialTaskBounds.top, stepSize, !GREATER_THAN_TARGET);
-
- currentTaskBounds.left += getStepSize(
- currentTaskBounds.left, initialTaskBounds.left, stepSize, !GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (initialTaskBounds.top > currentTaskBounds.top
- || initialTaskBounds.left > currentTaskBounds.left);
-
- // Size by top-right
- pw.println("Growing top-right");
- pw.flush();
- do {
- currentTaskBounds.top -= getStepSize(
- currentTaskBounds.top, stackBounds.top, stepSize, GREATER_THAN_TARGET);
-
- currentTaskBounds.right += getStepSize(
- currentTaskBounds.right, stackBounds.right, stepSize, !GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (stackBounds.top < currentTaskBounds.top
- || stackBounds.right > currentTaskBounds.right);
-
- // Back to original size
- pw.println("Shrinking top-right");
- pw.flush();
- do {
- currentTaskBounds.top += getStepSize(
- currentTaskBounds.top, initialTaskBounds.top, stepSize, !GREATER_THAN_TARGET);
-
- currentTaskBounds.right -= getStepSize(currentTaskBounds.right, initialTaskBounds.right,
- stepSize, GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (initialTaskBounds.top > currentTaskBounds.top
- || initialTaskBounds.right < currentTaskBounds.right);
-
- // Size by bottom-left
- pw.println("Growing bottom-left");
- pw.flush();
- do {
- currentTaskBounds.bottom += getStepSize(
- currentTaskBounds.bottom, stackBounds.bottom, stepSize, !GREATER_THAN_TARGET);
-
- currentTaskBounds.left -= getStepSize(
- currentTaskBounds.left, stackBounds.left, stepSize, GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (stackBounds.bottom > currentTaskBounds.bottom
- || stackBounds.left < currentTaskBounds.left);
-
- // Back to original size
- pw.println("Shrinking bottom-left");
- pw.flush();
- do {
- currentTaskBounds.bottom -= getStepSize(currentTaskBounds.bottom,
- initialTaskBounds.bottom, stepSize, GREATER_THAN_TARGET);
-
- currentTaskBounds.left += getStepSize(
- currentTaskBounds.left, initialTaskBounds.left, stepSize, !GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (initialTaskBounds.bottom < currentTaskBounds.bottom
- || initialTaskBounds.left > currentTaskBounds.left);
-
- // Size by bottom-right
- pw.println("Growing bottom-right");
- pw.flush();
- do {
- currentTaskBounds.bottom += getStepSize(
- currentTaskBounds.bottom, stackBounds.bottom, stepSize, !GREATER_THAN_TARGET);
-
- currentTaskBounds.right += getStepSize(
- currentTaskBounds.right, stackBounds.right, stepSize, !GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (stackBounds.bottom > currentTaskBounds.bottom
- || stackBounds.right > currentTaskBounds.right);
-
- // Back to original size
- pw.println("Shrinking bottom-right");
- pw.flush();
- do {
- currentTaskBounds.bottom -= getStepSize(currentTaskBounds.bottom,
- initialTaskBounds.bottom, stepSize, GREATER_THAN_TARGET);
-
- currentTaskBounds.right -= getStepSize(currentTaskBounds.right, initialTaskBounds.right,
- stepSize, GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (initialTaskBounds.bottom < currentTaskBounds.bottom
- || initialTaskBounds.right < currentTaskBounds.right);
- return 0;
- }
-
int runTaskFocus(PrintWriter pw) throws RemoteException {
final int taskId = Integer.parseInt(getNextArgRequired());
pw.println("Setting focus to task " + taskId);
@@ -2880,8 +2699,8 @@
pw.println(" Place <TASK_ID> in <STACK_ID> at <POSITION>");
pw.println(" list");
pw.println(" List all of the activity stacks and their sizes.");
- pw.println(" info <STACK_ID>");
- pw.println(" Display the information about activity stack <STACK_ID>.");
+ pw.println(" info <WINDOWING_MODE> <ACTIVITY_TYPE>");
+ pw.println(" Display the information about activity stack in <WINDOWING_MODE> and <ACTIVITY_TYPE>.");
pw.println(" remove <STACK_ID>");
pw.println(" Remove stack <STACK_ID>.");
pw.println(" task [COMMAND] [...]: sub-commands for operating on activity tasks.");
@@ -2899,14 +2718,6 @@
pw.println(" Makes sure <TASK_ID> is in a stack with the specified bounds.");
pw.println(" Forces the task to be resizeable and creates a stack if no existing stack");
pw.println(" has the specified bounds.");
- pw.println(" drag-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS]");
- pw.println(" Test command for dragging/moving <TASK_ID> by");
- pw.println(" <STEP_SIZE> increments around the screen applying the optional [DELAY_MS]");
- pw.println(" between each step.");
- pw.println(" size-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS]");
- pw.println(" Test command for sizing <TASK_ID> by <STEP_SIZE>");
- pw.println(" increments within the screen applying the optional [DELAY_MS] between");
- pw.println(" each step.");
pw.println(" update-appinfo <USER_ID> <PACKAGE_NAME> [<PACKAGE_NAME>...]");
pw.println(" Update the ApplicationInfo objects of the listed packages for <USER_ID>");
pw.println(" without restarting any processes.");
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 0c8321d..fdcb8c6 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -2,12 +2,9 @@
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -32,13 +29,10 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
-import android.app.ActivityManager.StackId;
import android.content.Context;
import android.metrics.LogMaker;
import android.os.SystemClock;
-import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 142c97b..7b0b942 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
@@ -103,7 +102,6 @@
import static com.android.server.am.ActivityStack.LAUNCH_TICK;
import static com.android.server.am.ActivityStack.LAUNCH_TICK_MSG;
import static com.android.server.am.ActivityStack.PAUSE_TIMEOUT_MSG;
-import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
import static com.android.server.am.ActivityStack.STOP_TIMEOUT_MSG;
import static com.android.server.am.EventLogTags.AM_ACTIVITY_FULLY_DRAWN_TIME;
import static com.android.server.am.EventLogTags.AM_ACTIVITY_LAUNCH_TIME;
@@ -1069,6 +1067,11 @@
return getStack() != null ? getStack().mStackId : INVALID_STACK_ID;
}
+ ActivityDisplay getDisplay() {
+ final ActivityStack stack = getStack();
+ return stack != null ? stack.getDisplay() : null;
+ }
+
boolean changeWindowTranslucency(boolean toOpaque) {
if (fullscreen == toOpaque) {
return false;
@@ -1134,10 +1137,12 @@
* @return whether this activity supports split-screen multi-window and can be put in the docked
* stack.
*/
- boolean supportsSplitScreen() {
+ @Override
+ public boolean supportsSplitScreenWindowingMode() {
// An activity can not be docked even if it is considered resizeable because it only
// supports picture-in-picture mode but has a non-resizeable resizeMode
- return service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
+ return super.supportsSplitScreenWindowingMode()
+ && service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
}
/**
@@ -1198,7 +1203,8 @@
boolean isKeyguardLocked = service.isKeyguardLocked();
boolean isCurrentAppLocked = service.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
- boolean hasPinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID) != null;
+ final ActivityDisplay display = getDisplay();
+ boolean hasPinnedStack = display != null && display.hasPinnedStack();
// Don't return early if !isNotLocked, since we want to throw an exception if the activity
// is in an incorrect state
boolean isNotLockedOrOnKeyguard = !isKeyguardLocked && !isCurrentAppLocked;
@@ -1544,8 +1550,9 @@
if (service.mSupportsLeanbackOnly && isVisible && isActivityTypeRecents()) {
// On devices that support leanback only (Android TV), Recents activity can only be
// visible if the home stack is the focused stack or we are in split-screen mode.
- isVisible = mStackSupervisor.getStack(DOCKED_STACK_ID) != null
- || mStackSupervisor.isFocusedStack(getStack());
+ final ActivityDisplay display = getDisplay();
+ boolean hasSplitScreenStack = display != null && display.hasSplitScreenStack();
+ isVisible = hasSplitScreenStack || mStackSupervisor.isFocusedStack(getStack());
}
return isVisible;
@@ -1948,7 +1955,7 @@
return (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0
|| (mStackSupervisor.isCurrentProfileLocked(userId)
- && service.mUserController.isUserRunningLocked(userId, 0 /* flags */));
+ && service.mUserController.isUserRunning(userId, 0 /* flags */));
}
/**
@@ -2312,7 +2319,7 @@
// be visible based on the stack, task, and lockscreen state and use that here instead. The
// method should be based on the logic in ActivityStack.ensureActivitiesVisibleLocked().
// Skip updating configuration for activity is a stack that shouldn't be visible.
- if (stack.shouldBeVisible(null /* starting */) == STACK_INVISIBLE) {
+ if (!stack.shouldBeVisible(null /* starting */)) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Skipping config check invisible stack: " + this);
return true;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8d21862..34eaab2 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -16,19 +16,19 @@
package com.android.server.am;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.getActivityTypeForStackId;
-import static android.app.ActivityManager.StackId.getWindowingModeForStackId;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
@@ -37,6 +37,8 @@
import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.view.Display.INVALID_DISPLAY;
+import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
+import static com.android.server.am.ActivityDisplay.POSITION_TOP;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_APP;
@@ -74,7 +76,6 @@
import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
@@ -109,7 +110,6 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
-import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
@@ -240,11 +240,6 @@
DESTROYED
}
- // Stack is not considered visible.
- static final int STACK_INVISIBLE = 0;
- // Stack is considered visible
- static final int STACK_VISIBLE = 1;
-
@VisibleForTesting
/* The various modes for the method {@link #removeTask}. */
// Task is being completely removed from all stacks in the system.
@@ -267,7 +262,6 @@
final ActivityManagerService mService;
private final WindowManagerService mWindowManager;
T mWindowContainerController;
- private final RecentTasks mRecentTasks;
/**
* The back history of all previous (and possibly still
@@ -350,9 +344,6 @@
int mCurrentUser;
final int mStackId;
- /** The other stacks, in order, on the attached display. Updated at attach/detach time. */
- // TODO: This list doesn't belong here...
- ArrayList<ActivityStack> mStacks;
/** The attached Display's unique identifier, or -1 if detached */
int mDisplayId;
@@ -461,49 +452,35 @@
return count;
}
- ActivityStack(ActivityStackSupervisor.ActivityDisplay display, int stackId,
- ActivityStackSupervisor supervisor, RecentTasks recentTasks, boolean onTop) {
+ ActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
+ int windowingMode, int activityType, boolean onTop) {
mStackSupervisor = supervisor;
mService = supervisor.mService;
mHandler = new ActivityStackHandler(mService.mHandler.getLooper());
mWindowManager = mService.mWindowManager;
mStackId = stackId;
- mCurrentUser = mService.mUserController.getCurrentUserIdLocked();
- mRecentTasks = recentTasks;
+ mCurrentUser = mService.mUserController.getCurrentUserId();
mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
? new LaunchingTaskPositioner() : null;
mTmpRect2.setEmpty();
- updateOverrideConfiguration();
+ setWindowingMode(windowingMode);
+ setActivityType(activityType);
mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
mTmpRect2);
- mStackSupervisor.mStacks.put(mStackId, this);
postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
}
T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
- return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds);
+ return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds,
+ mStackSupervisor.mWindowManager);
}
T getWindowContainerController() {
return mWindowContainerController;
}
- // TODO: Not needed once we are no longer using stack ids as the override config. can be passed
- // in.
- private void updateOverrideConfiguration() {
- final int windowingMode = getWindowingModeForStackId(
- mStackId, mStackSupervisor.getStack(DOCKED_STACK_ID) != null);
- if (windowingMode != WINDOWING_MODE_UNDEFINED) {
- setWindowingMode(windowingMode);
- }
- final int activityType = getActivityTypeForStackId(mStackId);
- if (activityType != ACTIVITY_TYPE_UNDEFINED) {
- setActivityType(activityType);
- }
- }
-
/** Adds the stack to specified display and calls WindowManager to do the same. */
- void reparent(ActivityStackSupervisor.ActivityDisplay activityDisplay, boolean onTop) {
+ void reparent(ActivityDisplay activityDisplay, boolean onTop) {
removeFromDisplay();
mTmpRect2.setEmpty();
postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
@@ -521,10 +498,8 @@
* @param activityDisplay New display to which this stack was attached.
* @param bounds Updated bounds.
*/
- private void postAddToDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay,
- Rect bounds, boolean onTop) {
+ private void postAddToDisplay(ActivityDisplay activityDisplay, Rect bounds, boolean onTop) {
mDisplayId = activityDisplay.mDisplayId;
- mStacks = activityDisplay.mStacks;
mBounds = bounds != null ? new Rect(bounds) : null;
mFullscreen = mBounds == null;
if (mTaskPositioner != null) {
@@ -533,7 +508,7 @@
}
onParentChanged();
- activityDisplay.attachStack(this, findStackInsertIndex(onTop));
+ activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
if (mStackId == DOCKED_STACK_ID) {
// If we created a docked stack we want to resize it so it resizes all other stacks
// in the system.
@@ -547,42 +522,36 @@
* either destroyed completely or re-parented.
*/
private void removeFromDisplay() {
- final ActivityStackSupervisor.ActivityDisplay display = getDisplay();
- if (display != null) {
- display.detachStack(this);
- }
- mDisplayId = INVALID_DISPLAY;
- mStacks = null;
- if (mTaskPositioner != null) {
- mTaskPositioner.reset();
- }
- if (mStackId == DOCKED_STACK_ID) {
+ if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// If we removed a docked stack we want to resize it so it resizes all other stacks
// in the system to fullscreen.
mStackSupervisor.resizeDockedStackLocked(
null, null, null, null, null, PRESERVE_WINDOWS);
}
+ final ActivityDisplay display = getDisplay();
+ if (display != null) {
+ display.removeChild(this);
+ }
+ mDisplayId = INVALID_DISPLAY;
+ if (mTaskPositioner != null) {
+ mTaskPositioner.reset();
+ }
}
/** Removes the stack completely. Also calls WindowManager to do the same on its side. */
void remove() {
removeFromDisplay();
- mStackSupervisor.mStacks.remove(mStackId);
mWindowContainerController.removeContainer();
mWindowContainerController = null;
onParentChanged();
}
- ActivityStackSupervisor.ActivityDisplay getDisplay() {
+ ActivityDisplay getDisplay() {
return mStackSupervisor.getActivityDisplay(mDisplayId);
}
- void getDisplaySize(Point out) {
- getDisplay().mDisplay.getSize(out);
- }
-
/**
- * @see ActivityStack.getStackDockedModeBounds(Rect, Rect, Rect, boolean)
+ * @see #getStackDockedModeBounds(Rect, Rect, Rect, boolean)
*/
void getStackDockedModeBounds(Rect currentTempTaskBounds, Rect outStackBounds,
Rect outTempTaskBounds, boolean ignoreVisibility) {
@@ -846,7 +815,7 @@
}
final boolean isHomeOrRecentsStack() {
- return StackId.isHomeOrRecentsStack(mStackId);
+ return isActivityTypeHome() || isActivityTypeRecents();
}
final boolean isDockedStack() {
@@ -858,7 +827,7 @@
}
final boolean isOnHomeDisplay() {
- return isAttached() && mDisplayId == DEFAULT_DISPLAY;
+ return mDisplayId == DEFAULT_DISPLAY;
}
void moveToFront(String reason) {
@@ -874,8 +843,7 @@
return;
}
- mStacks.remove(this);
- mStacks.add(findStackInsertIndex(ON_TOP), this);
+ getDisplay().positionChildAtTop(this);
mStackSupervisor.setFocusStackUnchecked(reason, this);
if (task != null) {
insertTaskAtTop(task, null);
@@ -889,45 +857,6 @@
}
}
- /**
- * @param task If non-null, the task will be moved to the back of the stack.
- * */
- private void moveToBack(TaskRecord task) {
- if (!isAttached()) {
- return;
- }
-
- mStacks.remove(this);
- mStacks.add(0, this);
-
- if (task != null) {
- mTaskHistory.remove(task);
- mTaskHistory.add(0, task);
- updateTaskMovement(task, false);
- mWindowContainerController.positionChildAtBottom(task.getWindowContainerController());
- }
- }
-
- /**
- * @return the index to insert a new stack into, taking the always-on-top stacks into account.
- */
- private int findStackInsertIndex(boolean onTop) {
- if (onTop) {
- int addIndex = mStacks.size();
- if (addIndex > 0) {
- final ActivityStack topStack = mStacks.get(addIndex - 1);
- if (topStack.getWindowConfiguration().isAlwaysOnTop()
- && topStack != this) {
- // If the top stack is always on top, we move this stack just below it.
- addIndex--;
- }
- }
- return addIndex;
- } else {
- return 0;
- }
- }
-
boolean isFocusable() {
if (getWindowConfiguration().canReceiveKeys()) {
return true;
@@ -939,7 +868,7 @@
}
final boolean isAttached() {
- return mStacks != null;
+ return getParent() != null;
}
/**
@@ -1114,14 +1043,6 @@
"Launch completed; removing icicle of " + r.icicle);
}
- void addRecentActivityLocked(ActivityRecord r) {
- if (r != null) {
- final TaskRecord task = r.getTask();
- mRecentTasks.addLocked(task);
- task.touchActiveTime();
- }
- }
-
private void startLaunchTraces(String packageName) {
if (mFullyDrawnStartTime != 0) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
@@ -1536,7 +1457,7 @@
// focus). Also if there is an active pinned stack - we always want to notify it about
// task stack changes, because its positioning may depend on it.
if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
- || mService.mStackSupervisor.getStack(PINNED_STACK_ID) != null) {
+ || getDisplay().hasPinnedStack()) {
mService.mTaskChangeNotificationController.notifyTaskStackChanged();
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
}
@@ -1568,54 +1489,6 @@
}
}
- // Find the first visible activity above the passed activity and if it is translucent return it
- // otherwise return null;
- ActivityRecord findNextTranslucentActivity(ActivityRecord r) {
- TaskRecord task = r.getTask();
- if (task == null) {
- return null;
- }
-
- final ActivityStack stack = task.getStack();
- if (stack == null) {
- return null;
- }
-
- int stackNdx = mStacks.indexOf(stack);
-
- ArrayList<TaskRecord> tasks = stack.mTaskHistory;
- int taskNdx = tasks.indexOf(task);
-
- ArrayList<ActivityRecord> activities = task.mActivities;
- int activityNdx = activities.indexOf(r) + 1;
-
- final int numStacks = mStacks.size();
- while (stackNdx < numStacks) {
- final ActivityStack historyStack = mStacks.get(stackNdx);
- tasks = historyStack.mTaskHistory;
- final int numTasks = tasks.size();
- while (taskNdx < numTasks) {
- final TaskRecord currentTask = tasks.get(taskNdx);
- activities = currentTask.mActivities;
- final int numActivities = activities.size();
- while (activityNdx < numActivities) {
- final ActivityRecord activity = activities.get(activityNdx);
- if (!activity.finishing) {
- return historyStack.mFullscreen
- && currentTask.mFullscreen && activity.fullscreen ? null : activity;
- }
- ++activityNdx;
- }
- activityNdx = 0;
- ++taskNdx;
- }
- taskNdx = 0;
- ++stackNdx;
- }
-
- return null;
- }
-
/** Returns true if the stack contains a fullscreen task. */
private boolean hasFullscreenTask() {
for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
@@ -1659,9 +1532,11 @@
return false;
}
+ final ActivityStack stackBehind = mStackSupervisor.getStack(stackBehindId);
+ final boolean stackBehindHomeOrRecent = stackBehind != null
+ && stackBehind.isHomeOrRecentsStack();
if (!isHomeOrRecentsStack() && r.frontOfTask && task.isOverHomeStack()
- && !StackId.isHomeOrRecentsStack(stackBehindId)
- && !isActivityTypeAssistant()) {
+ && !stackBehindHomeOrRecent && !isActivityTypeAssistant()) {
// Stack isn't translucent if it's top activity should have the home stack
// behind it and the stack currently behind it isn't the home or recents stack
// or the assistant stack.
@@ -1679,119 +1554,146 @@
}
/**
- * Returns what the stack visibility should be: {@link #STACK_INVISIBLE} or
- * {@link #STACK_VISIBLE}.
+ * Returns true if the stack should be visible.
*
* @param starting The currently starting activity or null if there is none.
*/
- int shouldBeVisible(ActivityRecord starting) {
+ boolean shouldBeVisible(ActivityRecord starting) {
if (!isAttached() || mForceHidden) {
- return STACK_INVISIBLE;
+ return false;
}
if (mStackSupervisor.isFrontStackOnDisplay(this) || mStackSupervisor.isFocusedStack(this)) {
- return STACK_VISIBLE;
+ return true;
}
- final int stackIndex = mStacks.indexOf(this);
+ final ActivityDisplay display = getDisplay();
+ final ArrayList<ActivityStack> displayStacks = display.mStacks;
+ final int stackIndex = displayStacks.indexOf(this);
- if (stackIndex == mStacks.size() - 1) {
+ if (stackIndex == displayStacks.size() - 1) {
Slog.wtf(TAG,
"Stack=" + this + " isn't front stack but is at the top of the stack list");
- return STACK_INVISIBLE;
+ return false;
}
// Check position and visibility of this stack relative to the front stack on its display.
final ActivityStack topStack = getTopStackOnDisplay();
final int topStackId = topStack.mStackId;
+ final int windowingMode = getWindowingMode();
+ final int activityType = getActivityType();
- if (mStackId == DOCKED_STACK_ID) {
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// If the assistant stack is focused and translucent, then the docked stack is always
// visible
if (topStack.isActivityTypeAssistant()) {
- return (topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) ? STACK_VISIBLE
- : STACK_INVISIBLE;
+ return topStack.isStackTranslucent(starting, DOCKED_STACK_ID);
}
- return STACK_VISIBLE;
+ return true;
}
// Set home stack to invisible when it is below but not immediately below the docked stack
// A case would be if recents stack exists but has no tasks and is below the docked stack
// and home stack is below recents
- if (mStackId == HOME_STACK_ID) {
- int dockedStackIndex = mStacks.indexOf(mStackSupervisor.getStack(DOCKED_STACK_ID));
+ if (activityType == ACTIVITY_TYPE_HOME) {
+ final ActivityStack splitScreenStack = display.getStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
+ int dockedStackIndex = displayStacks.indexOf(splitScreenStack);
if (dockedStackIndex > stackIndex && stackIndex != dockedStackIndex - 1) {
- return STACK_INVISIBLE;
+ return false;
}
}
// Find the first stack behind front stack that actually got something visible.
- int stackBehindTopIndex = mStacks.indexOf(topStack) - 1;
+ int stackBehindTopIndex = displayStacks.indexOf(topStack) - 1;
while (stackBehindTopIndex >= 0 &&
- mStacks.get(stackBehindTopIndex).topRunningActivityLocked() == null) {
+ displayStacks.get(stackBehindTopIndex).topRunningActivityLocked() == null) {
stackBehindTopIndex--;
}
- final int stackBehindTopId = (stackBehindTopIndex >= 0)
- ? mStacks.get(stackBehindTopIndex).mStackId : INVALID_STACK_ID;
+ final ActivityStack stackBehindTop = (stackBehindTopIndex >= 0)
+ ? displayStacks.get(stackBehindTopIndex) : null;
+ int stackBehindTopId = INVALID_STACK_ID;
+ int stackBehindTopWindowingMode = WINDOWING_MODE_UNDEFINED;
+ int stackBehindTopActivityType = ACTIVITY_TYPE_UNDEFINED;
+ if (stackBehindTop != null) {
+ stackBehindTopId = stackBehindTop.mStackId;
+ stackBehindTopWindowingMode = stackBehindTop.getWindowingMode();
+ stackBehindTopActivityType = stackBehindTop.getActivityType();
+ }
+
final boolean alwaysOnTop = topStack.getWindowConfiguration().isAlwaysOnTop();
- if (topStackId == DOCKED_STACK_ID || alwaysOnTop) {
+ if (topStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || alwaysOnTop) {
if (stackIndex == stackBehindTopIndex) {
// Stacks directly behind the docked or pinned stack are always visible.
- return STACK_VISIBLE;
+ return true;
} else if (alwaysOnTop && stackIndex == stackBehindTopIndex - 1) {
// Otherwise, this stack can also be visible if it is directly behind a docked stack
// or translucent assistant stack behind an always-on-top top-most stack
- if (stackBehindTopId == DOCKED_STACK_ID) {
- return STACK_VISIBLE;
- } else if (stackBehindTopId == ASSISTANT_STACK_ID) {
- return mStacks.get(stackBehindTopIndex).isStackTranslucent(starting, mStackId)
- ? STACK_VISIBLE : STACK_INVISIBLE;
+ if (stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ return true;
+ } else if (stackBehindTopActivityType == ACTIVITY_TYPE_ASSISTANT) {
+ return displayStacks.get(stackBehindTopIndex).isStackTranslucent(
+ starting, mStackId);
}
}
}
- if (StackId.isBackdropToTranslucentActivity(topStackId)
+ if (topStack.isBackdropToTranslucentActivity()
&& topStack.isStackTranslucent(starting, stackBehindTopId)) {
// Stacks behind the fullscreen or assistant stack with a translucent activity are
// always visible so they can act as a backdrop to the translucent activity.
// For example, dialog activities
if (stackIndex == stackBehindTopIndex) {
- return STACK_VISIBLE;
+ return true;
}
if (stackBehindTopIndex >= 0) {
- if ((stackBehindTopId == DOCKED_STACK_ID
- || stackBehindTopId == PINNED_STACK_ID)
+ if ((stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || stackBehindTopWindowingMode == WINDOWING_MODE_PINNED)
&& stackIndex == (stackBehindTopIndex - 1)) {
// The stack behind the docked or pinned stack is also visible so we can have a
// complete backdrop to the translucent activity when the docked stack is up.
- return STACK_VISIBLE;
+ return true;
}
}
}
- if (StackId.isStaticStack(mStackId)) {
+ if (StackId.isStaticStack(mStackId)
+ || isHomeOrRecentsStack() || isActivityTypeAssistant()) {
// Visibility of any static stack should have been determined by the conditions above.
- return STACK_INVISIBLE;
+ return false;
}
- for (int i = stackIndex + 1; i < mStacks.size(); i++) {
- final ActivityStack stack = mStacks.get(i);
+ for (int i = stackIndex + 1; i < displayStacks.size(); i++) {
+ final ActivityStack stack = displayStacks.get(i);
if (!stack.mFullscreen && !stack.hasFullscreenTask()) {
continue;
}
- if (!StackId.isDynamicStacksVisibleBehindAllowed(stack.mStackId)) {
+ if (!stack.isDynamicStacksVisibleBehindAllowed()) {
// These stacks can't have any dynamic stacks visible behind them.
- return STACK_INVISIBLE;
+ return false;
}
if (!stack.isStackTranslucent(starting, INVALID_STACK_ID)) {
- return STACK_INVISIBLE;
+ return false;
}
}
- return STACK_VISIBLE;
+ return true;
+ }
+
+ private boolean isBackdropToTranslucentActivity() {
+ if (isActivityTypeAssistant()) {
+ return true;
+ }
+ final int windowingMode = getWindowingMode();
+ return windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ }
+
+ private boolean isDynamicStacksVisibleBehindAllowed() {
+ return isActivityTypeAssistant() || getWindowingMode() == WINDOWING_MODE_PINNED;
}
final int rankTaskLayers(int baseLayer) {
@@ -1828,12 +1730,10 @@
// If the top activity is not fullscreen, then we need to
// make sure any activities under it are now visible.
boolean aboveTop = top != null;
- final int stackVisibility = shouldBeVisible(starting);
- final boolean stackInvisible = stackVisibility != STACK_VISIBLE;
- boolean behindFullscreenActivity = stackInvisible;
+ final boolean stackShouldBeVisible = shouldBeVisible(starting);
+ boolean behindFullscreenActivity = !stackShouldBeVisible;
boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this)
&& (isInStackLocked(starting) == null);
- boolean behindTranslucentActivity = false;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -1857,11 +1757,8 @@
final boolean reallyVisible = checkKeyguardVisibility(r,
visibleIgnoringKeyguard, isTop);
if (visibleIgnoringKeyguard) {
- behindFullscreenActivity = updateBehindFullscreen(stackInvisible,
+ behindFullscreenActivity = updateBehindFullscreen(!stackShouldBeVisible,
behindFullscreenActivity, task, r);
- if (behindFullscreenActivity && !r.fullscreen) {
- behindTranslucentActivity = true;
- }
}
if (reallyVisible) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r
@@ -1897,10 +1794,10 @@
configChanges |= r.configChangeFlags;
} else {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r
- + " finishing=" + r.finishing + " state=" + r.state + " stackInvisible="
- + stackInvisible + " behindFullscreenActivity="
- + behindFullscreenActivity + " mLaunchTaskBehind="
- + r.mLaunchTaskBehind);
+ + " finishing=" + r.finishing + " state=" + r.state
+ + " stackShouldBeVisible=" + stackShouldBeVisible
+ + " behindFullscreenActivity=" + behindFullscreenActivity
+ + " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
makeInvisible(r);
}
}
@@ -1908,10 +1805,10 @@
// The visibility of tasks and the activities they contain in freeform stack are
// determined individually unlike other stacks where the visibility or fullscreen
// status of an activity in a previous task affects other.
- behindFullscreenActivity = stackVisibility == STACK_INVISIBLE;
- } else if (mStackId == HOME_STACK_ID) {
+ behindFullscreenActivity = !stackShouldBeVisible;
+ } else if (isActivityTypeHome()) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + task
- + " stackInvisible=" + stackInvisible
+ + " stackShouldBeVisible=" + stackShouldBeVisible
+ " behindFullscreenActivity=" + behindFullscreenActivity);
// No other task in the home stack should be visible behind the home activity.
// Home activities is usually a translucent activity with the wallpaper behind
@@ -2012,7 +1909,7 @@
* {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied.
*/
private boolean canShowWithInsecureKeyguard() {
- final ActivityStackSupervisor.ActivityDisplay activityDisplay = getDisplay();
+ final ActivityDisplay activityDisplay = getDisplay();
if (activityDisplay == null) {
throw new IllegalStateException("Stack is not attached to any display, stackId="
+ mStackId);
@@ -2192,7 +2089,7 @@
// activities as we need to display their starting window until they are done initializing.
boolean behindFullscreenActivity = false;
- if (shouldBeVisible(null) == STACK_INVISIBLE) {
+ if (!shouldBeVisible(null)) {
// The stack is not visible, so no activity in it should be displaying a starting
// window. Mark all activities below top and behind fullscreen.
aboveTop = false;
@@ -2268,9 +2165,7 @@
mResumedActivity = r;
r.state = ActivityState.RESUMED;
mService.setResumedActivityUncheckLocked(r, reason);
- final TaskRecord task = r.getTask();
- task.touchActiveTime();
- mRecentTasks.addLocked(task);
+ mStackSupervisor.addRecentActivity(r);
}
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
@@ -2553,128 +2448,139 @@
|| (lastStack.mLastPausedActivity != null
&& !lastStack.mLastPausedActivity.fullscreen));
- // This activity is now becoming visible.
- if (!next.visible || next.stopped || lastActivityTranslucent) {
- next.setVisibility(true);
- }
-
- // schedule launch ticks to collect information about slow apps.
- next.startLaunchTickingLocked();
-
- ActivityRecord lastResumedActivity =
- lastStack == null ? null :lastStack.mResumedActivity;
- ActivityState lastState = next.state;
-
- mService.updateCpuStats();
-
- if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next + " (in existing)");
-
- setResumedActivityLocked(next, "resumeTopActivityInnerLocked");
-
- mService.updateLruProcessLocked(next.app, true, null);
- updateLRUListLocked(next);
- mService.updateOomAdjLocked();
-
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order.
- boolean notUpdated = true;
- if (mStackSupervisor.isFocusedStack(this)) {
-
- // We have special rotation behavior when Keyguard is locked. Make sure all activity
- // visibilities are set correctly as well as the transition is updated if needed to
- // get the correct rotation behavior.
- // TODO: Remove this once visibilities are set correctly immediately when starting
- // an activity.
- if (mStackSupervisor.mKeyguardController.isKeyguardLocked()) {
- mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */,
- 0 /* configChanges */, false /* preserveWindows */);
- }
- final Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mStackSupervisor.getDisplayOverrideConfiguration(mDisplayId),
- next.mayFreezeScreenLocked(next.app) ? next.appToken : null, mDisplayId);
- if (config != null) {
- next.frozenBeforeDestroy = true;
- }
- notUpdated = !mService.updateDisplayOverrideConfigurationLocked(config, next,
- false /* deferResume */, mDisplayId);
- }
-
- if (notUpdated) {
- // The configuration update wasn't able to keep the existing
- // instance of the activity, and instead started a new one.
- // We should be all done, but let's just make sure our activity
- // is still at the top and schedule another run if something
- // weird happened.
- ActivityRecord nextNext = topRunningActivityLocked();
- if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES,
- "Activity config changed during resume: " + next
- + ", new next: " + nextNext);
- if (nextNext != next) {
- // Do over!
- mStackSupervisor.scheduleResumeTopActivities();
- }
- if (!next.visible || next.stopped) {
+ // The contained logic must be synchronized, since we are both changing the visibility
+ // and updating the {@link Configuration}. {@link ActivityRecord#setVisibility} will
+ // ultimately cause the client code to schedule a layout. Since layouts retrieve the
+ // current {@link Configuration}, we must ensure that the below code updates it before
+ // the layout can occur.
+ synchronized(mWindowManager.getWindowManagerLock()) {
+ // This activity is now becoming visible.
+ if (!next.visible || next.stopped || lastActivityTranslucent) {
next.setVisibility(true);
}
- next.completeResumeLocked();
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return true;
- }
- try {
- // Deliver all pending results.
- ArrayList<ResultInfo> a = next.results;
- if (a != null) {
- final int N = a.size();
- if (!next.finishing && N > 0) {
- if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
- "Delivering results to " + next + ": " + a);
- next.app.thread.scheduleSendResult(next.appToken, a);
+ // schedule launch ticks to collect information about slow apps.
+ next.startLaunchTickingLocked();
+
+ ActivityRecord lastResumedActivity =
+ lastStack == null ? null :lastStack.mResumedActivity;
+ ActivityState lastState = next.state;
+
+ mService.updateCpuStats();
+
+ if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next
+ + " (in existing)");
+
+ setResumedActivityLocked(next, "resumeTopActivityInnerLocked");
+
+ mService.updateLruProcessLocked(next.app, true, null);
+ updateLRUListLocked(next);
+ mService.updateOomAdjLocked();
+
+ // Have the window manager re-evaluate the orientation of
+ // the screen based on the new activity order.
+ boolean notUpdated = true;
+
+ if (mStackSupervisor.isFocusedStack(this)) {
+
+ // We have special rotation behavior when Keyguard is locked. Make sure all
+ // activity visibilities are set correctly as well as the transition is updated
+ // if needed to get the correct rotation behavior.
+ // TODO: Remove this once visibilities are set correctly immediately when
+ // starting an activity.
+ if (mStackSupervisor.mKeyguardController.isKeyguardLocked()) {
+ mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */,
+ 0 /* configChanges */, false /* preserveWindows */);
}
+ final Configuration config = mWindowManager.updateOrientationFromAppTokens(
+ mStackSupervisor.getDisplayOverrideConfiguration(mDisplayId),
+ next.mayFreezeScreenLocked(next.app) ? next.appToken : null,
+ mDisplayId);
+ if (config != null) {
+ next.frozenBeforeDestroy = true;
+ }
+ notUpdated = !mService.updateDisplayOverrideConfigurationLocked(config, next,
+ false /* deferResume */, mDisplayId);
}
- if (next.newIntents != null) {
- next.app.thread.scheduleNewIntent(
- next.newIntents, next.appToken, false /* andPause */);
+ if (notUpdated) {
+ // The configuration update wasn't able to keep the existing
+ // instance of the activity, and instead started a new one.
+ // We should be all done, but let's just make sure our activity
+ // is still at the top and schedule another run if something
+ // weird happened.
+ ActivityRecord nextNext = topRunningActivityLocked();
+ if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES,
+ "Activity config changed during resume: " + next
+ + ", new next: " + nextNext);
+ if (nextNext != next) {
+ // Do over!
+ mStackSupervisor.scheduleResumeTopActivities();
+ }
+ if (!next.visible || next.stopped) {
+ next.setVisibility(true);
+ }
+ next.completeResumeLocked();
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+ return true;
}
- // Well the app will no longer be stopped.
- // Clear app token stopped state in window manager if needed.
- next.notifyAppResumed(next.stopped);
+ try {
+ // Deliver all pending results.
+ ArrayList<ResultInfo> a = next.results;
+ if (a != null) {
+ final int N = a.size();
+ if (!next.finishing && N > 0) {
+ if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
+ "Delivering results to " + next + ": " + a);
+ next.app.thread.scheduleSendResult(next.appToken, a);
+ }
+ }
- EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
- System.identityHashCode(next), next.getTask().taskId,
- next.shortComponentName);
+ if (next.newIntents != null) {
+ next.app.thread.scheduleNewIntent(
+ next.newIntents, next.appToken, false /* andPause */);
+ }
- next.sleeping = false;
- mService.showUnsupportedZoomDialogIfNeededLocked(next);
- mService.showAskCompatModeDialogLocked(next);
- next.app.pendingUiClean = true;
- next.app.forceProcessStateUpTo(mService.mTopProcessState);
- next.clearOptionsLocked();
- next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
- mService.isNextTransitionForward(), resumeAnimOptions);
+ // Well the app will no longer be stopped.
+ // Clear app token stopped state in window manager if needed.
+ next.notifyAppResumed(next.stopped);
- if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed " + next);
- } catch (Exception e) {
- // Whoops, need to restart this activity!
- if (DEBUG_STATES) Slog.v(TAG_STATES, "Resume failed; resetting state to "
- + lastState + ": " + next);
- next.state = lastState;
- if (lastStack != null) {
- lastStack.mResumedActivity = lastResumedActivity;
+ EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
+ System.identityHashCode(next), next.getTask().taskId,
+ next.shortComponentName);
+
+ next.sleeping = false;
+ mService.showUnsupportedZoomDialogIfNeededLocked(next);
+ mService.showAskCompatModeDialogLocked(next);
+ next.app.pendingUiClean = true;
+ next.app.forceProcessStateUpTo(mService.mTopProcessState);
+ next.clearOptionsLocked();
+ next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
+ mService.isNextTransitionForward(), resumeAnimOptions);
+
+ if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
+ + next);
+ } catch (Exception e) {
+ // Whoops, need to restart this activity!
+ if (DEBUG_STATES) Slog.v(TAG_STATES, "Resume failed; resetting state to "
+ + lastState + ": " + next);
+ next.state = lastState;
+ if (lastStack != null) {
+ lastStack.mResumedActivity = lastResumedActivity;
+ }
+ Slog.i(TAG, "Restarting because process died: " + next);
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else if (SHOW_APP_STARTING_PREVIEW && lastStack != null &&
+ mStackSupervisor.isFrontStackOnDisplay(lastStack)) {
+ next.showStartingWindow(null /* prev */, false /* newTask */,
+ false /* taskSwitch */);
+ }
+ mStackSupervisor.startSpecificActivityLocked(next, true, false);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+ return true;
}
- Slog.i(TAG, "Restarting because process died: " + next);
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else if (SHOW_APP_STARTING_PREVIEW && lastStack != null &&
- mStackSupervisor.isFrontStackOnDisplay(lastStack)) {
- next.showStartingWindow(null /* prev */, false /* newTask */,
- false /* taskSwitch */);
- }
- mStackSupervisor.startSpecificActivityLocked(next, true, false);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return true;
}
// From this point on, if something goes wrong there is no way
@@ -2999,9 +2905,9 @@
// Ensure that we do not trigger entering PiP an activity on the pinned stack
return false;
}
- final int targetStackId = toFrontTask != null ? toFrontTask.getStackId()
- : toFrontActivity.getStackId();
- if (targetStackId == ASSISTANT_STACK_ID) {
+ final ActivityStack targetStack = toFrontTask != null
+ ? toFrontTask.getStack() : toFrontActivity.getStack();
+ if (targetStack != null && targetStack.isActivityTypeAssistant()) {
// Ensure the task/activity being brought forward is not the assistant
return false;
}
@@ -4558,7 +4464,7 @@
// Don't refocus if invisible to current user
final ActivityRecord top = tr.getTopActivity();
if (top == null || !top.okToShowLocked()) {
- addRecentActivityLocked(top);
+ mStackSupervisor.addRecentActivity(top);
ActivityOptions.abort(options);
return;
}
@@ -4611,7 +4517,7 @@
Slog.i(TAG, "moveTaskToBack: " + tr);
// If the task is locked, then show the lock task toast
- if (!mService.mLockTaskController.checkLockedTask(tr)) {
+ if (mService.mLockTaskController.checkLockedTask(tr)) {
return false;
}
@@ -4992,8 +4898,9 @@
}
ci.numActivities = numActivities;
ci.numRunning = numRunning;
- ci.supportsSplitScreenMultiWindow = task.supportsSplitScreen();
+ ci.supportsSplitScreenMultiWindow = task.supportsSplitScreenWindowingMode();
ci.resizeMode = task.mResizeMode;
+ ci.configuration = task.getConfiguration();
list.add(ci);
}
}
@@ -5162,8 +5069,7 @@
if (task.autoRemoveFromRecents() || isVoiceSession) {
// Task creator asked to remove this when done, or this task was a voice
// interaction, so it should not remain on the recent tasks list.
- mRecentTasks.remove(task);
- task.removedFromRecents();
+ mStackSupervisor.removeTaskFromRecents(task);
}
task.removeWindowContainer();
@@ -5180,9 +5086,8 @@
mStackSupervisor.moveHomeStackToFront(myReason);
}
}
- if (mStacks != null) {
- mStacks.remove(this);
- mStacks.add(0, this);
+ if (isAttached()) {
+ getDisplay().positionChildAtBottom(this);
}
if (!isHomeOrRecentsStack()) {
remove();
@@ -5359,7 +5264,7 @@
}
boolean shouldSleepActivities() {
- final ActivityStackSupervisor.ActivityDisplay display = getDisplay();
+ final ActivityDisplay display = getDisplay();
return display != null ? display.isSleeping() : mService.isSleepingLocked();
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 430b90b..42f3550 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -21,21 +21,14 @@
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
-import static android.app.ActivityManager.StackId.FIRST_STATIC_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
-import static android.app.ActivityManager.StackId.getStackIdForActivityType;
-import static android.app.ActivityManager.StackId.getStackIdForWindowingMode;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -52,9 +45,7 @@
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.INVALID_DISPLAY;
-import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
import static android.view.Display.TYPE_VIRTUAL;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
@@ -89,8 +80,6 @@
import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
-import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
-import static com.android.server.am.ActivityStack.STACK_VISIBLE;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -102,8 +91,6 @@
import static com.android.server.am.proto.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
import static com.android.server.am.proto.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
import static com.android.server.am.proto.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.proto.ActivityDisplayProto.STACKS;
-import static com.android.server.am.proto.ActivityDisplayProto.ID;
import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static java.lang.Integer.MAX_VALUE;
@@ -123,6 +110,7 @@
import android.app.ProfilerInfo;
import android.app.ResultInfo;
import android.app.WaitResult;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -174,7 +162,6 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.am.proto.ActivityDisplayProto;
import com.android.server.wm.ConfigurationContainer;
import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.WindowManagerService;
@@ -393,15 +380,11 @@
* They are used by components that may hide and block interaction with underlying
* activities.
*/
- final ArrayList<SleepToken> mSleepTokens = new ArrayList<SleepToken>();
+ final ArrayList<SleepToken> mSleepTokens = new ArrayList<>();
/** Stack id of the front stack when user switched, indexed by userId. */
SparseIntArray mUserStackInFront = new SparseIntArray(2);
- // TODO: Add listener for removal of references.
- /** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
- SparseArray<ActivityStack> mStacks = new SparseArray<>();
-
// TODO: There should be an ActivityDisplayController coordinating am/wm interaction.
/** Mapping from displayId to display current state */
private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>();
@@ -620,16 +603,13 @@
Display[] displays = mDisplayManager.getDisplays();
for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
final int displayId = displays[displayNdx].getDisplayId();
- ActivityDisplay activityDisplay = new ActivityDisplay(displayId);
- if (activityDisplay.mDisplay == null) {
- throw new IllegalStateException("Default Display does not exist");
- }
+ ActivityDisplay activityDisplay = new ActivityDisplay(this, displayId);
mActivityDisplays.put(displayId, activityDisplay);
calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
}
- mHomeStack = mFocusedStack = mLastFocusedStack =
- getStack(HOME_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+ mHomeStack = mFocusedStack = mLastFocusedStack = getDefaultDisplay().getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
}
@@ -685,7 +665,8 @@
}
void moveRecentsStackToFront(String reason) {
- final ActivityStack recentsStack = getStack(RECENTS_STACK_ID);
+ final ActivityStack recentsStack = getDefaultDisplay().getStack(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
if (recentsStack != null) {
recentsStack.moveToFront(reason);
}
@@ -1172,8 +1153,7 @@
void getTasksLocked(int maxNum, List<RunningTaskInfo> list, int callingUid, boolean allowed) {
// Gather all of the running tasks for each stack into runningTaskLists.
- ArrayList<ArrayList<RunningTaskInfo>> runningTaskLists =
- new ArrayList<ArrayList<RunningTaskInfo>>();
+ ArrayList<ArrayList<RunningTaskInfo>> runningTaskLists = new ArrayList<>();
final int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -1255,7 +1235,7 @@
synchronized (mService) {
return mService.getPackageManagerInternalLocked().resolveIntent(intent, resolvedType,
PackageManager.MATCH_INSTANT | PackageManager.MATCH_DEFAULT_ONLY | flags
- | ActivityManagerService.STOCK_PM_FLAGS, userId);
+ | ActivityManagerService.STOCK_PM_FLAGS, userId, true);
}
}
@@ -1975,7 +1955,7 @@
*/
void updateUserStackLocked(int userId, ActivityStack stack) {
if (userId != mCurrentUser) {
- mUserStackInFront.put(userId, stack != null ? stack.getStackId() : HOME_STACK_ID);
+ mUserStackInFront.put(userId, stack != null ? stack.getStackId() : mHomeStack.mStackId);
}
}
@@ -2116,19 +2096,19 @@
final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds());
task.updateOverrideConfiguration(bounds);
- int stackId = getLaunchStackId(null, options, task);
+ ActivityStack stack = getLaunchStack(null, options, task, ON_TOP);
- if (stackId != currentStack.mStackId) {
- task.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
- DEFER_RESUME, "findTaskToMoveToFrontLocked");
- stackId = currentStack.mStackId;
+ if (stack != currentStack) {
+ task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME,
+ "findTaskToMoveToFrontLocked");
+ stack = currentStack;
// moveTaskToStackUncheckedLocked() should already placed the task on top,
// still need moveTaskToFrontLocked() below for any transition settings.
}
- if (StackId.resizeStackWithLaunchBounds(stackId)) {
- resizeStackLocked(stackId, bounds,
- null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- !PRESERVE_WINDOWS, true /* allowResizeInDockedMode */, !DEFER_RESUME);
+ if (StackId.resizeStackWithLaunchBounds(stack.mStackId)) {
+ resizeStackLocked(stack.mStackId, bounds, null /* tempTaskBounds */,
+ null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
+ true /* allowResizeInDockedMode */, !DEFER_RESUME);
} else {
// WM resizeTask must be done after the task is moved to the correct stack,
// because Task's setBounds() also updates dim layer's bounds, but that has
@@ -2160,28 +2140,66 @@
}
protected <T extends ActivityStack> T getStack(int stackId) {
- return getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final T stack = mActivityDisplays.valueAt(i).getStack(stackId);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ return null;
}
- protected <T extends ActivityStack> T getStack(int stackId, boolean createStaticStackIfNeeded,
- boolean createOnTop) {
- final ActivityStack stack = mStacks.get(stackId);
- if (stack != null) {
- return (T) stack;
+ /** @see ActivityDisplay#getStack(int, int) */
+ private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final T stack = mActivityDisplays.valueAt(i).getStack(windowingMode, activityType);
+ if (stack != null) {
+ return stack;
+ }
}
- if (!createStaticStackIfNeeded || !StackId.isStaticStack(stackId)) {
- return null;
+ return null;
+ }
+
+ /**
+ * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
+ * @param windowingMode The windowing mode we are checking support for.
+ * @param supportsMultiWindow If we should consider support for multi-window mode in general.
+ * @param supportsSplitScreen If we should consider support for split-screen multi-window.
+ * @param supportsFreeform If we should consider support for freeform multi-window.
+ * @param supportsPip If we should consider support for picture-in-picture mutli-window.
+ * @param activityType The activity type under consideration.
+ * @return true if the windowing mode is supported.
+ */
+ boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
+ boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip,
+ int activityType) {
+
+ if (windowingMode == WINDOWING_MODE_UNDEFINED
+ || windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ return true;
}
- if (stackId == DOCKED_STACK_ID) {
- // Make sure recents stack exist when creating a dock stack as it normally need to be on
- // the other side of the docked stack and we make visibility decisions based on that.
- getStack(RECENTS_STACK_ID, CREATE_IF_NEEDED, createOnTop);
+ if (!supportsMultiWindow) {
+ return false;
}
- return (T) createStackOnDisplay(stackId, DEFAULT_DISPLAY, createOnTop);
+
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ return supportsSplitScreen && WindowConfiguration.supportSplitScreenWindowingMode(
+ windowingMode, activityType);
+ }
+
+ if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
+ return false;
+ }
+
+ if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) {
+ return false;
+ }
+ return true;
}
private int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
- @Nullable TaskRecord task) {
+ @Nullable TaskRecord task, int activityType) {
// First preference if the windowing mode in the activity options if set.
int windowingMode = (options != null)
@@ -2199,47 +2217,47 @@
}
// Make sure the windowing mode we are trying to use makes sense for what is supported.
- if (!mService.mSupportsMultiWindow && windowingMode != WINDOWING_MODE_FULLSCREEN) {
- windowingMode = WINDOWING_MODE_FULLSCREEN;
+ boolean supportsMultiWindow = mService.mSupportsMultiWindow;
+ boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow;
+ boolean supportsFreeform = mService.mSupportsFreeformWindowManagement;
+ boolean supportsPip = mService.mSupportsPictureInPicture;
+ if (supportsMultiWindow) {
+ if (task != null) {
+ supportsMultiWindow = task.isResizeable();
+ supportsSplitScreen = task.supportsSplitScreenWindowingMode();
+ // TODO: Do we need to check for freeform and Pip support here?
+ } else if (r != null) {
+ supportsMultiWindow = r.isResizeable();
+ supportsSplitScreen = r.supportsSplitScreenWindowingMode();
+ supportsFreeform = r.supportsFreeform();
+ supportsPip = r.supportsPictureInPicture();
+ }
}
- if (!mService.mSupportsSplitScreenMultiWindow
- && (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
- windowingMode = WINDOWING_MODE_FULLSCREEN;
+ if (isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
+ supportsFreeform, supportsPip, activityType)) {
+ return windowingMode;
}
-
- if (windowingMode == WINDOWING_MODE_FREEFORM
- && !mService.mSupportsFreeformWindowManagement) {
- windowingMode = WINDOWING_MODE_FULLSCREEN;
- }
-
- return windowingMode;
+ return WINDOWING_MODE_FULLSCREEN;
}
- private int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
+ int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
@Nullable TaskRecord task) {
- // First preference if the activity type in the activity options if set.
- int activityType = (options != null)
- ? options.getLaunchActivityType() : ACTIVITY_TYPE_UNDEFINED;
-
+ // Preference is given to the activity type for the activity then the task since the type
+ // once set shouldn't change.
+ int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
+ if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) {
+ activityType = task.getActivityType();
+ }
if (activityType != ACTIVITY_TYPE_UNDEFINED) {
return activityType;
}
-
- // If activity type is unset, then next preference is the task, then the activity record.
- if (task != null) {
- activityType = task.getActivityType();
- }
- if (activityType == ACTIVITY_TYPE_UNDEFINED && r != null) {
- activityType = r.getActivityType();
- }
- return activityType;
+ return options != null ? options.getLaunchActivityType() : ACTIVITY_TYPE_UNDEFINED;
}
- int getLaunchStackId(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
- @Nullable TaskRecord candidateTask) {
- return getLaunchStackId(r, options, candidateTask, INVALID_DISPLAY);
+ <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+ @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
+ return getLaunchStack(r, options, candidateTask, onTop, INVALID_DISPLAY);
}
/**
@@ -2251,8 +2269,9 @@
*
* @return The stack to use for the launch or INVALID_STACK_ID.
*/
- int getLaunchStackId(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
- @Nullable TaskRecord candidateTask, int candidateDisplayId) {
+ <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+ @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
+ int candidateDisplayId) {
int taskId = INVALID_TASK_ID;
int displayId = INVALID_DISPLAY;
//Rect bounds = null;
@@ -2274,13 +2293,13 @@
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options);
options.setLaunchTaskId(taskId);
if (task != null) {
- return task.getStack().mStackId;
+ return task.getStack();
}
}
- final int windowingMode = resolveWindowingMode(r, options, candidateTask);
final int activityType = resolveActivityType(r, options, candidateTask);
- ActivityStack stack = null;
+ int windowingMode = resolveWindowingMode(r, options, candidateTask, activityType);
+ T stack = null;
// Next preference for stack goes to the display Id set in the activity options or the
// candidate display.
@@ -2290,18 +2309,17 @@
if (displayId != INVALID_DISPLAY) {
if (r != null) {
// TODO: This should also take in the windowing mode and activity type into account.
- stack = getValidLaunchStackOnDisplay(displayId, r);
+ stack = (T) getValidLaunchStackOnDisplay(displayId, r);
if (stack != null) {
- return stack.mStackId;
+ return stack;
}
}
final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
if (display != null) {
for (int i = display.mStacks.size() - 1; i >= 0; --i) {
- stack = display.mStacks.get(i);
- if (stack.getWindowingMode() == windowingMode
- && stack.getActivityType() == activityType) {
- return stack.mStackId;
+ stack = (T) display.mStacks.get(i);
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return stack;
}
}
// TODO: We should create the stack we want on the display at this point.
@@ -2310,6 +2328,8 @@
// Give preference to the stack and display of the input task and activity if they match the
// mode we want to launch into.
+ stack = null;
+ ActivityDisplay display = null;
if (candidateTask != null) {
stack = candidateTask.getStack();
}
@@ -2317,40 +2337,33 @@
stack = r.getStack();
}
if (stack != null) {
- if (stack.getWindowingMode() == windowingMode
- && stack.getActivityType() == activityType) {
- return stack.mStackId;
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return stack;
}
- ActivityDisplay display = stack.getDisplay();
-
- if (display != null) {
- for (int i = display.mStacks.size() - 1; i >= 0; --i) {
- stack = display.mStacks.get(i);
- if (stack.getWindowingMode() == windowingMode
- && stack.getActivityType() == activityType) {
- return stack.mStackId;
- }
- }
- }
+ display = stack.getDisplay();
}
- // Give preference to the type of activity we are trying to launch followed by the windowing
- // mode.
- int stackId = getStackIdForActivityType(activityType);
- if (stackId != INVALID_STACK_ID) {
- return stackId;
+ if (display == null
+ // TODO: Can be removed once we figure-out how non-standard types should launch
+ // outside the default display.
+ || (activityType != ACTIVITY_TYPE_STANDARD
+ && activityType != ACTIVITY_TYPE_UNDEFINED)) {
+ display = getDefaultDisplay();
}
- stackId = getStackIdForWindowingMode(windowingMode);
- if (stackId != INVALID_STACK_ID) {
- return stackId;
+
+ stack = display.getOrCreateStack(windowingMode, activityType, onTop);
+ if (stack != null) {
+ return stack;
}
// Whatever...return some default for now.
if (candidateTask != null && candidateTask.mBounds != null
&& mService.mSupportsFreeformWindowManagement) {
- return FREEFORM_WORKSPACE_STACK_ID;
+ windowingMode = WINDOWING_MODE_FREEFORM;
+ } else {
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
}
- return FULLSCREEN_WORKSPACE_STACK_ID;
+ return display.getOrCreateStack(windowingMode, activityType, onTop);
}
/**
@@ -2367,50 +2380,48 @@
"Display with displayId=" + displayId + " not found.");
}
+ if (!r.canBeLaunchedOnDisplay(displayId)) {
+ return null;
+ }
+
// Return the topmost valid stack on the display.
for (int i = activityDisplay.mStacks.size() - 1; i >= 0; --i) {
final ActivityStack stack = activityDisplay.mStacks.get(i);
- if (isValidLaunchStackId(stack.mStackId, displayId, r)) {
+ if (isValidLaunchStack(stack, displayId, r)) {
return stack;
}
}
// If there is no valid stack on the external display - check if new dynamic stack will do.
- if (displayId != Display.DEFAULT_DISPLAY) {
- final int newDynamicStackId = getNextStackId();
- if (isValidLaunchStackId(newDynamicStackId, displayId, r)) {
- return createStackOnDisplay(newDynamicStackId, displayId, true /*onTop*/);
- }
+ if (displayId != DEFAULT_DISPLAY) {
+ return activityDisplay.createStack(
+ r.getWindowingMode(), r.getActivityType(), true /*onTop*/);
}
Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
return null;
}
- boolean isValidLaunchStackId(int stackId, int displayId, ActivityRecord r) {
- switch (stackId) {
- case INVALID_STACK_ID:
- case HOME_STACK_ID:
- return false;
- case FULLSCREEN_WORKSPACE_STACK_ID:
- return true;
- case FREEFORM_WORKSPACE_STACK_ID:
- return r.supportsFreeform();
- case DOCKED_STACK_ID:
- return r.supportsSplitScreen();
- case PINNED_STACK_ID:
- return r.supportsPictureInPicture();
- case RECENTS_STACK_ID:
- return r.isActivityTypeRecents();
- case ASSISTANT_STACK_ID:
- return r.isActivityTypeAssistant();
- default:
- if (StackId.isDynamicStack(stackId)) {
- return r.canBeLaunchedOnDisplay(displayId);
- }
- Slog.e(TAG, "isValidLaunchStackId: Unexpected stackId=" + stackId);
- return false;
+ // TODO: Can probably be consolidated into getLaunchStack()...
+ private boolean isValidLaunchStack(ActivityStack stack, int displayId, ActivityRecord r) {
+ switch (stack.getActivityType()) {
+ case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
+ case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
+ case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
}
+ switch (stack.getWindowingMode()) {
+ case WINDOWING_MODE_FULLSCREEN: return true;
+ case WINDOWING_MODE_FREEFORM: return r.supportsFreeform();
+ case WINDOWING_MODE_PINNED: return r.supportsPictureInPicture();
+ case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return r.supportsSplitScreenWindowingMode();
+ case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return r.supportsSplitScreenWindowingMode();
+ }
+
+ if (StackId.isDynamicStack(stack.mStackId)) {
+ return r.canBeLaunchedOnDisplay(displayId);
+ }
+ Slog.e(TAG, "isValidLaunchStack: Unexpected stack=" + stack);
+ return false;
}
ArrayList<ActivityStack> getStacks() {
@@ -2444,7 +2455,7 @@
for (int j = stacks.size() - 1; j >= 0; --j) {
final ActivityStack stack = stacks.get(j);
if (stack != currentFocus && stack.isFocusable()
- && stack.shouldBeVisible(null) != STACK_INVISIBLE) {
+ && stack.shouldBeVisible(null)) {
return stack;
}
}
@@ -2513,7 +2524,7 @@
return;
}
- final boolean splitScreenActive = getStack(DOCKED_STACK_ID) != null;
+ final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenStack();
if (!allowResizeInDockedMode
&& !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
// If the docked stack exists, don't resize non-floating stacks independently of the
@@ -2524,7 +2535,7 @@
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
mWindowManager.deferSurfaceLayout();
try {
- if (stack.supportSplitScreenWindowingMode()) {
+ if (stack.supportsSplitScreenWindowingMode()) {
if (bounds == null && stack.inSplitScreenWindowingMode()) {
// null bounds = fullscreen windowing mode...at least for now.
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -2545,22 +2556,22 @@
}
}
- private void deferUpdateBounds(int stackId) {
- final ActivityStack stack = getStack(stackId);
+ private void deferUpdateBounds(int activityType) {
+ final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
if (stack != null) {
stack.deferUpdateBounds();
}
}
- private void continueUpdateBounds(int stackId) {
- final ActivityStack stack = getStack(stackId);
+ private void continueUpdateBounds(int activityType) {
+ final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
if (stack != null) {
stack.continueUpdateBounds();
}
}
void notifyAppTransitionDone() {
- continueUpdateBounds(RECENTS_STACK_ID);
+ continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
final int taskId = mResizingTasksDuringAnimation.valueAt(i);
final TaskRecord task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY);
@@ -2571,29 +2582,31 @@
mResizingTasksDuringAnimation.clear();
}
- private void moveTasksToFullscreenStackInSurfaceTransaction(int fromStackId,
- boolean onTop) {
-
- final ActivityStack stack = getStack(fromStackId);
- if (stack == null) {
- return;
- }
+ /**
+ * TODO: This should just change the windowing mode and resize vs. actually moving task around.
+ * Can do that once we are no longer using static stack ids. Specially when
+ * {@link ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} is removed.
+ */
+ private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack,
+ int toDisplayId, boolean onTop) {
mWindowManager.deferSurfaceLayout();
try {
- if (fromStackId == DOCKED_STACK_ID) {
+ final int windowingMode = fromStack.getWindowingMode();
+ final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED;
+ final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId);
+
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// We are moving all tasks from the docked stack to the fullscreen stack,
// which is dismissing the docked stack, so resize all other stacks to
// fullscreen here already so we don't end up with resize trashing.
- for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- final ActivityStack otherStack = getStack(i);
- if (otherStack == null) {
- continue;
- }
+ final ArrayList<ActivityStack> displayStacks = toDisplay.mStacks;
+ for (int i = displayStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack otherStack = displayStacks.get(i);
if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
continue;
}
- resizeStackLocked(i, null, null, null, PRESERVE_WINDOWS,
+ resizeStackLocked(otherStack.mStackId, null, null, null, PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, DEFER_RESUME);
}
@@ -2602,35 +2615,35 @@
// resize when we remove task from it below and it is detached from the
// display because it no longer contains any tasks.
mAllowDockedStackResize = false;
- } else if (fromStackId == PINNED_STACK_ID) {
- if (onTop) {
- // Log if we are expanding the PiP to fullscreen
- MetricsLogger.action(mService.mContext,
- ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
- }
+ } else if (inPinnedWindowingMode && onTop) {
+ // Log if we are expanding the PiP to fullscreen
+ MetricsLogger.action(mService.mContext,
+ ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
}
- ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
- final boolean isFullscreenStackVisible = fullscreenStack != null &&
- fullscreenStack.shouldBeVisible(null) == STACK_VISIBLE;
+
// If we are moving from the pinned stack, then the animation takes care of updating
// the picture-in-picture mode.
- final boolean schedulePictureInPictureModeChange = (fromStackId == PINNED_STACK_ID);
- final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ final boolean schedulePictureInPictureModeChange = inPinnedWindowingMode;
+ final ArrayList<TaskRecord> tasks = fromStack.getAllTasks();
final int size = tasks.size();
+ final ActivityStack fullscreenStack = toDisplay.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, onTop);
+
if (onTop) {
+ final int returnToType =
+ toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED);
for (int i = 0; i < size; i++) {
final TaskRecord task = tasks.get(i);
final boolean isTopTask = i == (size - 1);
- if (fromStackId == PINNED_STACK_ID) {
+ if (inPinnedWindowingMode) {
// Update the return-to to reflect where the pinned stack task was moved
// from so that we retain the stack that was previously visible if the
// pinned stack is recreated. See moveActivityToPinnedStackLocked().
- task.setTaskToReturnTo(isFullscreenStackVisible ?
- ACTIVITY_TYPE_STANDARD : ACTIVITY_TYPE_HOME);
+ task.setTaskToReturnTo(returnToType);
}
// Defer resume until all the tasks have been moved to the fullscreen stack
- task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP,
- REPARENT_MOVE_STACK_TO_FRONT, isTopTask /* animate */, DEFER_RESUME,
+ task.reparent(fullscreenStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
+ isTopTask /* animate */, DEFER_RESUME,
schedulePictureInPictureModeChange,
"moveTasksToFullscreenStack - onTop");
}
@@ -2640,7 +2653,7 @@
// Position the tasks in the fullscreen stack in order at the bottom of the
// stack. Also defer resume until all the tasks have been moved to the
// fullscreen stack.
- task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, i /* position */,
+ task.reparent(fullscreenStack, i /* position */,
REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
schedulePictureInPictureModeChange,
"moveTasksToFullscreenStack - NOT_onTop");
@@ -2655,9 +2668,13 @@
}
}
- void moveTasksToFullscreenStackLocked(int fromStackId, boolean onTop) {
- mWindowManager.inSurfaceTransaction(
- () -> moveTasksToFullscreenStackInSurfaceTransaction(fromStackId, onTop));
+ void moveTasksToFullscreenStackLocked(ActivityStack fromStack, boolean onTop) {
+ moveTasksToFullscreenStackLocked(fromStack, DEFAULT_DISPLAY, onTop);
+ }
+
+ void moveTasksToFullscreenStackLocked(ActivityStack fromStack, int toDisplayId, boolean onTop) {
+ mWindowManager.inSurfaceTransaction(() ->
+ moveTasksToFullscreenStackInSurfaceTransaction(fromStack, toDisplayId, onTop));
}
void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
@@ -2668,7 +2685,7 @@
false /* deferResume */);
}
- void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
+ private void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
boolean preserveWindows, boolean deferResume) {
@@ -2677,7 +2694,8 @@
return;
}
- final ActivityStack stack = getStack(DOCKED_STACK_ID);
+ final ActivityStack stack = getDefaultDisplay().getStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
if (stack == null) {
Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
return;
@@ -2697,7 +2715,7 @@
// The dock stack either was dismissed or went fullscreen, which is kinda the same.
// In this case we make all other static stacks fullscreen and move all
// docked stack tasks to the fullscreen stack.
- moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, ON_TOP);
+ moveTasksToFullscreenStackLocked(stack, ON_TOP);
// stack shouldn't contain anymore activities, so nothing to resume.
r = null;
@@ -2706,13 +2724,14 @@
// static stacks need to be adjusted so they don't overlap with the docked stack.
// We get the bounds to use from window manager which has been adjusted for any
// screen controls and is also the same for all stacks.
+ final ArrayList<ActivityStack> stacks = getStacksOnDefaultDisplay();
final Rect otherTaskRect = new Rect();
- for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- if (i == DOCKED_STACK_ID) {
+ for (int i = stacks.size() - 1; i >= 0; --i) {
+ final ActivityStack current = stacks.get(i);
+ if (current.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
continue;
}
- final ActivityStack current = getStack(i);
- if (current == null || !current.supportSplitScreenWindowingMode()) {
+ if (!current.supportsSplitScreenWindowingMode()) {
continue;
}
// Need to set windowing mode here before we try to get the dock bounds.
@@ -2722,7 +2741,7 @@
tempRect /* outStackBounds */,
otherTaskRect /* outTempTaskBounds */, true /* ignoreVisibility */);
- resizeStackLocked(i, !tempRect.isEmpty() ? tempRect : null,
+ resizeStackLocked(current.mStackId, !tempRect.isEmpty() ? tempRect : null,
!otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
tempOtherTaskInsetBounds, preserveWindows,
true /* allowResizeInDockedMode */, deferResume);
@@ -2739,7 +2758,9 @@
}
void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
- final PinnedActivityStack stack = getStack(PINNED_STACK_ID);
+ // TODO(multi-display): Pinned stack display should be passed in.
+ final PinnedActivityStack stack = getDefaultDisplay().getStack(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (stack == null) {
Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
return;
@@ -2777,32 +2798,14 @@
}
}
- ActivityStack createStackOnDisplay(int stackId, int displayId, boolean onTop) {
- final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
- if (activityDisplay == null) {
- return null;
- }
- return createStack(stackId, activityDisplay, onTop);
-
- }
-
- ActivityStack createStack(int stackId, ActivityDisplay display, boolean onTop) {
- switch (stackId) {
- case PINNED_STACK_ID:
- return new PinnedActivityStack(display, stackId, this, mRecentTasks, onTop);
- default:
- return new ActivityStack(display, stackId, this, mRecentTasks, onTop);
- }
- }
-
- void removeStackInSurfaceTransaction(int stackId) {
+ private void removeStackInSurfaceTransaction(int stackId) {
final ActivityStack stack = getStack(stackId);
if (stack == null) {
return;
}
final ArrayList<TaskRecord> tasks = stack.getAllTasks();
- if (stack.getStackId() == PINNED_STACK_ID) {
+ if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) {
/**
* Workaround: Force-stop all the activities in the pinned stack before we reparent them
* to the fullscreen stack. This is to guarantee that when we are removing a stack,
@@ -2820,7 +2823,7 @@
true /* processPausingActivites */, null /* configuration */);
// Move all the tasks to the bottom of the fullscreen stack
- moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP);
+ moveTasksToFullscreenStackLocked(pinnedStack, !ON_TOP);
} else {
for (int i = tasks.size() - 1; i >= 0; i--) {
removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
@@ -2835,8 +2838,23 @@
* instead moved back onto the fullscreen stack.
*/
void removeStackLocked(int stackId) {
- mWindowManager.inSurfaceTransaction(
- () -> removeStackInSurfaceTransaction(stackId));
+ mWindowManager.inSurfaceTransaction(() -> removeStackInSurfaceTransaction(stackId));
+ }
+
+ /**
+ * Removes stacks in the input windowing modes from the system if they are of activity type
+ * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+ */
+ void removeStacksInWindowingModes(int... windowingModes) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ mActivityDisplays.valueAt(i).removeStacksInWindowingModes(windowingModes);
+ }
+ }
+
+ void removeStacksWithActivityTypes(int... activityTypes) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ mActivityDisplays.valueAt(i).removeStacksWithActivityTypes(activityTypes);
+ }
}
/**
@@ -2871,10 +2889,23 @@
return false;
}
+ void addRecentActivity(ActivityRecord r) {
+ if (r == null) {
+ return;
+ }
+ final TaskRecord task = r.getTask();
+ mRecentTasks.addLocked(task);
+ task.touchActiveTime();
+ }
+
+ void removeTaskFromRecents(TaskRecord task) {
+ mRecentTasks.remove(task);
+ task.removedFromRecents();
+ }
+
void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) {
if (removeFromRecents) {
- mRecentTasks.remove(tr);
- tr.removedFromRecents();
+ removeTaskFromRecents(tr);
}
ComponentName component = tr.getBaseIntent().getComponent();
if (component == null) {
@@ -2961,11 +2992,11 @@
* @return true if the task has been restored successfully.
*/
boolean restoreRecentTaskLocked(TaskRecord task, ActivityOptions aOptions) {
- final int stackId = getLaunchStackId(null, aOptions, task);
+ final ActivityStack stack = getLaunchStack(null, aOptions, task, !ON_TOP);
final ActivityStack currentStack = task.getStack();
if (currentStack != null) {
// Task has already been restored once. See if we need to do anything more
- if (currentStack.mStackId == stackId) {
+ if (currentStack == stack) {
// Nothing else to do since it is already restored in the right stack.
return true;
}
@@ -2974,11 +3005,9 @@
currentStack.removeTask(task, "restoreRecentTaskLocked", REMOVE_TASK_MODE_MOVING);
}
- final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, !ON_TOP);
-
- stack.addTask(task, false /* toTop */, "restoreRecentTask");
+ stack.addTask(task, !ON_TOP, "restoreRecentTask");
// TODO: move call for creation here and other place into Stack.addTask()
- task.createWindowContainer(false /* toTop */, true /* showForAllUsers */);
+ task.createWindowContainer(!ON_TOP, true /* showForAllUsers */);
if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
"Added restored task=" + task + " to stack=" + stack);
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -3000,7 +3029,7 @@
throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown displayId="
+ displayId);
}
- final ActivityStack stack = mStacks.get(stackId);
+ final ActivityStack stack = getStack(stackId);
if (stack == null) {
throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown stackId="
+ stackId);
@@ -3025,8 +3054,10 @@
* Returns the reparent target stack, creating the stack if necessary. This call also enforces
* the various checks on tasks that are going to be reparented from one stack to another.
*/
- ActivityStack getReparentTargetStack(TaskRecord task, int stackId, boolean toTop) {
+ ActivityStack getReparentTargetStack(TaskRecord task, ActivityStack stack, boolean toTop) {
final ActivityStack prevStack = task.getStack();
+ final int stackId = stack.mStackId;
+ final boolean inMultiWindowMode = stack.inMultiWindowMode();
// Check that we aren't reparenting to the same stack that the task is already in
if (prevStack != null && prevStack.mStackId == stackId) {
@@ -3037,22 +3068,22 @@
// Ensure that we aren't trying to move into a multi-window stack without multi-window
// support
- if (StackId.isMultiWindowStack(stackId) && !mService.mSupportsMultiWindow) {
+ if (inMultiWindowMode && !mService.mSupportsMultiWindow) {
throw new IllegalArgumentException("Device doesn't support multi-window, can not"
- + " reparent task=" + task + " to stackId=" + stackId);
+ + " reparent task=" + task + " to stack=" + stack);
}
// Ensure that we're not moving a task to a dynamic stack if device doesn't support
// multi-display.
- // TODO(multi-display): Support non-dynamic stacks on secondary displays.
- if (StackId.isDynamicStack(stackId) && !mService.mSupportsMultiDisplay) {
+ if (stack.mDisplayId != DEFAULT_DISPLAY && !mService.mSupportsMultiDisplay) {
throw new IllegalArgumentException("Device doesn't support multi-display, can not"
+ " reparent task=" + task + " to stackId=" + stackId);
}
// Ensure that we aren't trying to move into a freeform stack without freeform
// support
- if (stackId == FREEFORM_WORKSPACE_STACK_ID && !mService.mSupportsFreeformWindowManagement) {
+ if (stack.getWindowingMode() == WINDOWING_MODE_FREEFORM
+ && !mService.mSupportsFreeformWindowManagement) {
throw new IllegalArgumentException("Device doesn't support freeform, can not reparent"
+ " task=" + task);
}
@@ -3061,25 +3092,25 @@
// used for split-screen mode and will cause things like the docked divider to show up. We
// instead leave the task in its current stack or move it to the fullscreen stack if it
// isn't currently in a stack.
- if (stackId == DOCKED_STACK_ID && !task.isResizeable()) {
- stackId = (prevStack != null) ? prevStack.mStackId : FULLSCREEN_WORKSPACE_STACK_ID;
+ if (inMultiWindowMode && !task.isResizeable()) {
Slog.w(TAG, "Can not move unresizeable task=" + task + " to docked stack."
+ " Moving to stackId=" + stackId + " instead.");
+ // Temporarily disable resizeablility of the task as we don't want it to be resized if,
+ // for example, a docked stack is created which will lead to the stack we are moving
+ // from being resized and and its resizeable tasks being resized.
+ try {
+ task.mTemporarilyUnresizable = true;
+ stack = stack.getDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop);
+ } finally {
+ task.mTemporarilyUnresizable = false;
+ }
}
-
- // Temporarily disable resizeablility of the task as we don't want it to be resized if, for
- // example, a docked stack is created which will lead to the stack we are moving from being
- // resized and and its resizeable tasks being resized.
- try {
- task.mTemporarilyUnresizable = true;
- return getStack(stackId, CREATE_IF_NEEDED, toTop);
- } finally {
- task.mTemporarilyUnresizable = false;
- }
+ return stack;
}
boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect destBounds) {
- final ActivityStack stack = getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
+ final ActivityStack stack = getStack(stackId);
if (stack == null) {
throw new IllegalArgumentException(
"moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId);
@@ -3109,12 +3140,17 @@
mWindowManager.deferSurfaceLayout();
+ final ActivityDisplay display = r.getStack().getDisplay();
+ PinnedActivityStack stack = display.getPinnedStack();
+
// This will clear the pinned stack by moving an existing task to the full screen stack,
// ensuring only one task is present.
- moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP);
+ if (stack != null) {
+ moveTasksToFullscreenStackLocked(stack, !ON_TOP);
+ }
// Need to make sure the pinned stack exist so we can resize it below...
- final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+ stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
try {
final TaskRecord task = r.getTask();
@@ -3138,8 +3174,8 @@
moveHomeStackToFront(reason);
}
// Defer resume until below, and do not schedule PiP changes until we animate below
- task.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
- DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
+ task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
+ false /* schedulePictureInPictureModeChange */, reason);
} else {
// There are multiple activities in the task and moving the top activity should
// reveal/leave the other activities in their original task.
@@ -3154,7 +3190,7 @@
r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
// Defer resume until below, and do not schedule PiP changes until we animate below
- newTask.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+ newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
}
@@ -3180,7 +3216,7 @@
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeFocusedStackTopActivityLocked();
- mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName,
+ mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName, r.userId,
r.getTask().taskId);
}
@@ -3241,6 +3277,9 @@
// tasks should always have lower priority than any affinity-matching tasks
// in the fullscreen stacks
affinityMatch = mTmpFindTaskResult.r;
+ } else if (DEBUG_TASKS && mTmpFindTaskResult.matchedByRootAffinity) {
+ Slog.d(TAG_TASKS, "Skipping match on different display "
+ + mTmpFindTaskResult.r.getDisplayId() + " " + displayId);
}
}
}
@@ -3605,14 +3644,17 @@
boolean switchUserLocked(int userId, UserState uss) {
final int focusStackId = mFocusedStack.getStackId();
// We dismiss the docked stack whenever we switch users.
- moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, focusStackId == DOCKED_STACK_ID);
+ final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenStack();
+ if (dockedStack != null) {
+ moveTasksToFullscreenStackLocked(dockedStack, mFocusedStack == dockedStack);
+ }
// Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
// also cause all tasks to be moved to the fullscreen stack at a position that is
// appropriate.
removeStackLocked(PINNED_STACK_ID);
mUserStackInFront.put(mCurrentUser, focusStackId);
- final int restoreStackId = mUserStackInFront.get(userId, HOME_STACK_ID);
+ final int restoreStackId = mUserStackInFront.get(userId, mHomeStack.mStackId);
mCurrentUser = userId;
mStartingUsers.add(uss);
@@ -3645,7 +3687,7 @@
/** Checks whether the userid is a profile of the current user. */
boolean isCurrentProfileLocked(int userId) {
if (userId == mCurrentUser) return true;
- return mService.mUserController.isCurrentProfileLocked(userId);
+ return mService.mUserController.isCurrentProfile(userId);
}
/**
@@ -3752,7 +3794,10 @@
pw.print(prefix);
pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
- pw.print(prefix); pw.println("mStacks=" + mStacks);
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.valueAt(i);
+ pw.println(prefix + "displayId=" + display.mDisplayId + " mStacks=" + display.mStacks);
+ }
if (!mWaitingForActivityVisible.isEmpty()) {
pw.print(prefix); pw.println("mWaitingForActivityVisible=");
for (int i = 0; i < mWaitingForActivityVisible.size(); ++i) {
@@ -3813,8 +3858,7 @@
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
ActivityStack stack = stacks.get(stackNdx);
- if (!dumpVisibleStacksOnly ||
- stack.shouldBeVisible(null) == STACK_VISIBLE) {
+ if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
activities.addAll(stack.getDumpActivitiesLocked(name));
}
}
@@ -4040,15 +4084,22 @@
return getActivityDisplayOrCreateLocked(displayId) != null;
}
+ // TODO: Look into consolidating with getActivityDisplayOrCreateLocked()
ActivityDisplay getActivityDisplay(int displayId) {
return mActivityDisplays.get(displayId);
}
+ // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
+ ActivityDisplay getDefaultDisplay() {
+ return mActivityDisplays.get(DEFAULT_DISPLAY);
+ }
+
/**
* Get an existing instance of {@link ActivityDisplay} or create new if there is a
* corresponding record in display manager.
*/
- private ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) {
+ // TODO: Look into consolidating with getActivityDisplay()
+ ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
if (activityDisplay != null) {
return activityDisplay;
@@ -4063,17 +4114,18 @@
return null;
}
// The display hasn't been added to ActivityManager yet, create a new record now.
- activityDisplay = new ActivityDisplay(displayId);
- if (activityDisplay.mDisplay == null) {
- Slog.w(TAG, "Display " + displayId + " gone before initialization complete");
- return null;
- }
- mActivityDisplays.put(displayId, activityDisplay);
+ activityDisplay = new ActivityDisplay(this, displayId);
+ attachDisplay(activityDisplay);
calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
mWindowManager.onDisplayAdded(displayId);
return activityDisplay;
}
+ @VisibleForTesting
+ void attachDisplay(ActivityDisplay display) {
+ mActivityDisplays.put(display.mDisplayId, display);
+ }
+
private void calculateDefaultMinimalSizeOfResizeableTasks(ActivityDisplay display) {
mDefaultMinSizeOfResizeableTask =
mService.mContext.getResources().getDimensionPixelSize(
@@ -4101,7 +4153,7 @@
// Moving all tasks to fullscreen stack, because it's guaranteed to be
// a valid launch stack for all activities. This way the task history from
// external display will be preserved on primary after move.
- moveTasksToFullscreenStackLocked(stack.getStackId(), true /* onTop */);
+ moveTasksToFullscreenStackLocked(stack, true /* onTop */);
}
}
@@ -4163,7 +4215,7 @@
if (display.mAllSleepTokens.isEmpty()) {
return;
}
- for (SleepTokenImpl token : display.mAllSleepTokens) {
+ for (SleepToken token : display.mAllSleepTokens) {
mSleepTokens.remove(token);
}
display.mAllSleepTokens.clear();
@@ -4171,7 +4223,7 @@
mService.updateSleepIfNeededLocked();
}
- private StackInfo getStackInfoLocked(ActivityStack stack) {
+ private StackInfo getStackInfo(ActivityStack stack) {
final int displayId = stack.mDisplayId;
final ActivityDisplay display = mActivityDisplays.get(displayId);
StackInfo info = new StackInfo();
@@ -4179,11 +4231,10 @@
info.displayId = displayId;
info.stackId = stack.mStackId;
info.userId = stack.mCurrentUser;
- info.visible = stack.shouldBeVisible(null) == STACK_VISIBLE;
+ info.visible = stack.shouldBeVisible(null);
// A stack might be not attached to a display.
- info.position = display != null
- ? display.mStacks.indexOf(stack)
- : 0;
+ info.position = display != null ? display.mStacks.indexOf(stack) : 0;
+ info.configuration.setTo(stack.getConfiguration());
ArrayList<TaskRecord> tasks = stack.getAllTasks();
final int numTasks = tasks.size();
@@ -4212,20 +4263,25 @@
return info;
}
- StackInfo getStackInfoLocked(int stackId) {
+ StackInfo getStackInfo(int stackId) {
ActivityStack stack = getStack(stackId);
if (stack != null) {
- return getStackInfoLocked(stack);
+ return getStackInfo(stack);
}
return null;
}
+ StackInfo getStackInfo(int windowingMode, int activityType) {
+ final ActivityStack stack = getStack(windowingMode, activityType);
+ return (stack != null) ? getStackInfo(stack) : null;
+ }
+
ArrayList<StackInfo> getAllStackInfosLocked() {
ArrayList<StackInfo> list = new ArrayList<>();
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int ndx = stacks.size() - 1; ndx >= 0; --ndx) {
- list.add(getStackInfoLocked(stacks.get(ndx)));
+ list.add(getStackInfo(stacks.get(ndx)));
}
}
return list;
@@ -4272,7 +4328,8 @@
}
final ActivityRecord topActivity = task.getTopActivity();
- if (launchOnSecondaryDisplayFailed || !task.supportsSplitScreen() || forceNonResizable) {
+ if (launchOnSecondaryDisplayFailed
+ || !task.supportsSplitScreenWindowingMode() || forceNonResizable) {
if (launchOnSecondaryDisplayFailed) {
// Display a warning toast that we tried to put a non-resizeable task on a secondary
// display with config different from global config.
@@ -4286,7 +4343,12 @@
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
// we need to move it to top of fullscreen stack, otherwise it will be covered.
- moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, actualStackId == DOCKED_STACK_ID);
+
+ final ActivityStack dockedStack = task.getStack().getDisplay().getSplitScreenStack();
+ if (dockedStack != null) {
+ moveTasksToFullscreenStackLocked(dockedStack,
+ actualStackId == dockedStack.getStackId());
+ }
} else if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
&& !topActivity.noDisplay) {
final String packageName = topActivity.appInfo.packageName;
@@ -4491,133 +4553,6 @@
}
}
- // TODO: Move to its own file.
- /** Exactly one of these classes per Display in the system. Capable of holding zero or more
- * attached {@link ActivityStack}s */
- class ActivityDisplay extends ConfigurationContainer {
- /** Actual Display this object tracks. */
- int mDisplayId;
- Display mDisplay;
-
- /** All of the stacks on this display. Order matters, topmost stack is in front of all other
- * stacks, bottommost behind. Accessed directly by ActivityManager package classes */
- final ArrayList<ActivityStack> mStacks = new ArrayList<>();
-
- /** Array of all UIDs that are present on the display. */
- private IntArray mDisplayAccessUIDs = new IntArray();
-
- /** All tokens used to put activities on this stack to sleep (including mOffToken) */
- final ArrayList<SleepTokenImpl> mAllSleepTokens = new ArrayList<>();
- /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
- SleepToken mOffToken;
-
- private boolean mSleeping;
-
- @VisibleForTesting
- ActivityDisplay() {
- mActivityDisplays.put(mDisplayId, this);
- }
-
- // After instantiation, check that mDisplay is not null before using this. The alternative
- // is for this to throw an exception if mDisplayManager.getDisplay() returns null.
- ActivityDisplay(int displayId) {
- final Display display = mDisplayManager.getDisplay(displayId);
- if (display == null) {
- return;
- }
- init(display);
- }
-
- void init(Display display) {
- mDisplay = display;
- mDisplayId = display.getDisplayId();
- }
-
- void attachStack(ActivityStack stack, int position) {
- if (DEBUG_STACK) Slog.v(TAG_STACK, "attachStack: attaching " + stack
- + " to displayId=" + mDisplayId + " position=" + position);
- mStacks.add(position, stack);
- mService.updateSleepIfNeededLocked();
- }
-
- void detachStack(ActivityStack stack) {
- if (DEBUG_STACK) Slog.v(TAG_STACK, "detachStack: detaching " + stack
- + " from displayId=" + mDisplayId);
- mStacks.remove(stack);
- mService.updateSleepIfNeededLocked();
- }
-
- @Override
- public String toString() {
- return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
- }
-
- @Override
- protected int getChildCount() {
- return mStacks.size();
- }
-
- @Override
- protected ConfigurationContainer getChildAt(int index) {
- return mStacks.get(index);
- }
-
- @Override
- protected ConfigurationContainer getParent() {
- return ActivityStackSupervisor.this;
- }
-
- boolean isPrivate() {
- return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
- }
-
- boolean isUidPresent(int uid) {
- for (ActivityStack stack : mStacks) {
- if (stack.isUidPresent(uid)) {
- return true;
- }
- }
- return false;
- }
-
- /** Update and get all UIDs that are present on the display and have access to it. */
- private IntArray getPresentUIDs() {
- mDisplayAccessUIDs.clear();
- for (ActivityStack stack : mStacks) {
- stack.getPresentUIDs(mDisplayAccessUIDs);
- }
- return mDisplayAccessUIDs;
- }
-
- boolean shouldDestroyContentOnRemove() {
- return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
- }
-
- boolean shouldSleep() {
- return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
- && (mService.mRunningVoice == null);
- }
-
- boolean isSleeping() {
- return mSleeping;
- }
-
- void setIsSleeping(boolean asleep) {
- mSleeping = asleep;
- }
-
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
- super.writeToProto(proto, ActivityDisplayProto.CONFIGURATION_CONTAINER);
- proto.write(ID, mDisplayId);
- for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = mStacks.get(stackNdx);
- stack.writeToProto(proto, STACKS);
- }
- proto.end(token);
- }
- }
-
ActivityStack findStackBehind(ActivityStack stack) {
// TODO(multi-display): We are only looking for stacks on the default display.
final ActivityDisplay display = mActivityDisplays.get(DEFAULT_DISPLAY);
@@ -4672,14 +4607,14 @@
// Defer updating the stack in which recents is until the app transition is done, to
// not run into issues where we still need to draw the task in recents but the
// docked stack is already created.
- deferUpdateBounds(RECENTS_STACK_ID);
+ deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
}
task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE,
activityOptions);
if (task == null) {
- continueUpdateBounds(RECENTS_STACK_ID);
+ continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
mWindowManager.executeAppTransition();
throw new IllegalArgumentException(
"startActivityFromRecentsInner: Task " + taskId + " not found.");
@@ -4687,13 +4622,10 @@
// Since we don't have an actual source record here, we assume that the currently
// focused activity was the source.
- final ActivityStack focusedStack = getFocusedStack();
- final ActivityRecord sourceRecord = focusedStack != null
- ? focusedStack.topActivity() : null;
- final int stackId = getLaunchStackId(null, activityOptions, task);
+ final ActivityStack stack = getLaunchStack(null, activityOptions, task, ON_TOP);
- if (stackId != INVALID_STACK_ID && task.getStackId() != stackId) {
- task.reparent(stackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
+ if (stack != null && task.getStack() != stack) {
+ task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"startActivityFromRecents");
}
@@ -4718,10 +4650,7 @@
}
mService.mActivityStarter.postStartActivityProcessing(task.getTopActivity(),
- ActivityManager.START_TASK_TO_FRONT,
- sourceRecord != null
- ? sourceRecord.getTask().getStackId() : INVALID_STACK_ID,
- sourceRecord, task.getStack());
+ ActivityManager.START_TASK_TO_FRONT, task.getStack());
return ActivityManager.START_TASK_TO_FRONT;
}
callingUid = task.mCallingUid;
@@ -4753,8 +4682,7 @@
for (int j = display.mStacks.size() - 1; j >= 0; j--) {
final ActivityStack stack = display.mStacks.get(j);
// Get top activity from a visible stack and add it to the list.
- if (stack.shouldBeVisible(null /* starting */)
- == ActivityStack.STACK_VISIBLE) {
+ if (stack.shouldBeVisible(null /* starting */)) {
final ActivityRecord top = stack.topActivity();
if (top != null) {
if (stack == mFocusedStack) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index d94e866..ed33d58 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -27,19 +27,18 @@
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityManager.StackId;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
import static android.app.ActivityManager.StackId.isDynamicStack;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
@@ -78,8 +77,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.ANIMATE;
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
-import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -169,7 +166,8 @@
private boolean mDoResume;
private int mStartFlags;
private ActivityRecord mSourceRecord;
- private int mSourceDisplayId;
+ // The display to launch the activity onto, barring any strong reason to do otherwise.
+ private int mPreferredDisplayId;
private TaskRecord mInTask;
private boolean mAddingToTask;
@@ -224,7 +222,7 @@
mDoResume = false;
mStartFlags = 0;
mSourceRecord = null;
- mSourceDisplayId = INVALID_DISPLAY;
+ mPreferredDisplayId = INVALID_DISPLAY;
mInTask = null;
mAddingToTask = false;
@@ -591,9 +589,7 @@
auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo);
}
- void postStartActivityProcessing(
- ActivityRecord r, int result, int prevFocusedStackId, ActivityRecord sourceRecord,
- ActivityStack targetStack) {
+ void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) {
if (ActivityManager.isStartResultFatalError(result)) {
return;
@@ -615,7 +611,8 @@
}
if (startedActivityStackId == DOCKED_STACK_ID) {
- final ActivityStack homeStack = mSupervisor.getStack(HOME_STACK_ID);
+ final ActivityStack homeStack = mSupervisor.getDefaultDisplay().getStack(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
final boolean homeStackVisible = homeStack != null && homeStack.isVisible();
if (homeStackVisible) {
// We launch an activity while being in home stack, which means either launcher or
@@ -1003,8 +1000,7 @@
mService.mWindowManager.continueSurfaceLayout();
}
- postStartActivityProcessing(r, result, mSupervisor.getLastStack().mStackId, mSourceRecord,
- mTargetStack);
+ postStartActivityProcessing(r, result, mTargetStack);
return result;
}
@@ -1240,7 +1236,7 @@
mOptions);
}
} else {
- mTargetStack.addRecentActivityLocked(mStartActivity);
+ mSupervisor.addRecentActivity(mStartActivity);
}
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
@@ -1263,7 +1259,7 @@
mVoiceSession = voiceSession;
mVoiceInteractor = voiceInteractor;
- mSourceDisplayId = getSourceDisplayId(mSourceRecord, mStartActivity);
+ mPreferredDisplayId = getPreferedDisplayId(mSourceRecord, mStartActivity, options);
mLaunchBounds = getOverrideBounds(r, options, inTask);
@@ -1518,7 +1514,7 @@
!mLaunchSingleTask);
} else {
// Otherwise find the best task to put the activity in.
- intentActivity = mSupervisor.findTaskLocked(mStartActivity, mSourceDisplayId);
+ intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
}
}
return intentActivity;
@@ -1526,10 +1522,12 @@
/**
* Returns the ID of the display to use for a new activity. If the device is in VR mode,
- * then return the Vr mode's virtual display ID. If not, if the source activity has
- * a explicit display ID set, use that to launch the activity.
+ * then return the Vr mode's virtual display ID. If not, if the activity was started with
+ * a launchDisplayId, use that. Otherwise, if the source activity has a explicit display ID
+ * set, use that to launch the activity.
*/
- private int getSourceDisplayId(ActivityRecord sourceRecord, ActivityRecord startingActivity) {
+ private int getPreferedDisplayId(
+ ActivityRecord sourceRecord, ActivityRecord startingActivity, ActivityOptions options) {
// Check if the Activity is a VR activity. If so, the activity should be launched in
// main display.
if (startingActivity != null && startingActivity.requestedVrComponent != null) {
@@ -1546,6 +1544,13 @@
return displayId;
}
+ // If the caller requested a display, prefer that display.
+ final int launchDisplayId =
+ (options != null) ? options.getLaunchDisplayId() : INVALID_DISPLAY;
+ if (launchDisplayId != INVALID_DISPLAY) {
+ return launchDisplayId;
+ }
+
displayId = sourceRecord != null ? sourceRecord.getDisplayId() : INVALID_DISPLAY;
// If the activity has a displayId set explicitly, launch it on the same displayId.
if (displayId != INVALID_DISPLAY) {
@@ -1604,12 +1609,11 @@
mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions,
mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
mMovedToFront = true;
- } else if (launchStack.mStackId == DOCKED_STACK_ID
- || launchStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ } else if (launchStack.inSplitScreenWindowingMode()) {
if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
// If we want to launch adjacent and mTargetStack is not the computed
// launch stack - move task to top of computed stack.
- intentTask.reparent(launchStack.mStackId, ON_TOP,
+ intentTask.reparent(launchStack, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"launchToSide");
} else {
@@ -1626,17 +1630,17 @@
// Target and computed stacks are on different displays and we've
// found a matching task - move the existing instance to that display and
// move it to front.
- intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP,
+ intentActivity.getTask().reparent(launchStack, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentToDisplay");
mMovedToFront = true;
- } else if (launchStack.getStackId() == StackId.HOME_STACK_ID
- && mTargetStack.getStackId() != StackId.HOME_STACK_ID) {
+ } else if (launchStack.isActivityTypeHome()
+ && !mTargetStack.isActivityTypeHome()) {
// It is possible for the home activity to be in another stack initially.
// For example, the activity may have been initially started with an intent
// which placed it in the fullscreen stack. To ensure the proper handling of
// the activity based on home stack assumptions, we must move it over.
- intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP,
+ intentActivity.getTask().reparent(launchStack, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentingHome");
mMovedToFront = true;
@@ -1795,15 +1799,8 @@
mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
mVoiceInteractor, !mLaunchTaskBehind /* toTop */);
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
- if (mLaunchBounds != null) {
- final int stackId = mTargetStack.mStackId;
- if (StackId.resizeStackWithLaunchBounds(stackId)) {
- mService.resizeStack(
- stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
- } else {
- mStartActivity.getTask().updateOverrideConfiguration(mLaunchBounds);
- }
- }
+ updateBounds(mStartActivity.getTask(), mLaunchBounds);
+
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
+ " in new task " + mStartActivity.getTask());
} else {
@@ -1871,8 +1868,8 @@
if (mTargetStack == null) {
mTargetStack = sourceStack;
} else if (mTargetStack != sourceStack) {
- sourceTask.reparent(mTargetStack.mStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
- !ANIMATE, DEFER_RESUME, "launchToSide");
+ sourceTask.reparent(mTargetStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+ DEFER_RESUME, "launchToSide");
}
final TaskRecord topTask = mTargetStack.topTask();
@@ -1967,18 +1964,15 @@
}
if (mLaunchBounds != null) {
- mInTask.updateOverrideConfiguration(mLaunchBounds);
// TODO: Shouldn't we already know what stack to use by the time we get here?
- int stackId = mSupervisor.getLaunchStackId(null, null, mInTask);
- if (stackId != mInTask.getStackId()) {
- mInTask.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
+ ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
+ if (stack != mInTask.getStack()) {
+ mInTask.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
DEFER_RESUME, "inTaskToFront");
- stackId = mInTask.getStackId();
mTargetStack = mInTask.getStack();
}
- if (StackId.resizeStackWithLaunchBounds(stackId)) {
- mService.resizeStack(stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
- }
+
+ updateBounds(mInTask, mLaunchBounds);
}
mTargetStack.moveTaskToFrontLocked(
@@ -1991,6 +1985,19 @@
return START_SUCCESS;
}
+ void updateBounds(TaskRecord task, Rect bounds) {
+ if (bounds == null) {
+ return;
+ }
+
+ final int stackId = task.getStackId();
+ if (StackId.resizeStackWithLaunchBounds(stackId)) {
+ mService.resizeStack(stackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
+ } else {
+ task.updateOverrideConfiguration(bounds);
+ }
+ }
+
private void setTaskToCurrentTopOrCreateNewTask() {
mTargetStack = computeStackFocus(mStartActivity, false, null /* bounds */, mLaunchFlags,
mOptions);
@@ -2084,20 +2091,21 @@
return mSupervisor.mFocusedStack;
}
- if (mSourceDisplayId != DEFAULT_DISPLAY) {
+ if (mPreferredDisplayId != DEFAULT_DISPLAY) {
// Try to put the activity in a stack on a secondary display.
- stack = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r);
+ stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r);
if (stack == null) {
// If source display is not suitable - look for topmost valid stack in the system.
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Can't launch on mSourceDisplayId=" + mSourceDisplayId
- + ", looking on all displays.");
- stack = mSupervisor.getNextValidLaunchStackLocked(r, mSourceDisplayId);
+ "computeStackFocus: Can't launch on mPreferredDisplayId="
+ + mPreferredDisplayId + ", looking on all displays.");
+ stack = mSupervisor.getNextValidLaunchStackLocked(r, mPreferredDisplayId);
}
}
if (stack == null) {
// We first try to put the task in the first dynamic stack on home display.
- final ArrayList<ActivityStack> homeDisplayStacks = mSupervisor.mHomeStack.mStacks;
+ final ArrayList<ActivityStack> homeDisplayStacks =
+ mSupervisor.getStacksOnDefaultDisplay();
for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
stack = homeDisplayStacks.get(stackNdx);
if (isDynamicStack(stack.mStackId)) {
@@ -2107,11 +2115,7 @@
}
}
// If there is no suitable dynamic stack then we figure out which static stack to use.
- final int stackId = task != null ? mSupervisor.getLaunchStackId(r, aOptions, task)
- // TODO: This should go in mSupervisor.getLaunchStackId method...
- : bounds != null && mService.mSupportsFreeformWindowManagement
- ? FREEFORM_WORKSPACE_STACK_ID : FULLSCREEN_WORKSPACE_STACK_ID;
- stack = mSupervisor.getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
+ stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
}
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+ r + " stackId=" + stack.mStackId);
@@ -2119,37 +2123,40 @@
}
/** Check if provided activity record can launch in currently focused stack. */
+ // TODO: This method can probably be consolidated into getLaunchStack() below.
private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
final ActivityStack focusedStack = mSupervisor.mFocusedStack;
- final int focusedStackId = mSupervisor.mFocusedStack.mStackId;
final boolean canUseFocusedStack;
- switch (focusedStackId) {
- case FULLSCREEN_WORKSPACE_STACK_ID:
- // The fullscreen stack can contain any task regardless of if the task is resizeable
- // or not. So, we let the task go in the fullscreen task if it is the focus stack.
- canUseFocusedStack = true;
- break;
- case ASSISTANT_STACK_ID:
- canUseFocusedStack = r.isActivityTypeAssistant();
- break;
- case DOCKED_STACK_ID:
- // Any activity which supports split screen can go in the docked stack.
- canUseFocusedStack = r.supportsSplitScreen();
- break;
- case FREEFORM_WORKSPACE_STACK_ID:
- // Any activity which supports freeform can go in the freeform stack.
- canUseFocusedStack = r.supportsFreeform();
- break;
- default:
- // Dynamic stacks behave similarly to the fullscreen stack and can contain any
- // resizeable task.
- canUseFocusedStack = isDynamicStack(focusedStackId)
- && r.canBeLaunchedOnDisplay(focusedStack.mDisplayId);
+ final int focusedStackId = mSupervisor.mFocusedStack.mStackId;
+ if (focusedStack.isActivityTypeAssistant()) {
+ canUseFocusedStack = r.isActivityTypeAssistant();
+ } else {
+ switch (focusedStack.getWindowingMode()) {
+ case WINDOWING_MODE_FULLSCREEN:
+ // The fullscreen stack can contain any task regardless of if the task is
+ // resizeable or not. So, we let the task go in the fullscreen task if it is the
+ // focus stack.
+ canUseFocusedStack = true;
+ break;
+ case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
+ case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
+ // Any activity which supports split screen can go in the docked stack.
+ canUseFocusedStack = r.supportsSplitScreenWindowingMode();
+ break;
+ case WINDOWING_MODE_FREEFORM:
+ // Any activity which supports freeform can go in the freeform stack.
+ canUseFocusedStack = r.supportsFreeform();
+ break;
+ default:
+ // Dynamic stacks behave similarly to the fullscreen stack and can contain any
+ // resizeable task.
+ canUseFocusedStack = isDynamicStack(focusedStackId)
+ && r.canBeLaunchedOnDisplay(focusedStack.mDisplayId);
+ }
}
-
return canUseFocusedStack && !newTask
- // We strongly prefer to launch activities on the same display as their source.
- && (mSourceDisplayId == focusedStack.mDisplayId);
+ // Using the focus stack isn't important enough to override the preferred display.
+ && (mPreferredDisplayId == focusedStack.mDisplayId);
}
private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, TaskRecord task,
@@ -2159,54 +2166,16 @@
return mReuseTask.getStack();
}
- // If the activity is of a specific type, return the associated stack, creating it if
- // necessary
- if (r.isActivityTypeHome()) {
- return mSupervisor.mHomeStack;
- }
- if (r.isActivityTypeRecents()) {
- return mSupervisor.getStack(RECENTS_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
- }
- if (r.isActivityTypeAssistant()) {
- return mSupervisor.getStack(ASSISTANT_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
- }
+ final int vrDisplayId = mUsingVr2dDisplay ? mPreferredDisplayId : INVALID_DISPLAY;
+ final ActivityStack launchStack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP,
+ vrDisplayId);
- int launchDisplayId = INVALID_DISPLAY;
- int launchStackId = INVALID_STACK_ID;
- if (aOptions != null) {
- launchDisplayId = aOptions.getLaunchDisplayId();
- final int vrDisplayId = mUsingVr2dDisplay ? mSourceDisplayId : INVALID_DISPLAY;
- launchStackId = mSupervisor.getLaunchStackId(r, aOptions, task, vrDisplayId);
- }
-
- // TODO: Will no longer be needed once we are on longer using static stack ids.
- if (mSupervisor.isValidLaunchStackId(launchStackId, launchDisplayId, r)) {
- return mSupervisor.getStack(launchStackId, CREATE_IF_NEEDED, ON_TOP);
- }
- if (launchStackId == DOCKED_STACK_ID) {
- // The preferred launch stack is the docked stack, but it isn't a valid launch stack
- // for this activity, so we put the activity in the fullscreen stack.
- return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
- }
- // TODO: Can probably be removed since ASS.getLaunchStackId() does display resolution.
- if (launchDisplayId != INVALID_DISPLAY) {
- // Stack id has higher priority than display id.
- return mSupervisor.getValidLaunchStackOnDisplay(launchDisplayId, r);
- }
-
- // If we are using Vr2d display, find the virtual display stack.
- // TODO: Can be removed.
- if (mUsingVr2dDisplay) {
- ActivityStack as = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r);
- if (DEBUG_STACK) {
- Slog.v(TAG, "Launch stack for app: " + r.toString() +
- ", on virtual display stack:" + as.toString());
- }
- return as;
+ if (launchStack != null) {
+ return launchStack;
}
if (((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0)
- || mSourceDisplayId != DEFAULT_DISPLAY) {
+ || mPreferredDisplayId != DEFAULT_DISPLAY) {
return null;
}
// Otherwise handle adjacent launch.
@@ -2228,15 +2197,16 @@
if (parentStack != null && parentStack.isDockedStack()) {
// If parent was in docked stack, the natural place to launch another activity
// will be fullscreen, so it can appear alongside the docked window.
- return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED,
- ON_TOP);
+ final int activityType = mSupervisor.resolveActivityType(r, mOptions, task);
+ return parentStack.getDisplay().getOrCreateStack(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP);
} else {
// If the parent is not in the docked stack, we check if there is docked window
// and if yes, we will launch into that stack. If not, we just put the new
// activity into parent's stack, because we can't find a better place.
- final ActivityStack dockedStack = mSupervisor.getStack(DOCKED_STACK_ID);
- if (dockedStack != null
- && dockedStack.shouldBeVisible(r) == STACK_INVISIBLE) {
+ final ActivityStack dockedStack = mSupervisor.getDefaultDisplay().getStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
+ if (dockedStack != null && !dockedStack.shouldBeVisible(r)) {
// There is a docked stack, but it isn't visible, so we can't launch into that.
return null;
} else {
@@ -2259,15 +2229,6 @@
mWindowManager = wm;
}
- void removePendingActivityLaunchesLocked(ActivityStack stack) {
- for (int palNdx = mPendingActivityLaunches.size() - 1; palNdx >= 0; --palNdx) {
- PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
- if (pal.stack == stack) {
- mPendingActivityLaunches.remove(palNdx);
- }
- }
- }
-
static boolean isDocumentLaunchesIntoExisting(int flags) {
return (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
(flags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0;
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 440b3d3b..fe38097 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -507,7 +507,7 @@
// launching the report UI under a different user.
app.errorReportReceiver = null;
- for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) {
+ for (int userId : mService.mUserController.getCurrentProfileIds()) {
if (app.userId == userId) {
app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
mContext, app.info.packageName, app.info.flags);
@@ -728,7 +728,7 @@
boolean isBackground = (UserHandle.getAppId(proc.uid)
>= Process.FIRST_APPLICATION_UID
&& proc.pid != MY_PID);
- for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) {
+ for (int userId : mService.mUserController.getCurrentProfileIds()) {
isBackground &= (proc.userId != userId);
}
if (isBackground && !showBackground) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3105e37..e839003 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -300,10 +300,10 @@
// TODO: remove this once we figure out properly where and how
// PROCESS_EVENT = 1112
- // EVENT SUBTYPE: START = 1
- // KEY_NAME: 1
+ // KEY_STATE = 1
+ // KEY_PACKAGE_NAME: 1002
// KEY_UID: 2
- StatsLog.writeArray(1112, 1, 1, name, 2, uid);
+ StatsLog.writeArray(1112, 1, 1, 1002, name, 2, uid);
}
}
@@ -313,10 +313,10 @@
// TODO: remove this once we figure out properly where and how
// PROCESS_EVENT = 1112
- // EVENT SUBTYPE: CRASH = 2
- // KEY_NAME: 1
+ // KEY_STATE = 1
+ // KEY_PACKAGE_NAME: 1002
// KEY_UID: 2
- StatsLog.writeArray(1112, 2, 1, name, 2, uid);
+ StatsLog.writeArray(1112, 1, 2, 1002, name, 2, uid);
}
}
@@ -550,10 +550,10 @@
synchronized (mStats) {
mStats.noteScreenStateLocked(state);
// TODO: remove this once we figure out properly where and how
- // SCREEN_EVENT = 1003
- // State key: 1
+ // SCREEN_EVENT = 2
+ // KEY_STATE: 1
// State value: state. We can change this to our own def later.
- StatsLog.writeArray(1003, 1, state);
+ StatsLog.writeArray(2, 1, state);
}
if (DBG) Slog.d(TAG, "end noteScreenState");
}
@@ -564,14 +564,14 @@
mStats.noteScreenBrightnessLocked(brightness);
}
}
-
+
public void noteUserActivity(int uid, int event) {
enforceCallingPermission();
synchronized (mStats) {
mStats.noteUserActivityLocked(uid, event);
}
}
-
+
public void noteWakeUp(String reason, int reasonUid) {
enforceCallingPermission();
synchronized (mStats) {
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index e03c530..5c48f90 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -342,8 +342,12 @@
// show on top of the lock screen. In this can we want to dismiss the docked
// stack since it will be complicated/risky to try to put the activity on top
// of the lock screen in the right fullscreen configuration.
- mStackSupervisor.moveTasksToFullscreenStackLocked(DOCKED_STACK_ID,
- mStackSupervisor.mFocusedStack.getStackId() == DOCKED_STACK_ID);
+ final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenStack();
+ if (stack == null) {
+ return;
+ }
+ mStackSupervisor.moveTasksToFullscreenStackLocked(stack,
+ mStackSupervisor.mFocusedStack == stack);
}
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index ee59386..7930f53 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -308,7 +308,7 @@
boolean sendFinish = finishedReceiver != null;
int userId = key.userId;
if (userId == UserHandle.USER_CURRENT) {
- userId = owner.mUserController.getCurrentOrTargetUserIdLocked();
+ userId = owner.mUserController.getCurrentOrTargetUserId();
}
int res = 0;
switch (key.type) {
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index a601ee1..2726979 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -16,6 +16,9 @@
package com.android.server.am;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
import android.app.RemoteAction;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -32,15 +35,16 @@
class PinnedActivityStack extends ActivityStack<PinnedStackWindowController>
implements PinnedStackWindowListener {
- PinnedActivityStack(ActivityStackSupervisor.ActivityDisplay display, int stackId,
- ActivityStackSupervisor supervisor, RecentTasks recentTasks, boolean onTop) {
- super(display, stackId, supervisor, recentTasks, onTop);
+ PinnedActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
+ boolean onTop) {
+ super(display, stackId, supervisor, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, onTop);
}
@Override
PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
Rect outBounds) {
- return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds);
+ return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds,
+ mStackSupervisor.mWindowManager);
}
Rect getDefaultPictureInPictureBounds(float aspectRatio) {
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 39aed7c..effb86c 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -23,11 +23,14 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.service.procstats.ProcessStatsProto;
+import android.service.procstats.ProcessStatsServiceDumpProto;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.DumpUtils;
@@ -622,13 +625,17 @@
long ident = Binder.clearCallingIdentity();
try {
- dumpInner(fd, pw, args);
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ dumpProto(fd);
+ } else {
+ dumpInner(pw, args);
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- private void dumpInner(FileDescriptor fd, PrintWriter pw, String[] args) {
+ private void dumpInner(PrintWriter pw, String[] args) {
final long now = SystemClock.uptimeMillis();
boolean isCheckin = false;
@@ -1038,4 +1045,44 @@
}
}
}
+
+ private void dumpAggregatedStats(ProtoOutputStream proto, int aggregateHours, long now) {
+ ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000
+ - (ProcessStats.COMMIT_PERIOD/2));
+ if (pfd == null) {
+ return;
+ }
+ ProcessStats stats = new ProcessStats(false);
+ InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ stats.read(stream);
+ if (stats.mReadError != null) {
+ return;
+ }
+ stats.toProto(proto, now);
+ }
+
+ private void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+ // dump current procstats
+ long nowToken = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_NOW);
+ long now;
+ synchronized (mAm) {
+ now = SystemClock.uptimeMillis();
+ mProcessStats.toProto(proto, now);
+ }
+ proto.end(nowToken);
+
+ // aggregated over last 3 hours procstats
+ long tokenOf3Hrs = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_OVER_3HRS);
+ dumpAggregatedStats(proto, 3, now);
+ proto.end(tokenOf3Hrs);
+
+ // aggregated over last 24 hours procstats
+ long tokenOf24Hrs = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_OVER_24HRS);
+ dumpAggregatedStats(proto, 24, now);
+ proto.end(tokenOf24Hrs);
+
+ proto.flush();
+ }
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 027dc08..ac85e6b 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -517,11 +517,14 @@
} catch (PackageManager.NameNotFoundException e) {
}
}
- if (localForegroundNoti.getSmallIcon() == null) {
+ if (localForegroundNoti.getSmallIcon() == null
+ || nm.getNotificationChannel(localPackageName, appUid,
+ localForegroundNoti.getChannelId()) == null) {
// Notifications whose icon is 0 are defined to not show
// a notification, silently ignoring it. We don't want to
// just ignore it, we want to prevent the service from
// being foreground.
+ // Also every notification needs a channel.
throw new RuntimeException("invalid service notification: "
+ foregroundNoti);
}
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
index 8297169..6a986bb 100644
--- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -95,7 +95,7 @@
};
private final TaskStackConsumer mNotifyActivityPinned = (l, m) -> {
- l.onActivityPinned((String) m.obj, m.arg1);
+ l.onActivityPinned((String) m.obj, m.arg1, m.arg2);
};
private final TaskStackConsumer mNotifyActivityUnpinned = (l, m) -> {
@@ -278,10 +278,10 @@
}
/** Notifies all listeners when an Activity is pinned. */
- void notifyActivityPinned(String packageName, int taskId) {
+ void notifyActivityPinned(String packageName, int userId, int taskId) {
mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG,
- taskId, 0, packageName);
+ userId, taskId, packageName);
forAllLocalListeners(mNotifyActivityPinned, msg);
msg.sendToTarget();
}
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index f6e20cd..61994b5 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -54,9 +54,6 @@
import java.util.Comparator;
import java.util.List;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
public class TaskPersister {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index fb8b034..0d8df79 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -18,20 +18,19 @@
import static android.app.ActivityManager.RESIZE_MODE_FORCED;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
-import static android.app.ActivityManager.StackId.getWindowingModeForStackId;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
@@ -573,36 +572,36 @@
/**
* Convenience method to reparent a task to the top or bottom position of the stack.
*/
- boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode,
- boolean animate, boolean deferResume, String reason) {
- return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate,
- deferResume, true /* schedulePictureInPictureModeChange */, reason);
+ boolean reparent(ActivityStack preferredStack, boolean toTop,
+ @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ String reason) {
+ return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate, deferResume,
+ true /* schedulePictureInPictureModeChange */, reason);
}
/**
* Convenience method to reparent a task to the top or bottom position of the stack, with
* an option to skip scheduling the picture-in-picture mode change.
*/
- boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode,
- boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange,
- String reason) {
- return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate,
+ boolean reparent(ActivityStack preferredStack, boolean toTop,
+ @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ boolean schedulePictureInPictureModeChange, String reason) {
+ return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate,
deferResume, schedulePictureInPictureModeChange, reason);
}
- /**
- * Convenience method to reparent a task to a specific position of the stack.
- */
- boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode,
- boolean animate, boolean deferResume, String reason) {
- return reparent(preferredStackId, position, moveStackMode, animate, deferResume,
+ /** Convenience method to reparent a task to a specific position of the stack. */
+ boolean reparent(ActivityStack preferredStack, int position,
+ @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ String reason) {
+ return reparent(preferredStack, position, moveStackMode, animate, deferResume,
true /* schedulePictureInPictureModeChange */, reason);
}
/**
* Reparents the task into a preferred stack, creating it if necessary.
*
- * @param preferredStackId the stack id of the target stack to move this task
+ * @param preferredStack the target stack to move this task
* @param position the position to place this task in the new stack
* @param animate whether or not we should wait for the new window created as a part of the
* reparenting to be drawn and animated in
@@ -616,13 +615,16 @@
* @param reason the caller of this reparenting
* @return whether the task was reparented
*/
- boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode,
- boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange,
- String reason) {
+ // TODO: Inspect all call sites and change to just changing windowing mode of the stack vs.
+ // re-parenting the task. Can only be done when we are no longer using static stack Ids like
+ /** {@link ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} */
+ boolean reparent(ActivityStack preferredStack, int position,
+ @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ boolean schedulePictureInPictureModeChange, String reason) {
final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
final WindowManagerService windowManager = mService.mWindowManager;
final ActivityStack sourceStack = getStack();
- final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStackId,
+ final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStack,
position == MAX_VALUE);
if (toStack == sourceStack) {
return false;
@@ -705,19 +707,22 @@
toStack.prepareFreezingTaskBounds();
// Make sure the task has the appropriate bounds/size for the stack it is in.
+ final int toStackWindowingMode = toStack.getWindowingMode();
+ final boolean toStackSplitScreenPrimary =
+ toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
if (stackId == FULLSCREEN_WORKSPACE_STACK_ID
&& !Objects.equals(mBounds, toStack.mBounds)) {
kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
deferResume);
- } else if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
+ } else if (toStackWindowingMode == WINDOWING_MODE_FREEFORM) {
Rect bounds = getLaunchBounds();
if (bounds == null) {
toStack.layoutTaskInStack(this, null);
bounds = mBounds;
}
kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
- } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
- if (stackId == DOCKED_STACK_ID && moveStackMode == REPARENT_KEEP_STACK_AT_FRONT) {
+ } else if (toStackSplitScreenPrimary || toStackWindowingMode == WINDOWING_MODE_PINNED) {
+ if (toStackSplitScreenPrimary && moveStackMode == REPARENT_KEEP_STACK_AT_FRONT) {
// Move recents to front so it is not behind home stack when going into docked
// mode
mService.mStackSupervisor.moveRecentsStackToFront(reason);
@@ -744,11 +749,12 @@
}
// TODO: Handle incorrect request to move before the actual move, not after.
- supervisor.handleNonResizableTaskIfNeeded(this, getWindowingModeForStackId(preferredStackId,
- supervisor.getStack(DOCKED_STACK_ID) != null), DEFAULT_DISPLAY, stackId);
+ final boolean inSplitScreenMode = supervisor.getDefaultDisplay().hasSplitScreenStack();
+ supervisor.handleNonResizableTaskIfNeeded(this, preferredStack.getWindowingMode(),
+ DEFAULT_DISPLAY, stackId);
- boolean successful = (preferredStackId == stackId);
- if (successful && stackId == DOCKED_STACK_ID) {
+ boolean successful = (preferredStack == toStack);
+ if (successful && toStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// If task moved to docked stack - show recents if needed.
mService.mWindowManager.showRecentApps(false /* fromHome */);
}
@@ -941,8 +947,8 @@
mNextAffiliateTaskId = nextAffiliate == null ? INVALID_TASK_ID : nextAffiliate.taskId;
}
- ActivityStack getStack() {
- return mStack;
+ <T extends ActivityStack> T getStack() {
+ return (T) mStack;
}
/**
@@ -1457,10 +1463,12 @@
return isResizeable(true /* checkSupportsPip */);
}
- boolean supportsSplitScreen() {
+ @Override
+ public boolean supportsSplitScreenWindowingMode() {
// A task can not be docked even if it is considered resizeable because it only supports
// picture-in-picture mode but has a non-resizeable resizeMode
- return mService.mSupportsSplitScreenMultiWindow
+ return super.supportsSplitScreenWindowingMode()
+ && mService.mSupportsSplitScreenMultiWindow
&& (mService.mForceResizableActivities
|| (isResizeable(false /* checkSupportsPip */)
&& !ActivityInfo.isPreserveOrientationMode(mResizeMode)));
@@ -2101,12 +2109,10 @@
return null;
}
- final int stackId = mStack.mStackId;
- if (stackId == HOME_STACK_ID
- || stackId == RECENTS_STACK_ID
- || stackId == ASSISTANT_STACK_ID
- || stackId == FULLSCREEN_WORKSPACE_STACK_ID
- || (stackId == DOCKED_STACK_ID && !isResizeable())) {
+ final int windowingMode = getWindowingMode();
+ if (!isActivityTypeStandardOrUndefined()
+ || windowingMode == WINDOWING_MODE_FULLSCREEN
+ || (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && !isResizeable())) {
return isResizeable() ? mStack.mBounds : null;
} else if (!getWindowConfiguration().persistTaskBounds()) {
return mStack.mBounds;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index f2e2942..055c9f6 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -103,11 +103,30 @@
/**
* Helper class for {@link ActivityManagerService} responsible for multi-user functionality.
+ *
+ * <p>This class use {@link #mLock} to synchronize access to internal state. Methods that require
+ * {@link #mLock} to be held should have "LU" suffix in the name.
+ *
+ * <p><strong>Important:</strong> Synchronized code, i.e. one executed inside a synchronized(mLock)
+ * block or inside LU method, should only access internal state of this class or make calls to
+ * other LU methods. Non-LU method calls or calls to external classes are discouraged as they
+ * may cause lock inversion.
*/
class UserController implements Handler.Callback {
private static final String TAG = TAG_WITH_CLASS_NAME ? "UserController" : TAG_AM;
- // Maximum number of users we allow to be running at a time.
+ /**
+ * Maximum number of users we allow to be running at a time, including the system user and
+ * its profiles.
+ * Note changing this to 2 is not recommended, since that would mean, if the user uses
+ * work profile and then switch to a secondary user, then the work profile user would be killed,
+ * which should work fine, but aggressively killing the work profile user that has just been
+ * running could cause data loss. (Even without work profile, witching from secondary user A
+ * to secondary user B would cause similar issues on user B.)
+ *
+ * TODO: Consider adding or replacing with "MAX_RUNNING_*SECONDARY*_USERS", which is the max
+ * number of running *secondary, switchable* users.
+ */
static final int MAX_RUNNING_USERS = 3;
// Amount of time we wait for observers to handle a user switch before
@@ -136,7 +155,9 @@
// when it never calls back.
private static final int USER_SWITCH_CALLBACKS_TIMEOUT_MS = 5 * 1000;
- private final Object mLock;
+ // Lock for internal state.
+ private final Object mLock = new Object();
+
private final Injector mInjector;
private final Handler mHandler;
private final Handler mUiHandler;
@@ -175,7 +196,8 @@
/**
* Mapping from each known user ID to the profile group ID it is associated with.
*/
- private final SparseIntArray mUserProfileGroupIdsSelfLocked = new SparseIntArray();
+ @GuardedBy("mLock")
+ private final SparseIntArray mUserProfileGroupIds = new SparseIntArray();
/**
* Registered observers of the user switching mechanics.
@@ -206,7 +228,6 @@
@VisibleForTesting
UserController(Injector injector) {
mInjector = injector;
- mLock = injector.getLock();
mHandler = mInjector.getHandler(this);
mUiHandler = mInjector.getUiHandler(this);
// User 0 is the first and only user that runs at boot.
@@ -214,19 +235,18 @@
mStartedUsers.put(UserHandle.USER_SYSTEM, uss);
mUserLru.add(UserHandle.USER_SYSTEM);
mLockPatternUtils = mInjector.getLockPatternUtils();
- updateStartedUserArrayLocked();
+ updateStartedUserArrayLU();
}
void finishUserSwitch(UserState uss) {
+ finishUserBoot(uss);
+ startProfiles();
synchronized (mLock) {
- finishUserBoot(uss);
-
- startProfilesLocked();
- stopRunningUsersLocked(MAX_RUNNING_USERS);
+ stopRunningUsersLU(MAX_RUNNING_USERS);
}
}
- void stopRunningUsersLocked(int maxRunningUsers) {
+ void stopRunningUsersLU(int maxRunningUsers) {
int num = mUserLru.size();
int i = 0;
while (num > maxRunningUsers && i < mUserLru.size()) {
@@ -255,7 +275,7 @@
continue;
}
// This is a user to be stopped.
- if (stopUsersLocked(oldUserId, false, null) != USER_OP_SUCCESS) {
+ if (stopUsersLU(oldUserId, false, null) != USER_OP_SUCCESS) {
num--;
}
num--;
@@ -273,55 +293,57 @@
Slog.d(TAG, "Finishing user boot " + userId);
synchronized (mLock) {
// Bail if we ended up with a stale user
- if (mStartedUsers.get(userId) != uss) return;
+ if (mStartedUsers.get(userId) != uss) {
+ return;
+ }
+ }
- // We always walk through all the user lifecycle states to send
- // consistent developer events. We step into RUNNING_LOCKED here,
- // but we might immediately step into RUNNING below if the user
- // storage is already unlocked.
- if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) {
- mInjector.getUserManagerInternal().setUserState(userId, uss.state);
- // Do not report secondary users, runtime restarts or first boot/upgrade
- if (userId == UserHandle.USER_SYSTEM
- && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
- int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000);
- MetricsLogger.histogram(mInjector.getContext(),
- "framework_locked_boot_completed", uptimeSeconds);
- final int MAX_UPTIME_SECONDS = 120;
- if (uptimeSeconds > MAX_UPTIME_SECONDS) {
- Slog.wtf("SystemServerTiming",
- "finishUserBoot took too long. uptimeSeconds=" + uptimeSeconds);
- }
+ // We always walk through all the user lifecycle states to send
+ // consistent developer events. We step into RUNNING_LOCKED here,
+ // but we might immediately step into RUNNING below if the user
+ // storage is already unlocked.
+ if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) {
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ // Do not report secondary users, runtime restarts or first boot/upgrade
+ if (userId == UserHandle.USER_SYSTEM
+ && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
+ int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000);
+ MetricsLogger.histogram(mInjector.getContext(),
+ "framework_locked_boot_completed", uptimeSeconds);
+ final int MAX_UPTIME_SECONDS = 120;
+ if (uptimeSeconds > MAX_UPTIME_SECONDS) {
+ Slog.wtf("SystemServerTiming",
+ "finishUserBoot took too long. uptimeSeconds=" + uptimeSeconds);
}
-
- mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
- userId, 0));
- Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mInjector.broadcastIntentLocked(intent, null, resultTo, 0, null, null,
- new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
- AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
}
- // We need to delay unlocking managed profiles until the parent user
- // is also unlocked.
- if (mInjector.getUserManager().isManagedProfile(userId)) {
- final UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
- if (parent != null
- && isUserRunningLocked(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) {
- Slog.d(TAG, "User " + userId + " (parent " + parent.id
- + "): attempting unlock because parent is unlocked");
- maybeUnlockUser(userId);
- } else {
- String parentId = (parent == null) ? "<null>" : String.valueOf(parent.id);
- Slog.d(TAG, "User " + userId + " (parent " + parentId
- + "): delaying unlock because parent is locked");
- }
- } else {
+ mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
+ userId, 0));
+ Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
+ new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
+ AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
+ }
+
+ // We need to delay unlocking managed profiles until the parent user
+ // is also unlocked.
+ if (mInjector.getUserManager().isManagedProfile(userId)) {
+ final UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
+ if (parent != null
+ && isUserRunning(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) {
+ Slog.d(TAG, "User " + userId + " (parent " + parent.id
+ + "): attempting unlock because parent is unlocked");
maybeUnlockUser(userId);
+ } else {
+ String parentId = (parent == null) ? "<null>" : String.valueOf(parent.id);
+ Slog.d(TAG, "User " + userId + " (parent " + parentId
+ + "): delaying unlock because parent is locked");
}
+ } else {
+ maybeUnlockUser(userId);
}
}
@@ -331,34 +353,30 @@
*/
private void finishUserUnlocking(final UserState uss) {
final int userId = uss.mHandle.getIdentifier();
- boolean proceedWithUnlock = false;
+ // Only keep marching forward if user is actually unlocked
+ if (!StorageManager.isUserKeyUnlocked(userId)) return;
synchronized (mLock) {
// Bail if we ended up with a stale user
if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
- // Only keep marching forward if user is actually unlocked
- if (!StorageManager.isUserKeyUnlocked(userId)) return;
-
- if (uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
- mInjector.getUserManagerInternal().setUserState(userId, uss.state);
- proceedWithUnlock = true;
+ // Do not proceed if unexpected state
+ if (!uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
+ return;
}
}
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ uss.mUnlockProgress.start();
- if (proceedWithUnlock) {
- uss.mUnlockProgress.start();
+ // Prepare app storage before we go any further
+ uss.mUnlockProgress.setProgress(5,
+ mInjector.getContext().getString(R.string.android_start_title));
+ mInjector.getUserManager().onBeforeUnlockUser(userId);
+ uss.mUnlockProgress.setProgress(20);
- // Prepare app storage before we go any further
- uss.mUnlockProgress.setProgress(5,
- mInjector.getContext().getString(R.string.android_start_title));
- mInjector.getUserManager().onBeforeUnlockUser(userId);
- uss.mUnlockProgress.setProgress(20);
-
- // Dispatch unlocked to system services; when fully dispatched,
- // that calls through to the next "unlocked" phase
- mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
- .sendToTarget();
- }
+ // Dispatch unlocked to system services; when fully dispatched,
+ // that calls through to the next "unlocked" phase
+ mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
+ .sendToTarget();
}
/**
@@ -367,64 +385,64 @@
*/
void finishUserUnlocked(final UserState uss) {
final int userId = uss.mHandle.getIdentifier();
+ // Only keep marching forward if user is actually unlocked
+ if (!StorageManager.isUserKeyUnlocked(userId)) return;
synchronized (mLock) {
// Bail if we ended up with a stale user
if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
- // Only keep marching forward if user is actually unlocked
- if (!StorageManager.isUserKeyUnlocked(userId)) return;
-
- if (uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) {
- mInjector.getUserManagerInternal().setUserState(userId, uss.state);
- uss.mUnlockProgress.finish();
-
- // Dispatch unlocked to external apps
- final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
- unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- unlockedIntent.addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
- mInjector.broadcastIntentLocked(unlockedIntent, null, null, 0, null,
- null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
- userId);
-
- if (getUserInfo(userId).isManagedProfile()) {
- UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
- if (parent != null) {
- final Intent profileUnlockedIntent = new Intent(
- Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
- profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
- profileUnlockedIntent.addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- mInjector.broadcastIntentLocked(profileUnlockedIntent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, SYSTEM_UID,
- parent.id);
- }
- }
-
- // Send PRE_BOOT broadcasts if user fingerprint changed; we
- // purposefully block sending BOOT_COMPLETED until after all
- // PRE_BOOT receivers are finished to avoid ANR'ing apps
- final UserInfo info = getUserInfo(userId);
- if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) {
- // Suppress double notifications for managed profiles that
- // were unlocked automatically as part of their parent user
- // being unlocked.
- final boolean quiet;
- if (info.isManagedProfile()) {
- quiet = !uss.tokenProvided
- || !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
- } else {
- quiet = false;
- }
- mInjector.sendPreBootBroadcast(userId, quiet,
- () -> finishUserUnlockedCompleted(uss));
- } else {
- finishUserUnlockedCompleted(uss);
- }
+ // Do not proceed if unexpected state
+ if (!uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) {
+ return;
}
}
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ uss.mUnlockProgress.finish();
+ // Dispatch unlocked to external apps
+ final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
+ unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ unlockedIntent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+ mInjector.broadcastIntent(unlockedIntent, null, null, 0, null,
+ null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ userId);
+
+ if (getUserInfo(userId).isManagedProfile()) {
+ UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
+ if (parent != null) {
+ final Intent profileUnlockedIntent = new Intent(
+ Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+ profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
+ profileUnlockedIntent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ mInjector.broadcastIntent(profileUnlockedIntent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, MY_PID, SYSTEM_UID,
+ parent.id);
+ }
+ }
+
+ // Send PRE_BOOT broadcasts if user fingerprint changed; we
+ // purposefully block sending BOOT_COMPLETED until after all
+ // PRE_BOOT receivers are finished to avoid ANR'ing apps
+ final UserInfo info = getUserInfo(userId);
+ if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) {
+ // Suppress double notifications for managed profiles that
+ // were unlocked automatically as part of their parent user
+ // being unlocked.
+ final boolean quiet;
+ if (info.isManagedProfile()) {
+ quiet = !uss.tokenProvided
+ || !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
+ } else {
+ quiet = false;
+ }
+ mInjector.sendPreBootBroadcast(userId, quiet,
+ () -> finishUserUnlockedCompleted(uss));
+ } else {
+ finishUserUnlockedCompleted(uss);
+ }
}
private void finishUserUnlockedCompleted(UserState uss) {
@@ -432,60 +450,59 @@
synchronized (mLock) {
// Bail if we ended up with a stale user
if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
- final UserInfo userInfo = getUserInfo(userId);
- if (userInfo == null) {
- return;
- }
-
- // Only keep marching forward if user is actually unlocked
- if (!StorageManager.isUserKeyUnlocked(userId)) return;
-
- // Remember that we logged in
- mInjector.getUserManager().onUserLoggedIn(userId);
-
- if (!userInfo.isInitialized()) {
- if (userId != UserHandle.USER_SYSTEM) {
- Slog.d(TAG, "Initializing user #" + userId);
- Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mInjector.broadcastIntentLocked(intent, null,
- new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser) {
- // Note: performReceive is called with mService lock held
- mInjector.getUserManager().makeInitialized(userInfo.id);
- }
- }, 0, null, null, null, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, SYSTEM_UID, userId);
- }
- }
-
- Slog.i(TAG, "Sending BOOT_COMPLETE user #" + userId);
- // Do not report secondary users, runtime restarts or first boot/upgrade
- if (userId == UserHandle.USER_SYSTEM
- && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
- int uptimeSeconds = (int) (SystemClock.elapsedRealtime() / 1000);
- MetricsLogger.histogram(mInjector.getContext(), "framework_boot_completed",
- uptimeSeconds);
- }
- final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
- bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mInjector.broadcastIntentLocked(bootIntent, null, new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered, boolean sticky, int sendingUser)
- throws RemoteException {
- Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u" + userId);
- }
- }, 0, null, null,
- new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
- AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
}
+ UserInfo userInfo = getUserInfo(userId);
+ if (userInfo == null) {
+ return;
+ }
+ // Only keep marching forward if user is actually unlocked
+ if (!StorageManager.isUserKeyUnlocked(userId)) return;
+
+ // Remember that we logged in
+ mInjector.getUserManager().onUserLoggedIn(userId);
+
+ if (!userInfo.isInitialized()) {
+ if (userId != UserHandle.USER_SYSTEM) {
+ Slog.d(TAG, "Initializing user #" + userId);
+ Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mInjector.broadcastIntent(intent, null,
+ new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ // Note: performReceive is called with mService lock held
+ mInjector.getUserManager().makeInitialized(userInfo.id);
+ }
+ }, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, true, false, MY_PID, SYSTEM_UID, userId);
+ }
+ }
+
+ Slog.i(TAG, "Sending BOOT_COMPLETE user #" + userId);
+ // Do not report secondary users, runtime restarts or first boot/upgrade
+ if (userId == UserHandle.USER_SYSTEM
+ && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
+ int uptimeSeconds = (int) (SystemClock.elapsedRealtime() / 1000);
+ MetricsLogger.histogram(mInjector.getContext(), "framework_boot_completed",
+ uptimeSeconds);
+ }
+ final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
+ bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mInjector.broadcastIntent(bootIntent, null, new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser)
+ throws RemoteException {
+ Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u" + userId);
+ }
+ }, 0, null, null,
+ new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
+ AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
}
int restartUser(final int userId, final boolean foreground) {
@@ -516,33 +533,33 @@
}
enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
synchronized (mLock) {
- return stopUsersLocked(userId, force, callback);
+ return stopUsersLU(userId, force, callback);
}
}
/**
* Stops the user along with its related users. The method calls
- * {@link #getUsersToStopLocked(int)} to determine the list of users that should be stopped.
+ * {@link #getUsersToStopLU(int)} to determine the list of users that should be stopped.
*/
- private int stopUsersLocked(final int userId, boolean force, final IStopUserCallback callback) {
+ private int stopUsersLU(final int userId, boolean force, final IStopUserCallback callback) {
if (userId == UserHandle.USER_SYSTEM) {
return USER_OP_ERROR_IS_SYSTEM;
}
- if (isCurrentUserLocked(userId)) {
+ if (isCurrentUserLU(userId)) {
return USER_OP_IS_CURRENT;
}
- int[] usersToStop = getUsersToStopLocked(userId);
+ int[] usersToStop = getUsersToStopLU(userId);
// If one of related users is system or current, no related users should be stopped
for (int i = 0; i < usersToStop.length; i++) {
int relatedUserId = usersToStop[i];
- if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLocked(relatedUserId)) {
+ if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLU(relatedUserId)) {
if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked cannot stop related user "
+ relatedUserId);
// We still need to stop the requested user if it's a force stop.
if (force) {
Slog.i(TAG,
"Force stop user " + userId + ". Related users will not be stopped");
- stopSingleUserLocked(userId, callback);
+ stopSingleUserLU(userId, callback);
return USER_OP_SUCCESS;
}
return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
@@ -550,25 +567,22 @@
}
if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop));
for (int userIdToStop : usersToStop) {
- stopSingleUserLocked(userIdToStop, userIdToStop == userId ? callback : null);
+ stopSingleUserLU(userIdToStop, userIdToStop == userId ? callback : null);
}
return USER_OP_SUCCESS;
}
- private void stopSingleUserLocked(final int userId, final IStopUserCallback callback) {
+ private void stopSingleUserLU(final int userId, final IStopUserCallback callback) {
if (DEBUG_MU) Slog.i(TAG, "stopSingleUserLocked userId=" + userId);
final UserState uss = mStartedUsers.get(userId);
if (uss == null) {
// User is not started, nothing to do... but we do need to
// callback if requested.
if (callback != null) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- callback.userStopped(userId);
- } catch (RemoteException e) {
- }
+ mHandler.post(() -> {
+ try {
+ callback.userStopped(userId);
+ } catch (RemoteException e) {
}
});
}
@@ -583,10 +597,10 @@
&& uss.state != UserState.STATE_SHUTDOWN) {
uss.setState(UserState.STATE_STOPPING);
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
- updateStartedUserArrayLocked();
+ updateStartedUserArrayLU();
- long ident = Binder.clearCallingIdentity();
- try {
+ // Post to handler to obtain amLock
+ mHandler.post(() -> {
// We are going to broadcast ACTION_USER_STOPPING and then
// once that is done send a final ACTION_SHUTDOWN and then
// stop the user.
@@ -599,24 +613,18 @@
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- finishUserStopping(userId, uss);
- }
- });
+ mHandler.post(() -> finishUserStopping(userId, uss));
}
};
+
// Clear broadcast queue for the user to avoid delivering stale broadcasts
- mInjector.clearBroadcastQueueForUserLocked(userId);
+ mInjector.clearBroadcastQueueForUser(userId);
// Kick things off.
- mInjector.broadcastIntentLocked(stoppingIntent,
+ mInjector.broadcastIntent(stoppingIntent,
null, stoppingReceiver, 0, null, null,
new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ });
}
}
@@ -652,18 +660,17 @@
Integer.toString(userId), userId);
mInjector.getSystemServiceManager().stopUser(userId);
- synchronized (mLock) {
- mInjector.broadcastIntentLocked(shutdownIntent,
- null, shutdownReceiver, 0, null, null, null,
- AppOpsManager.OP_NONE,
- null, true, false, MY_PID, SYSTEM_UID, userId);
- }
+ mInjector.broadcastIntent(shutdownIntent,
+ null, shutdownReceiver, 0, null, null, null,
+ AppOpsManager.OP_NONE,
+ null, true, false, MY_PID, SYSTEM_UID, userId);
}
void finishUserStopped(UserState uss) {
final int userId = uss.mHandle.getIdentifier();
boolean stopped;
ArrayList<IStopUserCallback> callbacks;
+ boolean forceStopUser = false;
synchronized (mLock) {
callbacks = new ArrayList<>(uss.mStopCallbacks);
if (mStartedUsers.get(userId) != uss) {
@@ -674,16 +681,18 @@
stopped = true;
// User can no longer run.
mStartedUsers.remove(userId);
- mInjector.getUserManagerInternal().removeUserState(userId);
mUserLru.remove(Integer.valueOf(userId));
- updateStartedUserArrayLocked();
-
- mInjector.activityManagerOnUserStopped(userId);
- // Clean up all state and processes associated with the user.
- // Kill all the processes for the user.
- forceStopUserLocked(userId, "finish user");
+ updateStartedUserArrayLU();
+ forceStopUser = true;
}
}
+ if (forceStopUser) {
+ mInjector.getUserManagerInternal().removeUserState(userId);
+ mInjector.activityManagerOnUserStopped(userId);
+ // Clean up all state and processes associated with the user.
+ // Kill all the processes for the user.
+ forceStopUser(userId, "finish user");
+ }
for (int i = 0; i < callbacks.size(); i++) {
try {
@@ -695,9 +704,7 @@
if (stopped) {
mInjector.systemServiceManagerCleanupUser(userId);
- synchronized (mLock) {
- mInjector.getActivityStackSupervisor().removeUserLocked(userId);
- }
+ mInjector.stackSupervisorRemoveUser(userId);
// Remove the user if it is ephemeral.
if (getUserInfo(userId).isEphemeral()) {
mInjector.getUserManager().removeUser(userId);
@@ -715,39 +722,36 @@
* Determines the list of users that should be stopped together with the specified
* {@code userId}. The returned list includes {@code userId}.
*/
- private @NonNull int[] getUsersToStopLocked(int userId) {
+ private @NonNull int[] getUsersToStopLU(int userId) {
int startedUsersSize = mStartedUsers.size();
IntArray userIds = new IntArray();
userIds.add(userId);
- synchronized (mUserProfileGroupIdsSelfLocked) {
- int userGroupId = mUserProfileGroupIdsSelfLocked.get(userId,
+ int userGroupId = mUserProfileGroupIds.get(userId, UserInfo.NO_PROFILE_GROUP_ID);
+ for (int i = 0; i < startedUsersSize; i++) {
+ UserState uss = mStartedUsers.valueAt(i);
+ int startedUserId = uss.mHandle.getIdentifier();
+ // Skip unrelated users (profileGroupId mismatch)
+ int startedUserGroupId = mUserProfileGroupIds.get(startedUserId,
UserInfo.NO_PROFILE_GROUP_ID);
- for (int i = 0; i < startedUsersSize; i++) {
- UserState uss = mStartedUsers.valueAt(i);
- int startedUserId = uss.mHandle.getIdentifier();
- // Skip unrelated users (profileGroupId mismatch)
- int startedUserGroupId = mUserProfileGroupIdsSelfLocked.get(startedUserId,
- UserInfo.NO_PROFILE_GROUP_ID);
- boolean sameGroup = (userGroupId != UserInfo.NO_PROFILE_GROUP_ID)
- && (userGroupId == startedUserGroupId);
- // userId has already been added
- boolean sameUserId = startedUserId == userId;
- if (!sameGroup || sameUserId) {
- continue;
- }
- userIds.add(startedUserId);
+ boolean sameGroup = (userGroupId != UserInfo.NO_PROFILE_GROUP_ID)
+ && (userGroupId == startedUserGroupId);
+ // userId has already been added
+ boolean sameUserId = startedUserId == userId;
+ if (!sameGroup || sameUserId) {
+ continue;
}
+ userIds.add(startedUserId);
}
return userIds.toArray();
}
- private void forceStopUserLocked(int userId, String reason) {
- mInjector.activityManagerForceStopPackageLocked(userId, reason);
+ private void forceStopUser(int userId, String reason) {
+ mInjector.activityManagerForceStopPackage(userId, reason);
Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- mInjector.broadcastIntentLocked(intent,
+ mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
}
@@ -756,6 +760,7 @@
* Stops the guest or ephemeral user if it has gone to the background.
*/
private void stopGuestOrEphemeralUserIfBackground() {
+ IntArray userIds = new IntArray();
synchronized (mLock) {
final int num = mUserLru.size();
for (int i = 0; i < num; i++) {
@@ -766,35 +771,42 @@
|| oldUss.state == UserState.STATE_SHUTDOWN) {
continue;
}
- UserInfo userInfo = getUserInfo(oldUserId);
- if (userInfo.isEphemeral()) {
- LocalServices.getService(UserManagerInternal.class)
- .onEphemeralUserStop(oldUserId);
+ userIds.add(oldUserId);
+ }
+ }
+ final int userIdsSize = userIds.size();
+ for (int i = 0; i < userIdsSize; i++) {
+ int oldUserId = userIds.get(i);
+ UserInfo userInfo = getUserInfo(oldUserId);
+ if (userInfo.isEphemeral()) {
+ LocalServices.getService(UserManagerInternal.class).onEphemeralUserStop(oldUserId);
+ }
+ if (userInfo.isGuest() || userInfo.isEphemeral()) {
+ // This is a user to be stopped.
+ synchronized (mLock) {
+ stopUsersLU(oldUserId, true, null);
}
- if (userInfo.isGuest() || userInfo.isEphemeral()) {
- // This is a user to be stopped.
- stopUsersLocked(oldUserId, true, null);
- break;
- }
+ break;
}
}
}
- void scheduleStartProfilesLocked() {
+ void scheduleStartProfiles() {
if (!mHandler.hasMessages(START_PROFILES_MSG)) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(START_PROFILES_MSG),
DateUtils.SECOND_IN_MILLIS);
}
}
- void startProfilesLocked() {
+ void startProfiles() {
+ int currentUserId = getCurrentUserId();
if (DEBUG_MU) Slog.i(TAG, "startProfilesLocked");
List<UserInfo> profiles = mInjector.getUserManager().getProfiles(
- mCurrentUserId, false /* enabledOnly */);
+ currentUserId, false /* enabledOnly */);
List<UserInfo> profilesToStart = new ArrayList<>(profiles.size());
for (UserInfo user : profiles) {
if ((user.flags & UserInfo.FLAG_INITIALIZED) == UserInfo.FLAG_INITIALIZED
- && user.id != mCurrentUserId && !user.isQuietModeEnabled()) {
+ && user.id != currentUserId && !user.isQuietModeEnabled()) {
profilesToStart.add(user);
}
}
@@ -856,143 +868,156 @@
final long ident = Binder.clearCallingIdentity();
try {
+ final int oldUserId = getCurrentUserId();
+ if (oldUserId == userId) {
+ return true;
+ }
+
+ if (foreground) {
+ // TODO: I don't think this does what the caller think it does. Seems to only
+ // remove one locked task and won't work if multiple locked tasks are present.
+ mInjector.clearLockTaskMode("startUser");
+ }
+
+ final UserInfo userInfo = getUserInfo(userId);
+ if (userInfo == null) {
+ Slog.w(TAG, "No user info for user #" + userId);
+ return false;
+ }
+ if (foreground && userInfo.isManagedProfile()) {
+ Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
+ return false;
+ }
+
+ if (foreground && mUserSwitchUiEnabled) {
+ mInjector.getWindowManager().startFreezingScreen(
+ R.anim.screen_user_exit, R.anim.screen_user_enter);
+ }
+
+ boolean needStart = false;
+ boolean updateUmState = false;
+ UserState uss;
+
+ // If the user we are switching to is not currently started, then
+ // we need to start it now.
synchronized (mLock) {
- final int oldUserId = mCurrentUserId;
- if (oldUserId == userId) {
- return true;
- }
-
- if (foreground) {
- // TODO: I don't think this does what the caller think it does. Seems to only
- // remove one locked task and won't work if multiple locked tasks are present.
- mInjector.getLockTaskController().clearLockTaskMode("startUser");
- }
-
- final UserInfo userInfo = getUserInfo(userId);
- if (userInfo == null) {
- Slog.w(TAG, "No user info for user #" + userId);
- return false;
- }
- if (foreground && userInfo.isManagedProfile()) {
- Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
- return false;
- }
-
- if (foreground && mUserSwitchUiEnabled) {
- mInjector.getWindowManager().startFreezingScreen(
- R.anim.screen_user_exit, R.anim.screen_user_enter);
- }
-
- boolean needStart = false;
-
- // If the user we are switching to is not currently started, then
- // we need to start it now.
- if (mStartedUsers.get(userId) == null) {
- UserState userState = new UserState(UserHandle.of(userId));
- mStartedUsers.put(userId, userState);
- mInjector.getUserManagerInternal().setUserState(userId, userState.state);
- updateStartedUserArrayLocked();
+ uss = mStartedUsers.get(userId);
+ if (uss == null) {
+ uss = new UserState(UserHandle.of(userId));
+ mStartedUsers.put(userId, uss);
+ updateStartedUserArrayLU();
needStart = true;
+ updateUmState = true;
}
-
- final UserState uss = mStartedUsers.get(userId);
final Integer userIdInt = userId;
mUserLru.remove(userIdInt);
mUserLru.add(userIdInt);
-
- if (foreground) {
+ }
+ if (updateUmState) {
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ }
+ if (foreground) {
+ synchronized (mLock) {
mCurrentUserId = userId;
- mInjector.updateUserConfigurationLocked();
mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
- updateCurrentProfileIdsLocked();
- mInjector.getWindowManager().setCurrentUser(userId, mCurrentProfileIds);
- // Once the internal notion of the active user has switched, we lock the device
- // with the option to show the user switcher on the keyguard.
- if (mUserSwitchUiEnabled) {
- mInjector.getWindowManager().setSwitchingUser(true);
- mInjector.getWindowManager().lockNow(null);
- }
- } else {
- final Integer currentUserIdInt = mCurrentUserId;
- updateCurrentProfileIdsLocked();
- mInjector.getWindowManager().setCurrentProfileIds(mCurrentProfileIds);
+ }
+ mInjector.updateUserConfiguration();
+ updateCurrentProfileIds();
+ mInjector.getWindowManager().setCurrentUser(userId, getCurrentProfileIds());
+ // Once the internal notion of the active user has switched, we lock the device
+ // with the option to show the user switcher on the keyguard.
+ if (mUserSwitchUiEnabled) {
+ mInjector.getWindowManager().setSwitchingUser(true);
+ mInjector.getWindowManager().lockNow(null);
+ }
+ } else {
+ final Integer currentUserIdInt = mCurrentUserId;
+ updateCurrentProfileIds();
+ mInjector.getWindowManager().setCurrentProfileIds(getCurrentProfileIds());
+ synchronized (mLock) {
mUserLru.remove(currentUserIdInt);
mUserLru.add(currentUserIdInt);
}
+ }
- // Make sure user is in the started state. If it is currently
- // stopping, we need to knock that off.
- if (uss.state == UserState.STATE_STOPPING) {
- // If we are stopping, we haven't sent ACTION_SHUTDOWN,
- // so we can just fairly silently bring the user back from
- // the almost-dead.
- uss.setState(uss.lastState);
- mInjector.getUserManagerInternal().setUserState(userId, uss.state);
- updateStartedUserArrayLocked();
- needStart = true;
- } else if (uss.state == UserState.STATE_SHUTDOWN) {
- // This means ACTION_SHUTDOWN has been sent, so we will
- // need to treat this as a new boot of the user.
- uss.setState(UserState.STATE_BOOTING);
- mInjector.getUserManagerInternal().setUserState(userId, uss.state);
- updateStartedUserArrayLocked();
- needStart = true;
+ // Make sure user is in the started state. If it is currently
+ // stopping, we need to knock that off.
+ if (uss.state == UserState.STATE_STOPPING) {
+ // If we are stopping, we haven't sent ACTION_SHUTDOWN,
+ // so we can just fairly silently bring the user back from
+ // the almost-dead.
+ uss.setState(uss.lastState);
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ synchronized (mLock) {
+ updateStartedUserArrayLU();
}
-
- if (uss.state == UserState.STATE_BOOTING) {
- // Give user manager a chance to propagate user restrictions
- // to other services and prepare app storage
- mInjector.getUserManager().onBeforeStartUser(userId);
-
- // Booting up a new user, need to tell system services about it.
- // Note that this is on the same handler as scheduling of broadcasts,
- // which is important because it needs to go first.
- mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
+ needStart = true;
+ } else if (uss.state == UserState.STATE_SHUTDOWN) {
+ // This means ACTION_SHUTDOWN has been sent, so we will
+ // need to treat this as a new boot of the user.
+ uss.setState(UserState.STATE_BOOTING);
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ synchronized (mLock) {
+ updateStartedUserArrayLU();
}
+ needStart = true;
+ }
- if (foreground) {
- mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
- oldUserId));
- mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
- mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
- oldUserId, userId, uss));
- mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
- oldUserId, userId, uss), USER_SWITCH_TIMEOUT_MS);
- }
+ if (uss.state == UserState.STATE_BOOTING) {
+ // Give user manager a chance to propagate user restrictions
+ // to other services and prepare app storage
+ mInjector.getUserManager().onBeforeStartUser(userId);
- if (needStart) {
- // Send USER_STARTED broadcast
- Intent intent = new Intent(Intent.ACTION_USER_STARTED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- mInjector.broadcastIntentLocked(intent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, SYSTEM_UID, userId);
- }
+ // Booting up a new user, need to tell system services about it.
+ // Note that this is on the same handler as scheduling of broadcasts,
+ // which is important because it needs to go first.
+ mHandler.sendMessage(
+ mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
+ }
- if (foreground) {
- moveUserToForegroundLocked(uss, oldUserId, userId);
- } else {
- finishUserBoot(uss);
- }
+ if (foreground) {
+ mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
+ oldUserId));
+ mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
+ mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
+ oldUserId, userId, uss));
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
+ oldUserId, userId, uss), USER_SWITCH_TIMEOUT_MS);
+ }
- if (needStart) {
- Intent intent = new Intent(Intent.ACTION_USER_STARTING);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- mInjector.broadcastIntentLocked(intent,
- null, new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered, boolean sticky,
- int sendingUser) throws RemoteException {
- }
- }, 0, null, null,
- new String[] {INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
- }
+ if (needStart) {
+ // Send USER_STARTED broadcast
+ Intent intent = new Intent(Intent.ACTION_USER_STARTED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mInjector.broadcastIntent(intent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, MY_PID, SYSTEM_UID, userId);
+ }
+
+ if (foreground) {
+ moveUserToForeground(uss, oldUserId, userId);
+ } else {
+ finishUserBoot(uss);
+ }
+
+ if (needStart) {
+ Intent intent = new Intent(Intent.ACTION_USER_STARTING);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mInjector.broadcastIntent(intent,
+ null, new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky,
+ int sendingUser) throws RemoteException {
+ }
+ }, 0, null, null,
+ new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
+ null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -1036,7 +1061,7 @@
* when the the credential-encrypted storage isn't tied to a user-provided
* PIN or pattern.
*/
- boolean maybeUnlockUser(final int userId) {
+ private boolean maybeUnlockUser(final int userId) {
// Try unlocking storage using empty token
return unlockUserCleared(userId, null, null, null);
}
@@ -1049,54 +1074,53 @@
}
}
- boolean unlockUserCleared(final int userId, byte[] token, byte[] secret,
+ private boolean unlockUserCleared(final int userId, byte[] token, byte[] secret,
IProgressListener listener) {
UserState uss;
- synchronized (mLock) {
- // TODO Move this block outside of synchronized if it causes lock contention
- if (!StorageManager.isUserKeyUnlocked(userId)) {
- final UserInfo userInfo = getUserInfo(userId);
- final IStorageManager storageManager = getStorageManager();
- try {
- // We always want to unlock user storage, even user is not started yet
- storageManager.unlockUserKey(userId, userInfo.serialNumber, token, secret);
- } catch (RemoteException | RuntimeException e) {
- Slog.w(TAG, "Failed to unlock: " + e.getMessage());
- }
+ if (!StorageManager.isUserKeyUnlocked(userId)) {
+ final UserInfo userInfo = getUserInfo(userId);
+ final IStorageManager storageManager = getStorageManager();
+ try {
+ // We always want to unlock user storage, even user is not started yet
+ storageManager.unlockUserKey(userId, userInfo.serialNumber, token, secret);
+ } catch (RemoteException | RuntimeException e) {
+ Slog.w(TAG, "Failed to unlock: " + e.getMessage());
}
- // Bail if user isn't actually running, otherwise register the given
- // listener to watch for unlock progress
+ }
+ synchronized (mLock) {
+ // Register the given listener to watch for unlock progress
uss = mStartedUsers.get(userId);
- if (uss == null) {
- notifyFinished(userId, listener);
- return false;
- } else {
+ if (uss != null) {
uss.mUnlockProgress.addListener(listener);
uss.tokenProvided = (token != null);
}
}
+ // Bail if user isn't actually running
+ if (uss == null) {
+ notifyFinished(userId, listener);
+ return false;
+ }
finishUserUnlocking(uss);
- final ArraySet<Integer> childProfilesToUnlock = new ArraySet<>();
- synchronized (mLock) {
+ // We just unlocked a user, so let's now attempt to unlock any
+ // managed profiles under that user.
- // We just unlocked a user, so let's now attempt to unlock any
- // managed profiles under that user.
- for (int i = 0; i < mStartedUsers.size(); i++) {
- final int testUserId = mStartedUsers.keyAt(i);
- final UserInfo parent = mInjector.getUserManager().getProfileParent(testUserId);
- if (parent != null && parent.id == userId && testUserId != userId) {
- Slog.d(TAG, "User " + testUserId + " (parent " + parent.id
- + "): attempting unlock because parent was just unlocked");
- childProfilesToUnlock.add(testUserId);
- }
+ // First, get list of userIds. Requires mLock, so we cannot make external calls, e.g. to UMS
+ int[] userIds;
+ synchronized (mLock) {
+ userIds = new int[mStartedUsers.size()];
+ for (int i = 0; i < userIds.length; i++) {
+ userIds[i] = mStartedUsers.keyAt(i);
}
}
-
- final int size = childProfilesToUnlock.size();
- for (int i = 0; i < size; i++) {
- maybeUnlockUser(childProfilesToUnlock.valueAt(i));
+ for (int testUserId : userIds) {
+ final UserInfo parent = mInjector.getUserManager().getProfileParent(testUserId);
+ if (parent != null && parent.id == userId && testUserId != userId) {
+ Slog.d(TAG, "User " + testUserId + " (parent " + parent.id
+ + "): attempting unlock because parent was just unlocked");
+ maybeUnlockUser(testUserId);
+ }
}
return true;
@@ -1104,33 +1128,31 @@
boolean switchUser(final int targetUserId) {
enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, targetUserId);
- int currentUserId;
- UserInfo targetUserInfo;
+ int currentUserId = getCurrentUserId();
+ UserInfo targetUserInfo = getUserInfo(targetUserId);
+ if (targetUserId == currentUserId) {
+ Slog.i(TAG, "user #" + targetUserId + " is already the current user");
+ return true;
+ }
+ if (targetUserInfo == null) {
+ Slog.w(TAG, "No user info for user #" + targetUserId);
+ return false;
+ }
+ if (!targetUserInfo.isDemo() && UserManager.isDeviceInDemoMode(mInjector.getContext())) {
+ Slog.w(TAG, "Cannot switch to non-demo user #" + targetUserId
+ + " when device is in demo mode");
+ return false;
+ }
+ if (!targetUserInfo.supportsSwitchTo()) {
+ Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
+ return false;
+ }
+ if (targetUserInfo.isManagedProfile()) {
+ Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
+ return false;
+ }
synchronized (mLock) {
- currentUserId = getCurrentUserIdLocked();
- targetUserInfo = getUserInfo(targetUserId);
- if (targetUserId == currentUserId) {
- Slog.i(TAG, "user #" + targetUserId + " is already the current user");
- return true;
- }
- if (targetUserInfo == null) {
- Slog.w(TAG, "No user info for user #" + targetUserId);
- return false;
- }
- if (!targetUserInfo.isDemo() && UserManager.isDeviceInDemoMode(mInjector.getContext())) {
- Slog.w(TAG, "Cannot switch to non-demo user #" + targetUserId
- + " when device is in demo mode");
- return false;
- }
- if (!targetUserInfo.supportsSwitchTo()) {
- Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
- return false;
- }
- if (targetUserInfo.isManagedProfile()) {
- Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
- return false;
- }
- setTargetUserIdLocked(targetUserId);
+ mTargetUserId = targetUserId;
}
if (mUserSwitchUiEnabled) {
UserInfo currentUserInfo = getUserInfo(currentUserId);
@@ -1146,12 +1168,12 @@
return true;
}
- void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
+ private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
// The dialog will show and then initiate the user switch by calling startUserInForeground
mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second);
}
- void dispatchForegroundProfileChanged(int userId) {
+ private void dispatchForegroundProfileChanged(int userId) {
final int observerCount = mUserSwitchObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
try {
@@ -1176,7 +1198,7 @@
mUserSwitchObservers.finishBroadcast();
}
- void dispatchLockedBootComplete(int userId) {
+ private void dispatchLockedBootComplete(int userId) {
final int observerCount = mUserSwitchObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
try {
@@ -1202,23 +1224,23 @@
synchronized (mLock) {
if (DEBUG_MU) Slog.i(TAG, "stopBackgroundUsersIfEnforced stopping " + oldUserId
+ " and related users");
- stopUsersLocked(oldUserId, false, null);
+ stopUsersLU(oldUserId, false, null);
}
}
- void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
+ private void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
synchronized (mLock) {
Slog.e(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
mTimeoutUserSwitchCallbacks = mCurWaitingUserSwitchCallbacks;
mHandler.removeMessages(USER_SWITCH_CALLBACKS_TIMEOUT_MSG);
- sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ sendContinueUserSwitchLU(uss, oldUserId, newUserId);
// Report observers that never called back (USER_SWITCH_CALLBACKS_TIMEOUT)
mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_CALLBACKS_TIMEOUT_MSG,
oldUserId, newUserId), USER_SWITCH_CALLBACKS_TIMEOUT_MS);
}
}
- void timeoutUserSwitchCallbacks(int oldUserId, int newUserId) {
+ private void timeoutUserSwitchCallbacks(int oldUserId, int newUserId) {
synchronized (mLock) {
if (mTimeoutUserSwitchCallbacks != null && !mTimeoutUserSwitchCallbacks.isEmpty()) {
Slog.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId
@@ -1261,7 +1283,7 @@
if (waitingCallbacksCount.decrementAndGet() == 0
&& (curWaitingUserSwitchCallbacks
== mCurWaitingUserSwitchCallbacks)) {
- sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ sendContinueUserSwitchLU(uss, oldUserId, newUserId);
}
}
}
@@ -1272,13 +1294,13 @@
}
} else {
synchronized (mLock) {
- sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ sendContinueUserSwitchLU(uss, oldUserId, newUserId);
}
}
mUserSwitchObservers.finishBroadcast();
}
- void sendContinueUserSwitchLocked(UserState uss, int oldUserId, int newUserId) {
+ void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) {
mCurWaitingUserSwitchCallbacks = null;
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG,
@@ -1288,9 +1310,7 @@
void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
Slog.d(TAG, "Continue user switch oldUser #" + oldUserId + ", newUser #" + newUserId);
if (mUserSwitchUiEnabled) {
- synchronized (mLock) {
- mInjector.getWindowManager().stopFreezingScreen();
- }
+ mInjector.getWindowManager().stopFreezingScreen();
}
uss.switching = false;
mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
@@ -1300,19 +1320,18 @@
stopBackgroundUsersIfEnforced(oldUserId);
}
- void moveUserToForegroundLocked(UserState uss, int oldUserId, int newUserId) {
- boolean homeInFront =
- mInjector.getActivityStackSupervisor().switchUserLocked(newUserId, uss);
+ private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) {
+ boolean homeInFront = mInjector.stackSupervisorSwitchUser(newUserId, uss);
if (homeInFront) {
- mInjector.startHomeActivityLocked(newUserId, "moveUserToForeground");
+ mInjector.startHomeActivity(newUserId, "moveUserToForeground");
} else {
- mInjector.getActivityStackSupervisor().resumeFocusedStackTopActivityLocked();
+ mInjector.stackSupervisorResumeFocusedStackTopActivity();
}
EventLogTags.writeAmSwitchUser(newUserId);
- sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
+ sendUserSwitchBroadcasts(oldUserId, newUserId);
}
- void sendUserSwitchBroadcastsLocked(int oldUserId, int newUserId) {
+ void sendUserSwitchBroadcasts(int oldUserId, int newUserId) {
long ident = Binder.clearCallingIdentity();
try {
Intent intent;
@@ -1326,7 +1345,7 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
- mInjector.broadcastIntentLocked(intent,
+ mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, profileUserId);
}
@@ -1341,7 +1360,7 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
- mInjector.broadcastIntentLocked(intent,
+ mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, profileUserId);
}
@@ -1349,7 +1368,7 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
- mInjector.broadcastIntentLocked(intent,
+ mInjector.broadcastIntent(intent,
null, null, 0, null, null,
new String[] {android.Manifest.permission.MANAGE_USERS},
AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
@@ -1374,7 +1393,7 @@
// the value the caller will receive and someone else changing it.
// We assume that USER_CURRENT_OR_SELF will use the current user; later
// we will switch to the calling user if access to the current user fails.
- int targetUserId = unsafeConvertIncomingUserLocked(userId);
+ int targetUserId = unsafeConvertIncomingUser(userId);
if (callingUid != 0 && callingUid != SYSTEM_UID) {
final boolean allow;
@@ -1442,9 +1461,9 @@
return targetUserId;
}
- int unsafeConvertIncomingUserLocked(int userId) {
+ int unsafeConvertIncomingUser(int userId) {
return (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF)
- ? getCurrentUserIdLocked(): userId;
+ ? getCurrentUserId(): userId;
}
void registerUserSwitchObserver(IUserSwitchObserver observer, String name) {
@@ -1470,15 +1489,17 @@
mUserSwitchObservers.unregister(observer);
}
- UserState getStartedUserStateLocked(int userId) {
- return mStartedUsers.get(userId);
+ UserState getStartedUserState(int userId) {
+ synchronized (mLock) {
+ return mStartedUsers.get(userId);
+ }
}
boolean hasStartedUserState(int userId) {
return mStartedUsers.get(userId) != null;
}
- private void updateStartedUserArrayLocked() {
+ private void updateStartedUserArrayLU() {
int num = 0;
for (int i = 0; i < mStartedUsers.size(); i++) {
UserState uss = mStartedUsers.valueAt(i);
@@ -1499,15 +1520,20 @@
}
}
- void sendBootCompletedLocked(IIntentReceiver resultTo) {
- for (int i = 0; i < mStartedUsers.size(); i++) {
- UserState uss = mStartedUsers.valueAt(i);
+ void sendBootCompleted(IIntentReceiver resultTo) {
+ // Get a copy of mStartedUsers to use outside of lock
+ SparseArray<UserState> startedUsers;
+ synchronized (mLock) {
+ startedUsers = mStartedUsers.clone();
+ }
+ for (int i = 0; i < startedUsers.size(); i++) {
+ UserState uss = startedUsers.valueAt(i);
finishUserBoot(uss, resultTo);
}
}
void onSystemReady() {
- updateCurrentProfileIdsLocked();
+ updateCurrentProfileIds();
}
/**
@@ -1515,33 +1541,35 @@
* user switch happens or when a new related user is started in the
* background.
*/
- private void updateCurrentProfileIdsLocked() {
- final List<UserInfo> profiles = mInjector.getUserManager().getProfiles(mCurrentUserId,
+ private void updateCurrentProfileIds() {
+ final List<UserInfo> profiles = mInjector.getUserManager().getProfiles(getCurrentUserId(),
false /* enabledOnly */);
int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
for (int i = 0; i < currentProfileIds.length; i++) {
currentProfileIds[i] = profiles.get(i).id;
}
- mCurrentProfileIds = currentProfileIds;
+ final List<UserInfo> users = mInjector.getUserManager().getUsers(false);
+ synchronized (mLock) {
+ mCurrentProfileIds = currentProfileIds;
- synchronized (mUserProfileGroupIdsSelfLocked) {
- mUserProfileGroupIdsSelfLocked.clear();
- final List<UserInfo> users = mInjector.getUserManager().getUsers(false);
+ mUserProfileGroupIds.clear();
for (int i = 0; i < users.size(); i++) {
UserInfo user = users.get(i);
if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
- mUserProfileGroupIdsSelfLocked.put(user.id, user.profileGroupId);
+ mUserProfileGroupIds.put(user.id, user.profileGroupId);
}
}
}
}
- int[] getStartedUserArrayLocked() {
- return mStartedUserArray;
+ int[] getStartedUserArray() {
+ synchronized (mLock) {
+ return mStartedUserArray;
+ }
}
- boolean isUserRunningLocked(int userId, int flags) {
- UserState state = getStartedUserStateLocked(userId);
+ boolean isUserRunning(int userId, int flags) {
+ UserState state = getStartedUserState(userId);
if (state == null) {
return false;
}
@@ -1604,29 +1632,38 @@
return getUserInfo(mCurrentUserId);
}
synchronized (mLock) {
- return getCurrentUserLocked();
+ return getCurrentUserLU();
}
}
- UserInfo getCurrentUserLocked() {
+ UserInfo getCurrentUserLU() {
int userId = mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
return getUserInfo(userId);
}
- int getCurrentOrTargetUserIdLocked() {
+ int getCurrentOrTargetUserId() {
+ synchronized (mLock) {
+ return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
+ }
+ }
+
+ int getCurrentOrTargetUserIdLU() {
return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
}
- int getCurrentUserIdLocked() {
+
+ int getCurrentUserIdLU() {
return mCurrentUserId;
}
- private boolean isCurrentUserLocked(int userId) {
- return userId == getCurrentOrTargetUserIdLocked();
+ int getCurrentUserId() {
+ synchronized (mLock) {
+ return mCurrentUserId;
+ }
}
- int setTargetUserIdLocked(int targetUserId) {
- return mTargetUserId = targetUserId;
+ private boolean isCurrentUserLU(int userId) {
+ return userId == getCurrentOrTargetUserIdLU();
}
int[] getUsers() {
@@ -1673,22 +1710,26 @@
if (callingUserId == targetUserId) {
return true;
}
- synchronized (mUserProfileGroupIdsSelfLocked) {
- int callingProfile = mUserProfileGroupIdsSelfLocked.get(callingUserId,
+ synchronized (mLock) {
+ int callingProfile = mUserProfileGroupIds.get(callingUserId,
UserInfo.NO_PROFILE_GROUP_ID);
- int targetProfile = mUserProfileGroupIdsSelfLocked.get(targetUserId,
+ int targetProfile = mUserProfileGroupIds.get(targetUserId,
UserInfo.NO_PROFILE_GROUP_ID);
return callingProfile != UserInfo.NO_PROFILE_GROUP_ID
&& callingProfile == targetProfile;
}
}
- boolean isCurrentProfileLocked(int userId) {
- return ArrayUtils.contains(mCurrentProfileIds, userId);
+ boolean isCurrentProfile(int userId) {
+ synchronized (mLock) {
+ return ArrayUtils.contains(mCurrentProfileIds, userId);
+ }
}
- int[] getCurrentProfileIdsLocked() {
- return mCurrentProfileIds;
+ int[] getCurrentProfileIds() {
+ synchronized (mLock) {
+ return mCurrentProfileIds;
+ }
}
/**
@@ -1713,35 +1754,40 @@
}
void dump(PrintWriter pw, boolean dumpAll) {
- pw.println(" mStartedUsers:");
- for (int i = 0; i < mStartedUsers.size(); i++) {
- UserState uss = mStartedUsers.valueAt(i);
- pw.print(" User #"); pw.print(uss.mHandle.getIdentifier());
- pw.print(": "); uss.dump("", pw);
- }
- pw.print(" mStartedUserArray: [");
- for (int i = 0; i < mStartedUserArray.length; i++) {
- if (i > 0) pw.print(", ");
- pw.print(mStartedUserArray[i]);
- }
- pw.println("]");
- pw.print(" mUserLru: [");
- for (int i = 0; i < mUserLru.size(); i++) {
- if (i > 0) pw.print(", ");
- pw.print(mUserLru.get(i));
- }
- pw.println("]");
- if (dumpAll) {
- pw.print(" mStartedUserArray: "); pw.println(Arrays.toString(mStartedUserArray));
- }
- synchronized (mUserProfileGroupIdsSelfLocked) {
- if (mUserProfileGroupIdsSelfLocked.size() > 0) {
+ synchronized (mLock) {
+ pw.println(" mStartedUsers:");
+ for (int i = 0; i < mStartedUsers.size(); i++) {
+ UserState uss = mStartedUsers.valueAt(i);
+ pw.print(" User #");
+ pw.print(uss.mHandle.getIdentifier());
+ pw.print(": ");
+ uss.dump("", pw);
+ }
+ pw.print(" mStartedUserArray: [");
+ for (int i = 0; i < mStartedUserArray.length; i++) {
+ if (i > 0)
+ pw.print(", ");
+ pw.print(mStartedUserArray[i]);
+ }
+ pw.println("]");
+ pw.print(" mUserLru: [");
+ for (int i = 0; i < mUserLru.size(); i++) {
+ if (i > 0)
+ pw.print(", ");
+ pw.print(mUserLru.get(i));
+ }
+ pw.println("]");
+ if (dumpAll) {
+ pw.print(" mStartedUserArray: ");
+ pw.println(Arrays.toString(mStartedUserArray));
+ }
+ if (mUserProfileGroupIds.size() > 0) {
pw.println(" mUserProfileGroupIds:");
- for (int i=0; i<mUserProfileGroupIdsSelfLocked.size(); i++) {
+ for (int i=0; i< mUserProfileGroupIds.size(); i++) {
pw.print(" User #");
- pw.print(mUserProfileGroupIdsSelfLocked.keyAt(i));
+ pw.print(mUserProfileGroupIds.keyAt(i));
pw.print(" -> profile #");
- pw.println(mUserProfileGroupIdsSelfLocked.valueAt(i));
+ pw.println(mUserProfileGroupIds.valueAt(i));
}
}
}
@@ -1765,9 +1811,7 @@
timeoutUserSwitchCallbacks(msg.arg1, msg.arg2);
break;
case START_PROFILES_MSG:
- synchronized (mLock) {
- startProfilesLocked();
- }
+ startProfiles();
break;
case SYSTEM_USER_START_MSG:
mInjector.batteryStatsServiceNoteEvent(
@@ -1778,9 +1822,7 @@
case SYSTEM_USER_UNLOCK_MSG:
final int userId = msg.arg1;
mInjector.getSystemServiceManager().unlockUser(userId);
- synchronized (mLock) {
- mInjector.loadUserRecentsLocked(userId);
- }
+ mInjector.loadUserRecents(userId);
if (userId == UserHandle.USER_SYSTEM) {
mInjector.startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
}
@@ -1823,10 +1865,6 @@
mService = service;
}
- protected Object getLock() {
- return mService;
- }
-
protected Handler getHandler(Handler.Callback callback) {
return new Handler(mService.mHandlerThread.getLooper(), callback);
}
@@ -1843,13 +1881,16 @@
return new LockPatternUtils(getContext());
}
- protected int broadcastIntentLocked(Intent intent, String resolvedType,
+ protected int broadcastIntent(Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
- return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo,
- resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions,
- ordered, sticky, callingPid, callingUid, userId);
+ // TODO b/64165549 Verify that mLock is not held before calling AMS methods
+ synchronized (mService) {
+ return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo,
+ resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions,
+ ordered, sticky, callingPid, callingUid, userId);
+ }
}
int checkCallingPermission(String permission) {
@@ -1860,7 +1901,9 @@
return mService.mWindowManager;
}
void activityManagerOnUserStopped(int userId) {
- mService.onUserStoppedLocked(userId);
+ synchronized (mService) {
+ mService.onUserStoppedLocked(userId);
+ }
}
void systemServiceManagerCleanupUser(int userId) {
@@ -1916,9 +1959,11 @@
}.sendNext();
}
- void activityManagerForceStopPackageLocked(int userId, String reason) {
- mService.forceStopPackageLocked(null, -1, false, false, true, false, false,
- userId, reason);
+ void activityManagerForceStopPackage(int userId, String reason) {
+ synchronized (mService) {
+ mService.forceStopPackageLocked(null, -1, false, false, true, false, false,
+ userId, reason);
+ }
};
int checkComponentPermission(String permission, int pid, int uid, int owningUid,
@@ -1926,20 +1971,28 @@
return mService.checkComponentPermission(permission, pid, uid, owningUid, exported);
}
- void startHomeActivityLocked(int userId, String reason) {
- mService.startHomeActivityLocked(userId, reason);
+ protected void startHomeActivity(int userId, String reason) {
+ synchronized (mService) {
+ mService.startHomeActivityLocked(userId, reason);
+ }
}
- void updateUserConfigurationLocked() {
- mService.updateUserConfigurationLocked();
+ void updateUserConfiguration() {
+ synchronized (mService) {
+ mService.updateUserConfigurationLocked();
+ }
}
- void clearBroadcastQueueForUserLocked(int userId) {
- mService.clearBroadcastQueueForUserLocked(userId);
+ void clearBroadcastQueueForUser(int userId) {
+ synchronized (mService) {
+ mService.clearBroadcastQueueForUserLocked(userId);
+ }
}
- void loadUserRecentsLocked(int userId) {
- mService.mRecentTasks.loadUserRecentsLocked(userId);
+ void loadUserRecents(int userId) {
+ synchronized (mService) {
+ mService.mRecentTasks.loadUserRecentsLocked(userId);
+ }
}
void startPersistentApps(int matchFlags) {
@@ -1956,12 +2009,28 @@
d.show();
}
- ActivityStackSupervisor getActivityStackSupervisor() {
- return mService.mStackSupervisor;
+ void stackSupervisorRemoveUser(int userId) {
+ synchronized (mService) {
+ mService.mStackSupervisor.removeUserLocked(userId);
+ }
}
- LockTaskController getLockTaskController() {
- return mService.mLockTaskController;
+ protected boolean stackSupervisorSwitchUser(int userId, UserState uss) {
+ synchronized (mService) {
+ return mService.mStackSupervisor.switchUserLocked(userId, uss);
+ }
+ }
+
+ protected void stackSupervisorResumeFocusedStackTopActivity() {
+ synchronized (mService) {
+ mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ }
+ }
+
+ protected void clearLockTaskMode(String reason) {
+ synchronized (mService) {
+ mService.mLockTaskController.clearLockTaskMode(reason);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index 2e27387..d36d9cb 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -59,8 +59,10 @@
/**
* The last time that a provider was reported to usage stats as being brought to important
* foreground procstate.
+ * <p><strong>Important: </strong>Only access this field when holding ActivityManagerService
+ * lock.
*/
- public final ArrayMap<String,Long> mProviderLastReportedFg = new ArrayMap<>();
+ final ArrayMap<String,Long> mProviderLastReportedFg = new ArrayMap<>();
public UserState(UserHandle handle) {
mHandle = handle;
diff --git a/services/core/java/com/android/server/audio/AudioEventLogger.java b/services/core/java/com/android/server/audio/AudioEventLogger.java
index c96138f..9ebd75b 100644
--- a/services/core/java/com/android/server/audio/AudioEventLogger.java
+++ b/services/core/java/com/android/server/audio/AudioEventLogger.java
@@ -16,6 +16,8 @@
package com.android.server.audio;
+import android.util.Log;
+
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -47,6 +49,22 @@
}
/**
+ * Causes the string message for the event to appear in the logcat.
+ * Here is an example of how to create a new event (a StringEvent), adding it to the logger
+ * (an instance of AudioEventLogger) while also making it show in the logcat:
+ * <pre>
+ * myLogger.log(
+ * (new StringEvent("something for logcat and logger")).printLog(MyClass.TAG) );
+ * </pre>
+ * @param tag the tag for the android.util.Log.v
+ * @return the same instance of the event
+ */
+ public Event printLog(String tag) {
+ Log.i(tag, eventToString());
+ return this;
+ }
+
+ /**
* Convert event to String.
* This method is only called when the logger history is about to the dumped,
* so this method is where expensive String conversions should be made, not when the Event
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 11d0470..5eb2a8d 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -461,6 +461,8 @@
// Forced device usage for communications
private int mForcedUseForComm;
+ private int mForcedUseForCommExt; // External state returned by getters: always consistent
+ // with requests by setters
// List of binder death handlers for setMode() client processes.
// The last process to have called setMode() is at the top of the list.
@@ -749,6 +751,9 @@
// relies on audio policy having correct ranges for volume indexes.
mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
+ mPlaybackMonitor =
+ new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]);
+
mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
mRecordMonitor = new RecordingActivityMonitor(mContext);
@@ -2544,13 +2549,15 @@
}
}
int status = AudioSystem.AUDIO_STATUS_OK;
+ int actualMode;
do {
+ actualMode = mode;
if (mode == AudioSystem.MODE_NORMAL) {
// get new mode from client at top the list if any
if (!mSetModeDeathHandlers.isEmpty()) {
hdlr = mSetModeDeathHandlers.get(0);
cb = hdlr.getBinder();
- mode = hdlr.getMode();
+ actualMode = hdlr.getMode();
if (DEBUG_MODE) {
Log.w(TAG, " using mode=" + mode + " instead due to death hdlr at pid="
+ hdlr.mPid);
@@ -2574,12 +2581,11 @@
hdlr.setMode(mode);
}
- if (mode != mMode) {
- status = AudioSystem.setPhoneState(mode);
+ if (actualMode != mMode) {
+ status = AudioSystem.setPhoneState(actualMode);
if (status == AudioSystem.AUDIO_STATUS_OK) {
- if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + mode); }
- mMode = mode;
- mModeLogger.log(new PhoneStateEvent(caller, pid, mode));
+ if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); }
+ mMode = actualMode;
} else {
if (hdlr != null) {
mSetModeDeathHandlers.remove(hdlr);
@@ -2595,13 +2601,16 @@
} while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
if (status == AudioSystem.AUDIO_STATUS_OK) {
- if (mode != AudioSystem.MODE_NORMAL) {
+ if (actualMode != AudioSystem.MODE_NORMAL) {
if (mSetModeDeathHandlers.isEmpty()) {
Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
} else {
newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
}
}
+ // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
+ mModeLogger.log(
+ new PhoneStateEvent(caller, pid, mode, newModeOwnerPid, actualMode));
int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
int device = getDeviceForStream(streamType);
int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
@@ -2890,13 +2899,14 @@
mForcedUseForComm = AudioSystem.FORCE_NONE;
}
+ mForcedUseForCommExt = mForcedUseForComm;
sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource, 0);
}
/** @see AudioManager#isSpeakerphoneOn() */
public boolean isSpeakerphoneOn() {
- return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
+ return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
}
/** @see AudioManager#setBluetoothScoOn(boolean) */
@@ -2904,6 +2914,13 @@
if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
return;
}
+
+ // Only enable calls from system components
+ if (Binder.getCallingUid() >= FIRST_APPLICATION_UID) {
+ mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
+ return;
+ }
+
// for logging only
final String eventSource = new StringBuilder("setBluetoothScoOn(").append(on)
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
@@ -2913,11 +2930,21 @@
public void setBluetoothScoOnInt(boolean on, String eventSource) {
if (on) {
+ // do not accept SCO ON if SCO audio is not connected
+ synchronized(mScoClients) {
+ if ((mBluetoothHeadset != null) &&
+ (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
+ != BluetoothHeadset.STATE_AUDIO_CONNECTED)) {
+ mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
+ return;
+ }
+ }
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
} else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
mForcedUseForComm = AudioSystem.FORCE_NONE;
}
-
+ mForcedUseForCommExt = mForcedUseForComm;
+ AudioSystem.setParameters("BT_SCO="+ (on ? "on" : "off"));
sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource, 0);
sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
@@ -2926,7 +2953,7 @@
/** @see AudioManager#isBluetoothScoOn() */
public boolean isBluetoothScoOn() {
- return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
+ return (mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO);
}
/** @see AudioManager#setBluetoothA2dpOn(boolean) */
@@ -4134,7 +4161,8 @@
newDevice, AudioSystem.getOutputDeviceName(newDevice)));
}
synchronized (mConnectedDevices) {
- if ((newDevice & DEVICE_MEDIA_UNMUTED_ON_PLUG) != 0
+ if (mNm.getZenMode() != Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
+ && (newDevice & DEVICE_MEDIA_UNMUTED_ON_PLUG) != 0
&& mStreamStates[AudioSystem.STREAM_MUSIC].mIsMuted
&& mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(newDevice) != 0
&& (newDevice & AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0)
@@ -6952,7 +6980,7 @@
//======================
// Audio playback notification
//======================
- private final PlaybackActivityMonitor mPlaybackMonitor = new PlaybackActivityMonitor();
+ private final PlaybackActivityMonitor mPlaybackMonitor;
public void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb) {
final boolean isPrivileged =
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 634c8c2..9d9e35b 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -26,20 +26,27 @@
final static class PhoneStateEvent extends AudioEventLogger.Event {
final String mPackage;
- final int mPid;
- final int mMode;
+ final int mOwnerPid;
+ final int mRequesterPid;
+ final int mRequestedMode;
+ final int mActualMode;
- PhoneStateEvent(String callingPackage, int pid, int mode) {
+ PhoneStateEvent(String callingPackage, int requesterPid, int requestedMode,
+ int ownerPid, int actualMode) {
mPackage = callingPackage;
- mPid = pid;
- mMode = mode;
+ mRequesterPid = requesterPid;
+ mRequestedMode = requestedMode;
+ mOwnerPid = ownerPid;
+ mActualMode = actualMode;
}
@Override
public String eventToString() {
- return new StringBuilder("setMode(").append(AudioSystem.modeToString(mMode))
+ return new StringBuilder("setMode(").append(AudioSystem.modeToString(mRequestedMode))
.append(") from package=").append(mPackage)
- .append(" pid=").append(mPid).toString();
+ .append(" pid=").append(mRequesterPid)
+ .append(" selected mode=").append(AudioSystem.modeToString(mActualMode))
+ .append(" by pid=").append(mOwnerPid).toString();
}
}
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 7d742ff..c5f563c7 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -89,6 +89,9 @@
pw.println("\nMediaFocusControl dump time: "
+ DateFormat.getTimeInstance().format(new Date()));
dumpFocusStack(pw);
+ pw.println("\n");
+ // log
+ mEventLogger.dump(pw);
}
//=================================================================
@@ -120,6 +123,14 @@
private final static Object mAudioFocusLock = new Object();
/**
+ * Arbitrary maximum size of audio focus stack to prevent apps OOM'ing this process.
+ */
+ private static final int MAX_STACK_SIZE = 100;
+
+ private static final AudioEventLogger mEventLogger = new AudioEventLogger(50,
+ "focus commands as seen by MediaFocusControl");
+
+ /**
* Discard the current audio focus owner.
* Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
* focus), remove it from the stack, and clear the remote control display.
@@ -643,11 +654,14 @@
protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
int sdk) {
- Log.i(TAG, " AudioFocus requestAudioFocus() from uid/pid " + Binder.getCallingUid()
- + "/" + Binder.getCallingPid()
- + " clientId=" + clientId
- + " req=" + focusChangeHint
- + " flags=0x" + Integer.toHexString(flags));
+ mEventLogger.log((new AudioEventLogger.StringEvent(
+ "requestAudioFocus() from uid/pid " + Binder.getCallingUid()
+ + "/" + Binder.getCallingPid()
+ + " clientId=" + clientId + " callingPack=" + callingPackageName
+ + " req=" + focusChangeHint
+ + " flags=0x" + Integer.toHexString(flags)
+ + " sdk=" + sdk))
+ .printLog(TAG));
// we need a valid binder callback for clients
if (!cb.pingBinder()) {
Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
@@ -660,6 +674,11 @@
}
synchronized(mAudioFocusLock) {
+ if (mFocusStack.size() > MAX_STACK_SIZE) {
+ Log.e(TAG, "Max AudioFocus stack size reached, failing requestAudioFocus()");
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+
boolean enteringRingOrCall = !mRingOrCallActive
& (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
if (enteringRingOrCall) { mRingOrCallActive = true; }
@@ -770,10 +789,12 @@
* */
protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa,
String callingPackageName) {
- // AudioAttributes are currently ignored, to be used for zones
- Log.i(TAG, " AudioFocus abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
- + "/" + Binder.getCallingPid()
- + " clientId=" + clientId);
+ // AudioAttributes are currently ignored, to be used for zones / a11y
+ mEventLogger.log((new AudioEventLogger.StringEvent(
+ "abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
+ + "/" + Binder.getCallingPid()
+ + " clientId=" + clientId))
+ .printLog(TAG));
try {
// this will take care of notifying the new focus owner if needed
synchronized(mAudioFocusLock) {
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index d1a37af..6506cf7 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -17,6 +17,8 @@
package com.android.server.audio;
import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
@@ -90,7 +92,14 @@
private final HashMap<Integer, AudioPlaybackConfiguration> mPlayers =
new HashMap<Integer, AudioPlaybackConfiguration>();
- PlaybackActivityMonitor() {
+ private final Context mContext;
+ private int mSavedAlarmVolume = -1;
+ private final int mMaxAlarmVolume;
+ private int mPrivilegedAlarmActiveCount = 0;
+
+ PlaybackActivityMonitor(Context context, int maxAlarmVolume) {
+ mContext = context;
+ mMaxAlarmVolume = maxAlarmVolume;
PlayMonitorClient.sListenerDeathMonitor = this;
AudioPlaybackConfiguration.sPlayerDeathMonitor = this;
}
@@ -105,7 +114,7 @@
if (index >= 0) {
if (!disable) {
if (DEBUG) { // hidden behind DEBUG, too noisy otherwise
- mEventLogger.log(new AudioEventLogger.StringEvent("unbanning uid:" + uid));
+ sEventLogger.log(new AudioEventLogger.StringEvent("unbanning uid:" + uid));
}
mBannedUids.remove(index);
// nothing else to do, future playback requests from this uid are ok
@@ -116,7 +125,7 @@
checkBanPlayer(apc, uid);
}
if (DEBUG) { // hidden behind DEBUG, too noisy otherwise
- mEventLogger.log(new AudioEventLogger.StringEvent("banning uid:" + uid));
+ sEventLogger.log(new AudioEventLogger.StringEvent("banning uid:" + uid));
}
mBannedUids.add(new Integer(uid));
} // no else to handle, uid already not in list, so enabling again is no-op
@@ -151,7 +160,7 @@
new AudioPlaybackConfiguration(pic, newPiid,
Binder.getCallingUid(), Binder.getCallingPid());
apc.init();
- mEventLogger.log(new NewPlayerEvent(apc));
+ sEventLogger.log(new NewPlayerEvent(apc));
synchronized(mPlayerLock) {
mPlayers.put(newPiid, apc);
}
@@ -163,7 +172,7 @@
synchronized(mPlayerLock) {
final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
if (checkConfigurationCaller(piid, apc, binderUid)) {
- mEventLogger.log(new AudioAttrEvent(piid, attr));
+ sEventLogger.log(new AudioAttrEvent(piid, attr));
change = apc.handleAudioAttributesEvent(attr);
} else {
Log.e(TAG, "Error updating audio attributes");
@@ -175,6 +184,38 @@
}
}
+ private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) {
+ if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED ||
+ apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ if ((apc.getAudioAttributes().getAllFlags() &
+ AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0 &&
+ apc.getAudioAttributes().getUsage() == AudioAttributes.USAGE_ALARM &&
+ mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE,
+ apc.getClientPid(), apc.getClientUid()) ==
+ PackageManager.PERMISSION_GRANTED) {
+ if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED &&
+ apc.getPlayerState() != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ if (mPrivilegedAlarmActiveCount++ == 0) {
+ mSavedAlarmVolume = AudioSystem.getStreamVolumeIndex(
+ AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER);
+ AudioSystem.setStreamVolumeIndex(AudioSystem.STREAM_ALARM,
+ mMaxAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER);
+ }
+ } else if (event != AudioPlaybackConfiguration.PLAYER_STATE_STARTED &&
+ apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ if (--mPrivilegedAlarmActiveCount == 0) {
+ if (AudioSystem.getStreamVolumeIndex(
+ AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER) ==
+ mMaxAlarmVolume) {
+ AudioSystem.setStreamVolumeIndex(AudioSystem.STREAM_ALARM,
+ mSavedAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER);
+ }
+ }
+ }
+ }
+ }
+ }
+
public void playerEvent(int piid, int event, int binderUid) {
if (DEBUG) { Log.v(TAG, String.format("playerEvent(piid=%d, event=%d)", piid, event)); }
final boolean change;
@@ -183,12 +224,12 @@
if (apc == null) {
return;
}
- mEventLogger.log(new PlayerEvent(piid, event));
+ sEventLogger.log(new PlayerEvent(piid, event));
if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
for (Integer uidInteger: mBannedUids) {
if (checkBanPlayer(apc, uidInteger.intValue())) {
// player was banned, do not update its state
- mEventLogger.log(new AudioEventLogger.StringEvent(
+ sEventLogger.log(new AudioEventLogger.StringEvent(
"not starting piid:" + piid + " ,is banned"));
return;
}
@@ -200,6 +241,7 @@
}
if (checkConfigurationCaller(piid, apc, binderUid)) {
//TODO add generation counter to only update to the latest state
+ checkVolumeForPrivilegedAlarm(apc, event);
change = apc.handleStateEvent(event);
} else {
Log.e(TAG, "Error handling event " + event);
@@ -216,7 +258,7 @@
public void playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio, int binderUid) {
// no check on UID yet because this is only for logging at the moment
- mEventLogger.log(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid));
+ sEventLogger.log(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid));
}
public void releasePlayer(int piid, int binderUid) {
@@ -224,10 +266,11 @@
synchronized(mPlayerLock) {
final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
if (checkConfigurationCaller(piid, apc, binderUid)) {
- mEventLogger.log(new AudioEventLogger.StringEvent(
+ sEventLogger.log(new AudioEventLogger.StringEvent(
"releasing player piid:" + piid));
mPlayers.remove(new Integer(piid));
mDuckingManager.removeReleased(apc);
+ checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
}
}
@@ -278,7 +321,7 @@
}
pw.println("\n");
// log
- mEventLogger.dump(pw);
+ sEventLogger.dump(pw);
}
}
@@ -456,7 +499,8 @@
}
if (mute) {
try {
- Log.v(TAG, "call: muting player" + piid + " uid:" + apc.getClientUid());
+ sEventLogger.log((new AudioEventLogger.StringEvent("call: muting piid:"
+ + piid + " uid:" + apc.getClientUid())).printLog(TAG));
apc.getPlayerProxy().setVolume(0.0f);
mMutedPlayers.add(new Integer(piid));
} catch (Exception e) {
@@ -480,7 +524,8 @@
final AudioPlaybackConfiguration apc = mPlayers.get(piid);
if (apc != null) {
try {
- Log.v(TAG, "call: unmuting player" + piid + " uid:" + apc.getClientUid());
+ sEventLogger.log(new AudioEventLogger.StringEvent("call: unmuting piid:"
+ + piid).printLog(TAG));
apc.getPlayerProxy().setVolume(1.0f);
} catch (Exception e) {
Log.e(TAG, "call: error unmuting player " + piid + " uid:"
@@ -669,8 +714,7 @@
return;
}
try {
- Log.v(TAG, "ducking (skipRamp=" + skipRamp + ") player piid:"
- + apc.getPlayerInterfaceId() + " uid:" + mUid);
+ sEventLogger.log((new DuckEvent(apc, skipRamp)).printLog(TAG));
apc.getPlayerProxy().applyVolumeShaper(
DUCK_VSHAPE,
skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
@@ -685,7 +729,8 @@
final AudioPlaybackConfiguration apc = players.get(piid);
if (apc != null) {
try {
- Log.v(TAG, "unducking player " + piid + " uid:" + mUid);
+ sEventLogger.log((new AudioEventLogger.StringEvent("unducking piid:"
+ + piid)).printLog(TAG));
apc.getPlayerProxy().applyVolumeShaper(
DUCK_ID,
VolumeShaper.Operation.REVERSE);
@@ -772,7 +817,28 @@
}
}
- private final static class AudioAttrEvent extends AudioEventLogger.Event {
+ private static final class DuckEvent extends AudioEventLogger.Event {
+ private final int mPlayerIId;
+ private final boolean mSkipRamp;
+ private final int mClientUid;
+ private final int mClientPid;
+
+ DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
+ mPlayerIId = apc.getPlayerInterfaceId();
+ mSkipRamp = skipRamp;
+ mClientUid = apc.getClientUid();
+ mClientPid = apc.getClientPid();
+ }
+
+ @Override
+ public String eventToString() {
+ return new StringBuilder("ducking player piid:").append(mPlayerIId)
+ .append(" uid/pid:").append(mClientUid).append("/").append(mClientPid)
+ .append(" skip ramp:").append(mSkipRamp).toString();
+ }
+ }
+
+ private static final class AudioAttrEvent extends AudioEventLogger.Event {
private final int mPlayerIId;
private final AudioAttributes mPlayerAttr;
@@ -787,6 +853,6 @@
}
}
- private final AudioEventLogger mEventLogger = new AudioEventLogger(100,
+ private static final AudioEventLogger sEventLogger = new AudioEventLogger(100,
"playback activity as reported through PlayerBase");
}
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index 475d786..f2445fa 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -34,6 +34,7 @@
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.RingBuffer;
import com.android.internal.util.TokenBucket;
import com.android.server.SystemService;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
@@ -44,7 +45,11 @@
import java.util.List;
import java.util.function.ToIntFunction;
-/** {@hide} */
+/**
+ * Event buffering service for core networking and connectivity metrics.
+ *
+ * {@hide}
+ */
final public class IpConnectivityMetrics extends SystemService {
private static final String TAG = IpConnectivityMetrics.class.getSimpleName();
private static final boolean DBG = false;
@@ -58,7 +63,10 @@
private static final String SERVICE_NAME = IpConnectivityLog.SERVICE_NAME;
- // Default size of the event buffer. Once the buffer is full, incoming events are dropped.
+ // Default size of the event rolling log for bug report dumps.
+ private static final int DEFAULT_LOG_SIZE = 500;
+ // Default size of the event buffer for metrics reporting.
+ // Once the buffer is full, incoming events are dropped.
private static final int DEFAULT_BUFFER_SIZE = 2000;
// Maximum size of the event buffer.
private static final int MAXIMUM_BUFFER_SIZE = DEFAULT_BUFFER_SIZE * 10;
@@ -67,24 +75,38 @@
private static final int ERROR_RATE_LIMITED = -1;
- // Lock ensuring that concurrent manipulations of the event buffer are correct.
+ // Lock ensuring that concurrent manipulations of the event buffers are correct.
// There are three concurrent operations to synchronize:
// - appending events to the buffer.
// - iterating throught the buffer.
// - flushing the buffer content and replacing it by a new buffer.
private final Object mLock = new Object();
+ // Implementation instance of IIpConnectivityMetrics.aidl.
@VisibleForTesting
public final Impl impl = new Impl();
+ // Subservice listening to Netd events via INetdEventListener.aidl.
@VisibleForTesting
NetdEventListenerService mNetdListener;
+ // Rolling log of the most recent events. This log is used for dumping
+ // connectivity events in bug reports.
+ @GuardedBy("mLock")
+ private final RingBuffer<ConnectivityMetricsEvent> mEventLog =
+ new RingBuffer(ConnectivityMetricsEvent.class, DEFAULT_LOG_SIZE);
+ // Buffer of connectivity events used for metrics reporting. This buffer
+ // does not rotate automatically and instead saturates when it becomes full.
+ // It is flushed at metrics reporting.
@GuardedBy("mLock")
private ArrayList<ConnectivityMetricsEvent> mBuffer;
+ // Total number of events dropped from mBuffer since last metrics reporting.
@GuardedBy("mLock")
private int mDropped;
+ // Capacity of mBuffer
@GuardedBy("mLock")
private int mCapacity;
+ // A list of rate limiting counters keyed by connectivity event types for
+ // metrics reporting mBuffer.
@GuardedBy("mLock")
private final ArrayMap<Class<?>, TokenBucket> mBuckets = makeRateLimitingBuckets();
@@ -132,6 +154,7 @@
private int append(ConnectivityMetricsEvent event) {
if (DBG) Log.d(TAG, "logEvent: " + event);
synchronized (mLock) {
+ mEventLog.append(event);
final int left = mCapacity - mBuffer.size();
if (event == null) {
return left;
@@ -216,6 +239,23 @@
}
}
+ /**
+ * Prints for bug reports the content of the rolling event log and the
+ * content of Netd event listener.
+ */
+ private void cmdDumpsys(FileDescriptor fd, PrintWriter pw, String[] args) {
+ final ConnectivityMetricsEvent[] events;
+ synchronized (mLock) {
+ events = mEventLog.toArray();
+ }
+ for (ConnectivityMetricsEvent ev : events) {
+ pw.println(ev.toString());
+ }
+ if (mNetdListener != null) {
+ mNetdListener.list(pw);
+ }
+ }
+
private void cmdStats(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mLock) {
pw.println("Buffered events: " + mBuffer.size());
@@ -258,7 +298,8 @@
cmdFlush(fd, pw, args);
return;
case CMD_DUMPSYS:
- // Fallthrough to CMD_LIST when dumpsys.cpp dumps services states (bug reports)
+ cmdDumpsys(fd, pw, args);
+ return;
case CMD_LIST:
cmdList(fd, pw, args);
return;
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index e6585ad..fceacba 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -20,6 +20,7 @@
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.NetworkInfo;
import android.net.RouteInfo;
import android.os.INetworkManagementService;
import android.os.RemoteException;
@@ -44,12 +45,18 @@
// This must match the interface prefix in clatd.c.
private static final String CLAT_PREFIX = "v4-";
- // The network types we will start clatd on,
+ // The network types on which we will start clatd,
// allowing clat only on networks for which we can support IPv6-only.
private static final int[] NETWORK_TYPES = {
- ConnectivityManager.TYPE_MOBILE,
- ConnectivityManager.TYPE_WIFI,
- ConnectivityManager.TYPE_ETHERNET,
+ ConnectivityManager.TYPE_MOBILE,
+ ConnectivityManager.TYPE_WIFI,
+ ConnectivityManager.TYPE_ETHERNET,
+ };
+
+ // The network states in which running clatd is supported.
+ private static final NetworkInfo.State[] NETWORK_STATES = {
+ NetworkInfo.State.CONNECTED,
+ NetworkInfo.State.SUSPENDED,
};
private final INetworkManagementService mNMService;
@@ -81,11 +88,8 @@
*/
public static boolean requiresClat(NetworkAgentInfo nai) {
// TODO: migrate to NetworkCapabilities.TRANSPORT_*.
- final int netType = nai.networkInfo.getType();
final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType());
- // TODO: this should also consider if the network is in SUSPENDED state to avoid stopping
- // clatd in SUSPENDED state.
- final boolean connected = nai.networkInfo.isConnected();
+ final boolean connected = ArrayUtils.contains(NETWORK_STATES, nai.networkInfo.getState());
// We only run clat on networks that don't have a native IPv4 address.
final boolean hasIPv4Address =
(nai.linkProperties != null) && nai.linkProperties.hasIPv4Address();
@@ -148,7 +152,6 @@
* turn ND offload off if on WiFi.
*/
private void enterRunningState() {
- maybeSetIpv6NdOffload(mBaseIface, false);
mState = State.RUNNING;
}
@@ -156,10 +159,6 @@
* Stop clatd, and turn ND offload on if it had been turned off.
*/
private void enterStoppingState() {
- if (isRunning()) {
- maybeSetIpv6NdOffload(mBaseIface, true);
- }
-
try {
mNMService.stopClatd(mBaseIface);
} catch(RemoteException|IllegalStateException e) {
@@ -275,19 +274,6 @@
}
}
- private void maybeSetIpv6NdOffload(String iface, boolean on) {
- // TODO: migrate to NetworkCapabilities.TRANSPORT_*.
- if (mNetwork.networkInfo.getType() != ConnectivityManager.TYPE_WIFI) {
- return;
- }
- try {
- Slog.d(TAG, (on ? "En" : "Dis") + "abling ND offload on " + iface);
- mNMService.setInterfaceIpv6NdOffload(iface, on);
- } catch(RemoteException|IllegalStateException e) {
- Slog.w(TAG, "Changing IPv6 ND offload on " + iface + "failed: " + e);
- }
- }
-
/**
* Adds stacked link on base link and transitions to RUNNING state.
*/
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 25dba35..6206dfc 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -38,6 +38,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.BitUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.RingBuffer;
import com.android.internal.util.TokenBucket;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import java.io.PrintWriter;
@@ -82,9 +83,8 @@
private final ArrayMap<String, WakeupStats> mWakeupStats = new ArrayMap<>();
// Ring buffer array for storing packet wake up events sent by Netd.
@GuardedBy("this")
- private final WakeupEvent[] mWakeupEvents = new WakeupEvent[WAKEUP_EVENT_BUFFER_LENGTH];
- @GuardedBy("this")
- private long mWakeupEventCursor = 0;
+ private final RingBuffer<WakeupEvent> mWakeupEvents =
+ new RingBuffer(WakeupEvent.class, WAKEUP_EVENT_BUFFER_LENGTH);
private final ConnectivityManager mCm;
@@ -175,13 +175,11 @@
@GuardedBy("this")
private void addWakeupEvent(String iface, long timestampMs, int uid) {
- int index = wakeupEventIndex(mWakeupEventCursor);
- mWakeupEventCursor++;
WakeupEvent event = new WakeupEvent();
event.iface = iface;
event.timestampMs = timestampMs;
event.uid = uid;
- mWakeupEvents[index] = event;
+ mWakeupEvents.append(event);
WakeupStats stats = mWakeupStats.get(iface);
if (stats == null) {
stats = new WakeupStats(iface);
@@ -190,23 +188,6 @@
stats.countEvent(event);
}
- @GuardedBy("this")
- private WakeupEvent[] getWakeupEvents() {
- int length = (int) Math.min(mWakeupEventCursor, (long) mWakeupEvents.length);
- WakeupEvent[] out = new WakeupEvent[length];
- // Reverse iteration from youngest event to oldest event.
- long inCursor = mWakeupEventCursor - 1;
- int outIdx = out.length - 1;
- while (outIdx >= 0) {
- out[outIdx--] = mWakeupEvents[wakeupEventIndex(inCursor--)];
- }
- return out;
- }
-
- private static int wakeupEventIndex(long cursor) {
- return (int) Math.abs(cursor % WAKEUP_EVENT_BUFFER_LENGTH);
- }
-
public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
flushProtos(events, mConnectEvents, IpConnectivityEventBuilder::toProto);
flushProtos(events, mDnsEvents, IpConnectivityEventBuilder::toProto);
@@ -230,7 +211,7 @@
for (int i = 0; i < mWakeupStats.size(); i++) {
pw.println(mWakeupStats.valueAt(i));
}
- for (WakeupEvent wakeup : getWakeupEvents()) {
+ for (WakeupEvent wakeup : mWakeupEvents.toArray()) {
pw.println(wakeup);
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index 5eafe5f..cff216c 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -52,6 +52,7 @@
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -73,6 +74,8 @@
private static final String ANYIP = "0.0.0.0";
private static final ForwardedStats EMPTY_STATS = new ForwardedStats();
+ private static enum UpdateType { IF_NEEDED, FORCE };
+
private final Handler mHandler;
private final OffloadHardwareInterface mHwInterface;
private final ContentResolver mContentResolver;
@@ -185,8 +188,8 @@
updateStatsForAllUpstreams();
forceTetherStatsPoll();
// [2] (Re)Push all state.
- // TODO: computeAndPushLocalPrefixes()
- // TODO: push all downstream state.
+ computeAndPushLocalPrefixes(UpdateType.FORCE);
+ pushAllDownstreamState();
pushUpstreamParameters(null);
}
@@ -319,7 +322,7 @@
}
private boolean maybeUpdateDataLimit(String iface) {
- // setDataLimit may only be called while offload is occuring on this upstream.
+ // setDataLimit may only be called while offload is occurring on this upstream.
if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) {
return true;
}
@@ -368,15 +371,15 @@
// upstream parameters fails (probably just wait for a subsequent
// onOffloadEvent() callback to tell us offload is available again and
// then reapply all state).
- computeAndPushLocalPrefixes();
+ computeAndPushLocalPrefixes(UpdateType.IF_NEEDED);
pushUpstreamParameters(prevUpstream);
}
public void setLocalPrefixes(Set<IpPrefix> localPrefixes) {
- if (!started()) return;
-
mExemptPrefixes = localPrefixes;
- computeAndPushLocalPrefixes();
+
+ if (!started()) return;
+ computeAndPushLocalPrefixes(UpdateType.IF_NEEDED);
}
public void notifyDownstreamLinkProperties(LinkProperties lp) {
@@ -385,27 +388,38 @@
if (Objects.equals(oldLp, lp)) return;
if (!started()) return;
+ pushDownstreamState(oldLp, lp);
+ }
- final List<RouteInfo> oldRoutes = (oldLp != null) ? oldLp.getRoutes() : new ArrayList<>();
- final List<RouteInfo> newRoutes = lp.getRoutes();
+ private void pushDownstreamState(LinkProperties oldLp, LinkProperties newLp) {
+ final String ifname = newLp.getInterfaceName();
+ final List<RouteInfo> oldRoutes =
+ (oldLp != null) ? oldLp.getRoutes() : Collections.EMPTY_LIST;
+ final List<RouteInfo> newRoutes = newLp.getRoutes();
// For each old route, if not in new routes: remove.
- for (RouteInfo oldRoute : oldRoutes) {
- if (shouldIgnoreDownstreamRoute(oldRoute)) continue;
- if (!newRoutes.contains(oldRoute)) {
- mHwInterface.removeDownstreamPrefix(ifname, oldRoute.getDestination().toString());
+ for (RouteInfo ri : oldRoutes) {
+ if (shouldIgnoreDownstreamRoute(ri)) continue;
+ if (!newRoutes.contains(ri)) {
+ mHwInterface.removeDownstreamPrefix(ifname, ri.getDestination().toString());
}
}
// For each new route, if not in old routes: add.
- for (RouteInfo newRoute : newRoutes) {
- if (shouldIgnoreDownstreamRoute(newRoute)) continue;
- if (!oldRoutes.contains(newRoute)) {
- mHwInterface.addDownstreamPrefix(ifname, newRoute.getDestination().toString());
+ for (RouteInfo ri : newRoutes) {
+ if (shouldIgnoreDownstreamRoute(ri)) continue;
+ if (!oldRoutes.contains(ri)) {
+ mHwInterface.addDownstreamPrefix(ifname, ri.getDestination().toString());
}
}
}
+ private void pushAllDownstreamState() {
+ for (LinkProperties lp : mDownstreams.values()) {
+ pushDownstreamState(null, lp);
+ }
+ }
+
public void removeDownstreamInterface(String ifname) {
final LinkProperties lp = mDownstreams.remove(ifname);
if (lp == null) return;
@@ -484,10 +498,11 @@
return success;
}
- private boolean computeAndPushLocalPrefixes() {
+ private boolean computeAndPushLocalPrefixes(UpdateType how) {
+ final boolean force = (how == UpdateType.FORCE);
final Set<String> localPrefixStrs = computeLocalPrefixStrings(
mExemptPrefixes, mUpstreamLinkProperties);
- if (mLastLocalPrefixStrs.equals(localPrefixStrs)) return true;
+ if (!force && mLastLocalPrefixStrs.equals(localPrefixStrs)) return true;
mLastLocalPrefixStrs = localPrefixStrs;
return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs));
@@ -581,9 +596,10 @@
}
mNatUpdateCallbacksReceived++;
+ final String natDescription = String.format("%s (%s, %s) -> (%s, %s)",
+ protoName, srcAddr, srcPort, dstAddr, dstPort);
if (DBG) {
- mLog.log(String.format("NAT timeout update: %s (%s, %s) -> (%s, %s)",
- protoName, srcAddr, srcPort, dstAddr, dstPort));
+ mLog.log("NAT timeout update: " + natDescription);
}
final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto);
@@ -594,7 +610,7 @@
NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg);
} catch (ErrnoException e) {
mNatUpdateNetlinkErrors++;
- mLog.e("Error updating NAT conntrack entry: " + e
+ mLog.e("Error updating NAT conntrack entry >" + natDescription + "<: " + e
+ ", msg: " + NetlinkConstants.hexify(msg));
mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived);
mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 2f3b559..9cd52d7 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -46,8 +46,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ProviderInfo;
import android.content.pm.RegisteredServicesCache;
import android.content.pm.RegisteredServicesCacheListener;
@@ -143,6 +143,7 @@
private static final boolean DEBUG_ACCOUNT_ACCESS = false;
+ // Only do the check on a debuggable build.
private static final boolean ENABLE_SUSPICIOUS_CHECK = Build.IS_DEBUGGABLE;
/** Delay a sync due to local changes this long. In milliseconds */
@@ -537,9 +538,11 @@
* @return whether the device most likely has some periodic syncs.
*/
private boolean likelyHasPeriodicSyncs() {
- // STOPSHIP Remove the google specific string.
try {
- return AccountManager.get(mContext).getAccountsByType("com.google").length > 0;
+ // Each sync adapter has a daily periodic sync by default, but sync adapters can remove
+ // them by themselves. So here, we use an arbitrary threshold. If there are more than
+ // this many sync endpoints, surely one of them should have a periodic sync...
+ return mSyncStorageEngine.getAuthorityCount() >= 6;
} catch (Throwable th) {
// Just in case.
}
@@ -3775,48 +3778,10 @@
}
if (op.isPeriodic) {
mLogger.log("Removing periodic sync ", op, " for ", why);
-
- if (ENABLE_SUSPICIOUS_CHECK && isSuspiciousPeriodicSyncRemoval(op)) {
- wtfWithLog("Suspicious removal of " + op + " for " + why);
- }
}
getJobScheduler().cancel(op.jobId);
}
- private boolean isSuspiciousPeriodicSyncRemoval(SyncOperation op) {
- // STOPSHIP Remove the google specific string.
- if (!op.isPeriodic){
- return false;
- }
- boolean found = false;
- for (UserInfo user : UserManager.get(mContext).getUsers(/*excludeDying=*/ true)) {
- if (op.target.userId == user.id) {
- found = true;
- break;
- }
- }
- if (!found) {
- return false; // User is being removed, okay.
- }
- switch (op.target.provider) {
- case "gmail-ls":
- case "com.android.contacts.metadata":
- break;
- default:
- return false;
- }
- final Account account = op.target.account;
- final Account[] accounts = AccountManager.get(mContext)
- .getAccountsByTypeAsUser(account.type, UserHandle.of(op.target.userId));
- for (Account a : accounts) {
- if (a.equals(account)) {
- return true; // Account still exists. Suspicious!
- }
- }
- // Account no longer exists. Makes sense...
- return false;
- }
-
private void wtfWithLog(String message) {
Slog.wtf(TAG, message);
mLogger.log("WTF: ", message);
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 7b277c0..3591871 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -911,6 +911,12 @@
}
}
+ public int getAuthorityCount() {
+ synchronized (mAuthorities) {
+ return mAuthorities.size();
+ }
+ }
+
public AuthorityInfo getAuthority(int authorityId) {
synchronized (mAuthorities) {
return mAuthorities.get(authorityId);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index ef6de4c..fddb81b 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -98,6 +98,12 @@
public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 9;
/**
+ * Flag: This display will destroy its content on removal.
+ * @hide
+ */
+ public static final int FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 10;
+
+ /**
* Touch attachment: Display does not receive touch.
*/
public static final int TOUCH_NONE = 0;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 8756484..d61a418 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -473,17 +473,18 @@
}
// If the state change was from or to VR, then we need to tell the light
- // so that it can apply appropriate VR brightness settings. This should
- // happen prior to changing the brightness but also if there is no
- // brightness change at all.
+ // so that it can apply appropriate VR brightness settings. Also, update the
+ // brightness so the state is propogated to light.
+ boolean vrModeChange = false;
if ((state == Display.STATE_VR || currentState == Display.STATE_VR) &&
currentState != state) {
setVrMode(state == Display.STATE_VR);
+ vrModeChange = true;
}
// Apply brightness changes given that we are in a non-suspended state.
- if (brightnessChanged) {
+ if (brightnessChanged || vrModeChange) {
setDisplayBrightness(brightness);
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index addad0b..78a5407 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -238,6 +238,9 @@
// For private displays by default content is destroyed on removal.
mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT;
}
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
+ mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT;
+ }
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_PRESENTATION) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_PRESENTATION;
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index d6ab888..f86d576 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -24,6 +24,8 @@
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
+import static android.hardware.display.DisplayManager
+ .VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
import android.content.Context;
import android.hardware.display.IVirtualDisplayCallback;
@@ -363,6 +365,9 @@
if ((mFlags & VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT) != 0) {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
}
+ if ((mFlags & VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL;
+ }
mInfo.type = Display.TYPE_VIRTUAL;
mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index b1c165e..1df9c86 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -63,6 +63,8 @@
import android.service.fingerprint.FingerprintServiceDumpProto;
import android.service.fingerprint.FingerprintUserStatsProto;
import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
@@ -81,7 +83,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -89,18 +90,19 @@
/**
* A service to manage multiple clients that want to access the fingerprint HAL API.
* The service is responsible for maintaining a list of clients and dispatching all
- * fingerprint -related events.
+ * fingerprint-related events.
*
* @hide
*/
public class FingerprintService extends SystemService implements IHwBinder.DeathRecipient {
static final String TAG = "FingerprintService";
static final boolean DEBUG = true;
- private static final boolean CLEANUP_UNUSED_FP = false;
+ private static final boolean CLEANUP_UNUSED_FP = true;
private static final String FP_DATA_DIR = "fpdata";
private static final int MSG_USER_SWITCHING = 10;
private static final String ACTION_LOCKOUT_RESET =
"com.android.server.fingerprint.ACTION_LOCKOUT_RESET";
+ private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
private class PerformanceStats {
int accept; // number of accepted fingerprints
@@ -128,8 +130,8 @@
private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
private Context mContext;
private long mHalDeviceId;
- private boolean mTimedLockoutCleared;
- private int mFailedAttempts;
+ private SparseBooleanArray mTimedLockoutCleared;
+ private SparseIntArray mFailedAttempts;
@GuardedBy("this")
private IBiometricsFingerprint mDaemon;
private final PowerManager mPowerManager;
@@ -139,10 +141,8 @@
private ClientMonitor mPendingClient;
private PerformanceStats mPerformanceStats;
-
private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
- private LinkedList<Integer> mEnumeratingUserIds = new LinkedList<>();
- private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw finterprints
+ private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints
private class UserFingerprint {
Fingerprint f;
@@ -177,15 +177,17 @@
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_LOCKOUT_RESET.equals(intent.getAction())) {
- resetFailedAttempts(false /* clearAttemptCounter */);
+ final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
+ resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
}
}
};
- private final Runnable mResetFailedAttemptsRunnable = new Runnable() {
+ private final Runnable mResetFailedAttemptsForCurrentUserRunnable = new Runnable() {
@Override
public void run() {
- resetFailedAttempts(true /* clearAttemptCounter */);
+ resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ ActivityManager.getCurrentUser());
}
};
@@ -221,6 +223,8 @@
mContext.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET),
RESET_FINGERPRINT_LOCKOUT, null /* handler */);
mUserManager = UserManager.get(mContext);
+ mTimedLockoutCleared = new SparseBooleanArray();
+ mFailedAttempts = new SparseIntArray();
}
@Override
@@ -233,7 +237,7 @@
public synchronized IBiometricsFingerprint getFingerprintDaemon() {
if (mDaemon == null) {
- Slog.v(TAG, "mDeamon was null, reconnect to fingerprint");
+ Slog.v(TAG, "mDaemon was null, reconnect to fingerprint");
try {
mDaemon = IBiometricsFingerprint.getService();
} catch (java.util.NoSuchElementException e) {
@@ -259,7 +263,7 @@
if (mHalDeviceId != 0) {
loadAuthenticatorIds();
updateActiveGroup(ActivityManager.getCurrentUser(), null);
- doFingerprintCleanup(ActivityManager.getCurrentUser());
+ doFingerprintCleanupForUser(ActivityManager.getCurrentUser());
} else {
Slog.w(TAG, "Failed to open Fingerprint HAL!");
MetricsLogger.count(mContext, "fingerprintd_openhal_error", 1);
@@ -288,52 +292,41 @@
}
}
- private void doFingerprintCleanup(int userId) {
+ /**
+ * This method should be called upon connection to the daemon, and when user switches.
+ * @param userId
+ */
+ private void doFingerprintCleanupForUser(int userId) {
if (CLEANUP_UNUSED_FP) {
- resetEnumerateState();
- mEnumeratingUserIds.push(userId);
- enumerateNextUser();
+ enumerateUser(userId);
}
}
- private void resetEnumerateState() {
- if (DEBUG) Slog.v(TAG, "Enumerate cleaning up");
- mEnumeratingUserIds.clear();
+ private void clearEnumerateState() {
+ if (DEBUG) Slog.v(TAG, "clearEnumerateState()");
mUnknownFingerprints.clear();
}
- private void enumerateNextUser() {
- int nextUser = mEnumeratingUserIds.getFirst();
- updateActiveGroup(nextUser, null);
+ private void enumerateUser(int userId) {
+ if (DEBUG) Slog.v(TAG, "Enumerating user(" + userId + ")");
boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
-
- if (DEBUG) Slog.v(TAG, "Enumerating user id " + nextUser + " of "
- + mEnumeratingUserIds.size() + " remaining users");
-
- startEnumerate(mToken, nextUser, null, restricted, true /* internal */);
+ startEnumerate(mToken, userId, null, restricted, true /* internal */);
}
// Remove unknown fingerprints from hardware
private void cleanupUnknownFingerprints() {
if (!mUnknownFingerprints.isEmpty()) {
- Slog.w(TAG, "unknown fingerprint size: " + mUnknownFingerprints.size());
UserFingerprint uf = mUnknownFingerprints.get(0);
mUnknownFingerprints.remove(uf);
boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
- updateActiveGroup(uf.userId, null);
startRemove(mToken, uf.f.getFingerId(), uf.f.getGroupId(), uf.userId, null,
restricted, true /* internal */);
} else {
- resetEnumerateState();
+ clearEnumerateState();
}
}
protected void handleEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "Enumerate: fid=" + fingerId
- + ", gid=" + groupId
- + ", dev=" + deviceId
- + ", rem=" + remaining);
-
ClientMonitor client = mCurrentClient;
if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) {
@@ -343,24 +336,21 @@
// All fingerprints in hardware for this user were enumerated
if (remaining == 0) {
- mEnumeratingUserIds.poll();
-
if (client instanceof InternalEnumerateClient) {
- List<Fingerprint> enrolled = ((InternalEnumerateClient) client).getEnumeratedList();
- Slog.w(TAG, "Added " + enrolled.size() + " enumerated fingerprints for deletion");
- for (Fingerprint f : enrolled) {
+ List<Fingerprint> unknownFingerprints =
+ ((InternalEnumerateClient) client).getUnknownFingerprints();
+
+ if (!unknownFingerprints.isEmpty()) {
+ Slog.w(TAG, "Adding " + unknownFingerprints.size() +
+ " fingerprints for deletion");
+ }
+ for (Fingerprint f : unknownFingerprints) {
mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId()));
}
- }
-
- removeClient(client);
-
- if (!mEnumeratingUserIds.isEmpty()) {
- enumerateNextUser();
- } else if (client instanceof InternalEnumerateClient) {
- if (DEBUG) Slog.v(TAG, "Finished enumerating all users");
- // This will start a chain of InternalRemovalClients
+ removeClient(client);
cleanupUnknownFingerprints();
+ } else {
+ removeClient(client);
}
}
}
@@ -368,7 +358,7 @@
protected void handleError(long deviceId, int error, int vendorCode) {
ClientMonitor client = mCurrentClient;
if (client instanceof InternalRemovalClient || client instanceof InternalEnumerateClient) {
- resetEnumerateState();
+ clearEnumerateState();
}
if (client != null && client.onError(error, vendorCode)) {
removeClient(client);
@@ -412,7 +402,7 @@
if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
cleanupUnknownFingerprints();
} else if (client instanceof InternalRemovalClient){
- resetEnumerateState();
+ clearEnumerateState();
}
}
@@ -466,8 +456,14 @@
}
void handleUserSwitching(int userId) {
+ if (mCurrentClient instanceof InternalRemovalClient
+ || mCurrentClient instanceof InternalEnumerateClient) {
+ Slog.w(TAG, "User switched while performing cleanup");
+ removeClient(mCurrentClient);
+ clearEnumerateState();
+ }
updateActiveGroup(userId, null);
- doFingerprintCleanup(userId);
+ doFingerprintCleanupForUser(userId);
}
private void removeClient(ClientMonitor client) {
@@ -488,27 +484,32 @@
}
private int getLockoutMode() {
- if (mFailedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
+ final int currentUser = ActivityManager.getCurrentUser();
+ final int failedAttempts = mFailedAttempts.get(currentUser, 0);
+ if (failedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
return AuthenticationClient.LOCKOUT_PERMANENT;
- } else if (mFailedAttempts > 0 && mTimedLockoutCleared == false &&
- (mFailedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
+ } else if (failedAttempts > 0 &&
+ mTimedLockoutCleared.get(currentUser, false) == false
+ && (failedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
return AuthenticationClient.LOCKOUT_TIMED;
}
return AuthenticationClient.LOCKOUT_NONE;
}
- private void scheduleLockoutReset() {
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS, getLockoutResetIntent());
+ private void scheduleLockoutResetForUser(int userId) {
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
+ getLockoutResetIntentForUser(userId));
}
- private void cancelLockoutReset() {
- mAlarmManager.cancel(getLockoutResetIntent());
+ private void cancelLockoutResetForUser(int userId) {
+ mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
}
- private PendingIntent getLockoutResetIntent() {
- return PendingIntent.getBroadcast(mContext, 0,
- new Intent(ACTION_LOCKOUT_RESET), PendingIntent.FLAG_UPDATE_CURRENT);
+ private PendingIntent getLockoutResetIntentForUser(int userId) {
+ return PendingIntent.getBroadcast(mContext, userId,
+ new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId),
+ PendingIntent.FLAG_UPDATE_CURRENT);
}
public long startPreEnroll(IBinder token) {
@@ -555,6 +556,12 @@
// This condition means we're currently running internal diagnostics to
// remove extra fingerprints in the hardware and/or the software
// TODO: design an escape hatch in case client never finishes
+ if (newClient != null) {
+ Slog.w(TAG, "Internal cleanup in progress but trying to start client "
+ + newClient.getClass().getSuperclass().getSimpleName()
+ + "(" + newClient.getOwnerString() + ")"
+ + ", initiatedByClient = " + initiatedByClient);
+ }
}
else {
currentClient.stop(initiatedByClient);
@@ -567,7 +574,7 @@
if (DEBUG) Slog.v(TAG, "starting client "
+ newClient.getClass().getSuperclass().getSimpleName()
+ "(" + newClient.getOwnerString() + ")"
- + ", initiatedByClient = " + initiatedByClient + ")");
+ + ", initiatedByClient = " + initiatedByClient);
notifyClientActiveCallbacks(true);
newClient.start();
@@ -813,8 +820,9 @@
receiver, mCurrentUserId, groupId, opId, restricted, opPackageName) {
@Override
public int handleFailedAttempt() {
- mFailedAttempts++;
- mTimedLockoutCleared = false;
+ final int currentUser = ActivityManager.getCurrentUser();
+ mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
+ mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
final int lockoutMode = getLockoutMode();
if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
mPerformanceStats.permanentLockout++;
@@ -824,7 +832,7 @@
// Failing multiple times will continue to push out the lockout time
if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
- scheduleLockoutReset();
+ scheduleLockoutResetForUser(currentUser);
return lockoutMode;
}
return AuthenticationClient.LOCKOUT_NONE;
@@ -832,7 +840,8 @@
@Override
public void resetFailedAttempts() {
- FingerprintService.this.resetFailedAttempts(true /* clearAttemptCounter */);
+ FingerprintService.this.resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ ActivityManager.getCurrentUser());
}
@Override
@@ -886,17 +895,17 @@
// attempt counter should only be cleared when Keyguard goes away or when
// a fingerprint is successfully authenticated
- protected void resetFailedAttempts(boolean clearAttemptCounter) {
+ protected void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
Slog.v(TAG, "Reset fingerprint lockout, clearAttemptCounter=" + clearAttemptCounter);
}
if (clearAttemptCounter) {
- mFailedAttempts = 0;
+ mFailedAttempts.put(userId, 0);
}
- mTimedLockoutCleared = true;
+ mTimedLockoutCleared.put(userId, true);
// If we're asked to reset failed attempts externally (i.e. from Keyguard),
// the alarm might still be pending; remove it.
- cancelLockoutReset();
+ cancelLockoutResetForUser(userId);
notifyLockoutResetMonitors();
}
@@ -1277,7 +1286,7 @@
public void resetTimeout(byte [] token) {
checkPermission(RESET_FINGERPRINT_LOCKOUT);
// TODO: confirm security token when we move timeout management into the HAL layer.
- mHandler.post(mResetFailedAttemptsRunnable);
+ mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
}
@Override
@@ -1338,6 +1347,8 @@
set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
+ set.put("permanentLockoutCrypto",
+ (cryptoStats != null) ? cryptoStats.permanentLockout : 0);
sets.put(set);
}
@@ -1367,7 +1378,7 @@
proto.write(FingerprintActionStatsProto.REJECT, normal.reject);
proto.write(FingerprintActionStatsProto.ACQUIRE, normal.acquire);
proto.write(FingerprintActionStatsProto.LOCKOUT, normal.lockout);
- proto.write(FingerprintActionStatsProto.LOCKOUT_PERMANENT, normal.lockout);
+ proto.write(FingerprintActionStatsProto.LOCKOUT_PERMANENT, normal.permanentLockout);
proto.end(countsToken);
}
@@ -1380,7 +1391,7 @@
proto.write(FingerprintActionStatsProto.REJECT, crypto.reject);
proto.write(FingerprintActionStatsProto.ACQUIRE, crypto.acquire);
proto.write(FingerprintActionStatsProto.LOCKOUT, crypto.lockout);
- proto.write(FingerprintActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout);
+ proto.write(FingerprintActionStatsProto.LOCKOUT_PERMANENT, crypto.permanentLockout);
proto.end(countsToken);
}
diff --git a/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java b/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
index 88d9ef4..434db98 100644
--- a/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
@@ -30,7 +30,7 @@
public abstract class InternalEnumerateClient extends EnumerateClient {
private List<Fingerprint> mEnrolledList;
- private List<Fingerprint> mEnumeratedList = new ArrayList<>(); // list of fp to delete
+ private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete
public InternalEnumerateClient(Context context, long halDeviceId, IBinder token,
IFingerprintServiceReceiver receiver, int groupId, int userId,
@@ -47,7 +47,6 @@
if (mEnrolledList.get(i).getFingerId() == fingerId) {
mEnrolledList.remove(i);
matched = true;
- Slog.e(TAG, "Matched fingerprint fid=" + fingerId);
break;
}
}
@@ -55,7 +54,7 @@
// fingerId 0 means no fingerprints are in hardware
if (!matched && fingerId != 0) {
Fingerprint fingerprint = new Fingerprint("", groupId, fingerId, getHalDeviceId());
- mEnumeratedList.add(fingerprint);
+ mUnknownFingerprints.add(fingerprint);
}
}
@@ -76,8 +75,8 @@
mEnrolledList.clear();
}
- public List<Fingerprint> getEnumeratedList() {
- return mEnumeratedList;
+ public List<Fingerprint> getUnknownFingerprints() {
+ return mUnknownFingerprints;
}
@Override
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index ac80794..78aa2f9 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -795,18 +795,22 @@
* @param uid Uid to check against for removal of a job.
*
*/
- public void cancelJobsForUid(int uid, String reason) {
+ public boolean cancelJobsForUid(int uid, String reason) {
if (uid == Process.SYSTEM_UID) {
Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
- return;
+ return false;
}
+
+ boolean jobsCanceled = false;
synchronized (mLock) {
final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
for (int i=0; i<jobsForUid.size(); i++) {
JobStatus toRemove = jobsForUid.get(i);
cancelJobImplLocked(toRemove, null, reason);
+ jobsCanceled = true;
}
}
+ return jobsCanceled;
}
/**
@@ -816,13 +820,14 @@
* @param uid Uid of the calling client.
* @param jobId Id of the job, provided at schedule-time.
*/
- public void cancelJob(int uid, int jobId) {
+ public boolean cancelJob(int uid, int jobId) {
JobStatus toCancel;
synchronized (mLock) {
toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
if (toCancel != null) {
cancelJobImplLocked(toCancel, null, "cancel() called by app");
}
+ return (toCancel != null);
}
}
@@ -2147,6 +2152,39 @@
return 0;
}
+ // Shell command infrastructure: cancel a scheduled job
+ int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
+ boolean hasJobId, int jobId) {
+ if (DEBUG) {
+ Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
+ }
+
+ int pkgUid = -1;
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ pkgUid = pm.getPackageUid(pkgName, 0, userId);
+ } catch (RemoteException e) { /* can't happen */ }
+
+ if (pkgUid < 0) {
+ pw.println("Package " + pkgName + " not found.");
+ return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
+ }
+
+ if (!hasJobId) {
+ pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
+ if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
+ pw.println("No matching jobs found.");
+ }
+ } else {
+ pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
+ if (!cancelJob(pkgUid, jobId)) {
+ pw.println("No matching job found.");
+ }
+ }
+
+ return 0;
+ }
+
void setMonitorBattery(boolean enabled) {
synchronized (mLock) {
if (mBatteryController != null) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java b/services/core/java/com/android/server/job/JobSchedulerShellCommand.java
index a53c088..d630aab 100644
--- a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/services/core/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -48,6 +48,8 @@
return runJob(pw);
case "timeout":
return timeout(pw);
+ case "cancel":
+ return cancelJob(pw);
case "monitor-battery":
return monitorBattery(pw);
case "get-battery-seq":
@@ -205,6 +207,42 @@
}
}
+ private int cancelJob(PrintWriter pw) throws Exception {
+ checkPermission("cancel jobs");
+
+ int userId = UserHandle.USER_SYSTEM;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-u":
+ case "--user":
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+
+ default:
+ pw.println("Error: unknown option '" + opt + "'");
+ return -1;
+ }
+ }
+
+ if (userId < 0) {
+ pw.println("Error: must specify a concrete user ID");
+ return -1;
+ }
+
+ final String pkgName = getNextArg();
+ final String jobIdStr = getNextArg();
+ final int jobId = jobIdStr != null ? Integer.parseInt(jobIdStr) : -1;
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mInternal.executeCancelCommand(pw, pkgName, userId, jobIdStr != null, jobId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
private int monitorBattery(PrintWriter pw) throws Exception {
checkPermission("change battery monitoring");
String opt = getNextArgRequired();
@@ -315,6 +353,12 @@
pw.println(" Options:");
pw.println(" -u or --user: specify which user's job is to be run; the default is");
pw.println(" all users");
+ pw.println(" cancel [-u | --user USER_ID] PACKAGE [JOB_ID]");
+ pw.println(" Cancel a scheduled job. If a job ID is not supplied, all jobs scheduled");
+ pw.println(" by that package will be canceled. USE WITH CAUTION.");
+ pw.println(" Options:");
+ pw.println(" -u or --user: specify which user's job is to be run; the default is");
+ pw.println(" the primary or system user");
pw.println(" monitor-battery [on|off]");
pw.println(" Control monitoring of all battery changes. Off by default. Turning");
pw.println(" on makes get-battery-seq useful.");
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 11043bd..a1a0106 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -376,7 +376,7 @@
}
public SyntheticPasswordManager getSyntheticPasswordManager(LockSettingsStorage storage) {
- return new SyntheticPasswordManager(storage, getUserManager());
+ return new SyntheticPasswordManager(getContext(), storage, getUserManager());
}
public int binderGetCallingUid() {
@@ -763,7 +763,8 @@
private void migrateOldDataAfterSystemReady() {
try {
// Migrate the FRP credential to the persistent data block
- if (LockPatternUtils.frpCredentialEnabled() && !getBoolean("migrated_frp", false, 0)) {
+ if (LockPatternUtils.frpCredentialEnabled(mContext)
+ && !getBoolean("migrated_frp", false, 0)) {
migrateFrpCredential();
setBoolean("migrated_frp", true, 0);
Slog.i(TAG, "Migrated migrated_frp.");
@@ -784,7 +785,7 @@
return;
}
for (UserInfo userInfo : mUserManager.getUsers()) {
- if (userOwnsFrpCredential(userInfo) && isUserSecure(userInfo.id)) {
+ if (userOwnsFrpCredential(mContext, userInfo) && isUserSecure(userInfo.id)) {
synchronized (mSpManager) {
if (isSyntheticPasswordBasedCredentialLocked(userInfo.id)) {
int actualQuality = (int) getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
@@ -2366,6 +2367,13 @@
Slog.w(TAG, "Invalid escrow token supplied");
return false;
}
+ if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
+ // Most likely, an untrusted credential reset happened in the past which
+ // changed the synthetic password
+ Slog.e(TAG, "Obsolete token: synthetic password derived but it fails GK "
+ + "verification.");
+ return false;
+ }
// Update PASSWORD_TYPE_KEY since it's needed by notifyActivePasswordMetricsAvailable()
// called by setLockCredentialWithAuthTokenLocked().
// TODO: refactor usage of PASSWORD_TYPE_KEY b/65239740
@@ -2497,7 +2505,7 @@
}
public void onSystemReady() {
- if (frpCredentialEnabled()) {
+ if (frpCredentialEnabled(mContext)) {
updateRegistration();
} else {
// If we don't intend to use frpCredentials and we're not provisioned yet, send
@@ -2526,7 +2534,7 @@
private void clearFrpCredentialIfOwnerNotSecure() {
List<UserInfo> users = mUserManager.getUsers();
for (UserInfo user : users) {
- if (userOwnsFrpCredential(user)) {
+ if (userOwnsFrpCredential(mContext, user)) {
if (!isUserSecure(user.id)) {
mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, user.id,
0, null);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
index 542b929..c9c9329 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
@@ -27,6 +27,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.trust.IStrongAuthTracker;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Binder;
import android.os.DeadObjectException;
@@ -74,7 +75,10 @@
}
public void systemReady() {
- mFingerprintManager = mContext.getSystemService(FingerprintManager.class);
+ final PackageManager pm = mContext.getPackageManager();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ mFingerprintManager = mContext.getSystemService(FingerprintManager.class);
+ }
}
private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) {
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 33a9a99..9440f17 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
+import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.weaver.V1_0.IWeaver;
import android.hardware.weaver.V1_0.WeaverConfig;
@@ -255,13 +256,16 @@
byte[] aggregatedSecret;
}
+ private final Context mContext;
private LockSettingsStorage mStorage;
private IWeaver mWeaver;
private WeaverConfig mWeaverConfig;
private final UserManager mUserManager;
- public SyntheticPasswordManager(LockSettingsStorage storage, UserManager userManager) {
+ public SyntheticPasswordManager(Context context, LockSettingsStorage storage,
+ UserManager userManager) {
+ mContext = context;
mStorage = storage;
mUserManager = userManager;
}
@@ -645,7 +649,7 @@
public void migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality) {
if (mStorage.getPersistentDataBlock() != null
- && LockPatternUtils.userOwnsFrpCredential(userInfo)) {
+ && LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)) {
PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle,
userInfo.id));
if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
@@ -662,7 +666,8 @@
private void synchronizeFrpPassword(PasswordData pwd,
int requestedQuality, int userId) {
if (mStorage.getPersistentDataBlock() != null
- && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) {
+ && LockPatternUtils.userOwnsFrpCredential(mContext,
+ mUserManager.getUserInfo(userId))) {
if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality,
pwd.toBytes());
@@ -675,7 +680,8 @@
private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId,
int weaverSlot) {
if (mStorage.getPersistentDataBlock() != null
- && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) {
+ && LockPatternUtils.userOwnsFrpCredential(mContext,
+ mUserManager.getUserInfo(userId))) {
if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot,
requestedQuality, pwd.toBytes());
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 3795b7f..1cfd5f0 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -271,14 +271,6 @@
// Binder call
@Override
- public boolean isGlobalBluetoothA2doOn() {
- synchronized (mLock) {
- return mGlobalBluetoothA2dpOn;
- }
- }
-
- // Binder call
- @Override
public void setDiscoveryRequest(IMediaRouterClient client,
int routeTypes, boolean activeScan) {
if (client == null) {
@@ -383,7 +375,7 @@
synchronized (mLock) {
a2dpOn = mGlobalBluetoothA2dpOn;
}
- Slog.v(TAG, "restoreBluetoothA2dp( " + a2dpOn + ")");
+ Slog.v(TAG, "restoreBluetoothA2dp(" + a2dpOn + ")");
mAudioService.setBluetoothA2dpOn(a2dpOn);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException while calling setBluetoothA2dpOn.");
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 89e1050..0b11479 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -462,18 +462,25 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- if (useSuggested) {
- if (AudioSystem.isStreamActive(stream, 0)) {
- mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction,
- flags, packageName, uid);
+ try {
+ if (useSuggested) {
+ if (AudioSystem.isStreamActive(stream, 0)) {
+ mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream,
+ direction, flags, packageName, uid);
+ } else {
+ mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
+ AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
+ flags | previousFlagPlaySound, packageName, uid);
+ }
} else {
- mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
- AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
- flags | previousFlagPlaySound, packageName, uid);
+ mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
+ packageName, uid);
}
- } else {
- mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
- packageName, uid);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream="
+ + stream + ", flags=" + flags + ", packageName=" + packageName
+ + ", uid=" + uid + ", useSuggested=" + useSuggested
+ + ", previousFlagPlaySound=" + previousFlagPlaySound, e);
}
}
});
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index b77ed91..b9a2d18 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -1363,6 +1363,10 @@
flags, packageName, TAG);
} catch (RemoteException e) {
Log.e(TAG, "Error adjusting default volume.", e);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Cannot adjust volume: direction=" + direction
+ + ", suggestedStream=" + suggestedStream + ", flags=" + flags,
+ e);
}
}
});
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 90dab2c..b4056b3 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -331,7 +331,6 @@
private static final int MSG_UPDATE_INTERFACE_QUOTA = 10;
private static final int MSG_REMOVE_INTERFACE_QUOTA = 11;
private static final int MSG_POLICIES_CHANGED = 13;
- private static final int MSG_SET_FIREWALL_RULES = 14;
private static final int MSG_RESET_FIREWALL_RULES_BY_UID = 15;
private static final int UID_MSG_STATE_CHANGED = 100;
@@ -3138,9 +3137,9 @@
uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW);
}
}
- setUidFirewallRulesAsync(chain, uidRules, CHAIN_TOGGLE_ENABLE);
+ setUidFirewallRulesUL(chain, uidRules, CHAIN_TOGGLE_ENABLE);
} else {
- setUidFirewallRulesAsync(chain, null, CHAIN_TOGGLE_DISABLE);
+ setUidFirewallRulesUL(chain, null, CHAIN_TOGGLE_DISABLE);
}
}
@@ -3207,7 +3206,7 @@
}
}
- setUidFirewallRulesAsync(FIREWALL_CHAIN_STANDBY, uidRules, CHAIN_TOGGLE_NONE);
+ setUidFirewallRulesUL(FIREWALL_CHAIN_STANDBY, uidRules, CHAIN_TOGGLE_NONE);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
@@ -3906,18 +3905,6 @@
removeInterfaceQuota((String) msg.obj);
return true;
}
- case MSG_SET_FIREWALL_RULES: {
- final int chain = msg.arg1;
- final int toggle = msg.arg2;
- final SparseIntArray uidRules = (SparseIntArray) msg.obj;
- if (uidRules != null) {
- setUidFirewallRules(chain, uidRules);
- }
- if (toggle != CHAIN_TOGGLE_NONE) {
- enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE);
- }
- return true;
- }
case MSG_RESET_FIREWALL_RULES_BY_UID: {
resetUidFirewallRules(msg.arg1);
return true;
@@ -4063,15 +4050,20 @@
/**
* Calls {@link #setUidFirewallRules(int, SparseIntArray)} and
- * {@link #enableFirewallChainUL(int, boolean)} asynchronously.
+ * {@link #enableFirewallChainUL(int, boolean)} synchronously.
*
* @param chain firewall chain.
* @param uidRules new UID rules; if {@code null}, only toggles chain state.
* @param toggle whether the chain should be enabled, disabled, or not changed.
*/
- private void setUidFirewallRulesAsync(int chain, @Nullable SparseIntArray uidRules,
+ private void setUidFirewallRulesUL(int chain, @Nullable SparseIntArray uidRules,
@ChainToggleType int toggle) {
- mHandler.obtainMessage(MSG_SET_FIREWALL_RULES, chain, toggle, uidRules).sendToTarget();
+ if (uidRules != null) {
+ setUidFirewallRulesUL(chain, uidRules);
+ }
+ if (toggle != CHAIN_TOGGLE_NONE) {
+ enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE);
+ }
}
/**
@@ -4079,7 +4071,7 @@
* here to netd. It will clean up dead rules and make sure the target chain only contains rules
* specified here.
*/
- private void setUidFirewallRules(int chain, SparseIntArray uidRules) {
+ private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) {
try {
int size = uidRules.size();
int[] uids = new int[size];
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 3444ef3..c0fbfbb 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -186,6 +186,11 @@
super.onPackagesChanged(removingPackage, pkgList, uid);
}
+ @Override
+ protected boolean isValidEntry(String packageOrComponent, int userId) {
+ return true;
+ }
+
public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
synchronized(mMutex) {
return checkServiceTokenLocked(provider);
diff --git a/services/core/java/com/android/server/notification/ImportanceExtractor.java b/services/core/java/com/android/server/notification/ImportanceExtractor.java
index 46ec92b..452121c 100644
--- a/services/core/java/com/android/server/notification/ImportanceExtractor.java
+++ b/services/core/java/com/android/server/notification/ImportanceExtractor.java
@@ -22,7 +22,7 @@
* Determines the importance of the given notification.
*/
public class ImportanceExtractor implements NotificationSignalExtractor {
- private static final String TAG = "ImportantTopicExtractor";
+ private static final String TAG = "ImportanceExtractor";
private static final boolean DBG = false;
private RankingConfig mConfig;
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index add4184..c1d7059 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -45,12 +45,16 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.service.notification.ManagedServiceInfoProto;
+import android.service.notification.ManagedServicesProto;
+import android.service.notification.ManagedServicesProto.ServiceProto;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.XmlUtils;
import com.android.server.notification.NotificationManagerService.DumpFilter;
@@ -214,6 +218,53 @@
}
}
+ public void dump(ProtoOutputStream proto, DumpFilter filter) {
+ proto.write(ManagedServicesProto.CAPTION, getCaption());
+ final int N = mApproved.size();
+ for (int i = 0 ; i < N; i++) {
+ final int userId = mApproved.keyAt(i);
+ final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i);
+ if (approvedByType != null) {
+ final int M = approvedByType.size();
+ for (int j = 0; j < M; j++) {
+ final boolean isPrimary = approvedByType.keyAt(j);
+ final ArraySet<String> approved = approvedByType.valueAt(j);
+ if (approvedByType != null && approvedByType.size() > 0) {
+ final long sToken = proto.start(ManagedServicesProto.APPROVED);
+ for (String s : approved) {
+ proto.write(ServiceProto.NAME, s);
+ }
+ proto.write(ServiceProto.USER_ID, userId);
+ proto.write(ServiceProto.IS_PRIMARY, isPrimary);
+ proto.end(sToken);
+ }
+ }
+ }
+ }
+
+ for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
+ if (filter != null && !filter.matches(cmpt)) continue;
+
+ final long cToken = proto.start(ManagedServicesProto.ENABLED);
+ cmpt.toProto(proto);
+ proto.end(cToken);
+ }
+
+ for (ManagedServiceInfo info : mServices) {
+ if (filter != null && !filter.matches(info.component)) continue;
+
+ final long lToken = proto.start(ManagedServicesProto.LIVE_SERVICES);
+ info.toProto(proto, this);
+ proto.end(lToken);
+ }
+
+ for (ComponentName name : mSnoozingForCurrentProfiles) {
+ final long cToken = proto.start(ManagedServicesProto.SNOOZED);
+ name.toProto(proto);
+ proto.end(cToken);
+ }
+ }
+
protected void onSettingRestored(String element, String value, int backupSdkInt, int userId) {
if (!mUseXml) {
Slog.d(TAG, "Restored managed service setting: " + element);
@@ -294,6 +345,7 @@
}
if (type == XmlPullParser.START_TAG) {
if (TAG_MANAGED_SERVICES.equals(tag)) {
+ Slog.i(TAG, "Read " + mConfig.caption + " permissions from xml");
final String approved = XmlUtils.readStringAttribute(parser, ATT_APPROVED_LIST);
final int userId = XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0);
final boolean isPrimary =
@@ -353,6 +405,8 @@
protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId,
boolean isPrimary, boolean enabled) {
+ Slog.i(TAG,
+ (enabled ? " Allowing " : "Disallowing ") + mConfig.caption + " " + pkgOrComponent);
ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.get(userId);
if (allowedByType == null) {
allowedByType = new ArrayMap<>();
@@ -460,6 +514,7 @@
}
public void onUserRemoved(int user) {
+ Slog.i(TAG, "Removing approved services for removed user " + user);
mApproved.remove(user);
rebindServices(true);
}
@@ -543,10 +598,8 @@
}
// State changed
- if (DEBUG) {
- Slog.d(TAG, ((enabled) ? "Enabling " : "Disabling ") + "component " +
- component.flattenToShortString());
- }
+ Slog.d(TAG, ((enabled) ? "Enabling " : "Disabling ") + "component " +
+ component.flattenToShortString());
synchronized (mMutex) {
final int[] userIds = mUserProfiles.getCurrentProfileIds();
@@ -628,12 +681,10 @@
int P = approved.size();
for (int k = P - 1; k >= 0; k--) {
final String approvedPackageOrComponent = approved.valueAt(k);
- if (!hasMatchingServices(approvedPackageOrComponent, userId)){
+ if (!isValidEntry(approvedPackageOrComponent, userId)){
approved.removeAt(k);
- if (DEBUG) {
- Slog.v(TAG, "Removing " + approvedPackageOrComponent
- + " from approved list; no matching services found");
- }
+ Slog.v(TAG, "Removing " + approvedPackageOrComponent
+ + " from approved list; no matching services found");
} else {
if (DEBUG) {
Slog.v(TAG, "Keeping " + approvedPackageOrComponent
@@ -678,6 +729,10 @@
}
}
+ protected boolean isValidEntry(String packageOrComponent, int userId) {
+ return hasMatchingServices(packageOrComponent, userId);
+ }
+
private boolean hasMatchingServices(String packageOrComponent, int userId) {
if (!TextUtils.isEmpty(packageOrComponent)) {
final String packageName = getPackageName(packageOrComponent);
@@ -830,8 +885,7 @@
if (name.equals(info.component)
&& info.userid == userid) {
// cut old connections
- if (DEBUG) Slog.v(TAG, " disconnecting old " + getCaption() + ": "
- + info.service);
+ Slog.v(TAG, " disconnecting old " + getCaption() + ": " + info.service);
removeServiceLocked(i);
if (info.connection != null) {
mContext.unbindService(info.connection);
@@ -859,7 +913,7 @@
appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE;
try {
- if (DEBUG) Slog.v(TAG, "binding: " + intent);
+ Slog.v(TAG, "binding: " + intent);
ServiceConnection serviceConnection = new ServiceConnection() {
IInterface mService;
@@ -917,8 +971,7 @@
final int N = mServices.size();
for (int i = N - 1; i >= 0; i--) {
final ManagedServiceInfo info = mServices.get(i);
- if (name.equals(info.component)
- && info.userid == userid) {
+ if (name.equals(info.component) && info.userid == userid) {
removeServiceLocked(i);
if (info.connection != null) {
try {
@@ -945,9 +998,8 @@
final int N = mServices.size();
for (int i = N - 1; i >= 0; i--) {
final ManagedServiceInfo info = mServices.get(i);
- if (info.service.asBinder() == service.asBinder()
- && info.userid == userid) {
- if (DEBUG) Slog.d(TAG, "Removing active service " + info.component);
+ if (info.service.asBinder() == service.asBinder() && info.userid == userid) {
+ Slog.d(TAG, "Removing active service " + info.component);
serviceInfo = removeServiceLocked(i);
}
}
@@ -1035,6 +1087,16 @@
.append(']').toString();
}
+ public void toProto(ProtoOutputStream proto, ManagedServices host) {
+ final long cToken = proto.start(ManagedServiceInfoProto.COMPONENT);
+ component.toProto(proto);
+ proto.end(cToken);
+ proto.write(ManagedServiceInfoProto.USER_ID, userid);
+ proto.write(ManagedServiceInfoProto.SERVICE, service.getClass().getName());
+ proto.write(ManagedServiceInfoProto.IS_SYSTEM, isSystem);
+ proto.write(ManagedServiceInfoProto.IS_GUEST, isGuest(host));
+ }
+
public boolean enabledAndUserMatches(int nid) {
if (!isEnabledForCurrentProfiles()) {
return false;
diff --git a/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java b/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
index 7c82845..ce11268 100644
--- a/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
@@ -22,7 +22,7 @@
* Applies adjustments from the group helper and notification assistant
*/
public class NotificationAdjustmentExtractor implements NotificationSignalExtractor {
- private static final String TAG = "BadgeExtractor";
+ private static final String TAG = "AdjustmentExtractor";
private static final boolean DBG = false;
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index 46ab556..11c7ab7 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -22,7 +22,7 @@
* Stores the latest notification channel information for this notification
*/
public class NotificationChannelExtractor implements NotificationSignalExtractor {
- private static final String TAG = "BadgeExtractor";
+ private static final String TAG = "ChannelExtractor";
private static final boolean DBG = false;
private RankingConfig mConfig;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index 4923b06..f1476b3 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -17,8 +17,10 @@
package com.android.server.notification;
import android.app.Notification;
+import android.app.NotificationChannel;
public interface NotificationManagerInternal {
+ NotificationChannel getNotificationChannel(String pkg, int uid, String channelId);
void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
String tag, int id, Notification notification, int userId);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fe39fcc..4cb5d0f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,8 +16,10 @@
package com.android.server.notification;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -125,6 +127,7 @@
import android.service.notification.IConditionProvider;
import android.service.notification.INotificationListener;
import android.service.notification.IStatusBarNotificationHolder;
+import android.service.notification.ListenersDisablingEffectsProto;
import android.service.notification.NotificationAssistantService;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationRankingUpdate;
@@ -1142,6 +1145,12 @@
}
@VisibleForTesting
+ NotificationRecord getNotificationRecord(String key) {
+ return mNotificationsByKey.get(key);
+ }
+
+
+ @VisibleForTesting
void setSystemReady(boolean systemReady) {
mSystemReady = systemReady;
}
@@ -1216,7 +1225,7 @@
mUsageStats = usageStats;
mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
mRankingHelper = new RankingHelper(getContext(),
- getContext().getPackageManager(),
+ mPackageManagerClient,
mRankingHandler,
mUsageStats,
extractorNames);
@@ -1269,13 +1278,11 @@
R.array.config_notificationFallbackVibePattern,
VIBRATE_PATTERN_MAXLEN,
DEFAULT_VIBRATE_PATTERN);
-
mInCallNotificationUri = Uri.parse("file://" +
resources.getString(R.string.config_inCallNotificationSound));
mInCallNotificationAudioAttributes = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
- .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
.build();
mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
@@ -1476,7 +1483,7 @@
}
}
}
- mRankingHelper.updateNotificationChannel(pkg, uid, channel);
+ mRankingHelper.updateNotificationChannel(pkg, uid, channel, true);
if (!fromListener) {
final NotificationChannel modifiedChannel =
@@ -3239,14 +3246,51 @@
}
}
proto.end(records);
- }
- long zenLog = proto.start(NotificationServiceDumpProto.ZEN);
- mZenModeHelper.dump(proto);
- for (ComponentName suppressor : mEffectsSuppressors) {
- proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString());
+ long zenLog = proto.start(NotificationServiceDumpProto.ZEN);
+ mZenModeHelper.dump(proto);
+ for (ComponentName suppressor : mEffectsSuppressors) {
+ proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString());
+ }
+ proto.end(zenLog);
+
+ long listenersToken = proto.start(NotificationServiceDumpProto.NOTIFICATION_LISTENERS);
+ mListeners.dump(proto, filter);
+ proto.end(listenersToken);
+
+ proto.write(NotificationServiceDumpProto.LISTENER_HINTS, mListenerHints);
+
+ for (int i = 0; i < mListenersDisablingEffects.size(); ++i) {
+ long effectsToken = proto.start(
+ NotificationServiceDumpProto.LISTENERS_DISABLING_EFFECTS);
+
+ proto.write(
+ ListenersDisablingEffectsProto.HINT, mListenersDisablingEffects.keyAt(i));
+ final ArraySet<ManagedServiceInfo> listeners =
+ mListenersDisablingEffects.valueAt(i);
+ for (int j = 0; j < listeners.size(); j++) {
+ final ManagedServiceInfo listener = listeners.valueAt(i);
+ listenersToken = proto.start(ListenersDisablingEffectsProto.LISTENERS);
+ listener.toProto(proto, null);
+ proto.end(listenersToken);
+ }
+
+ proto.end(effectsToken);
+ }
+
+ long assistantsToken = proto.start(
+ NotificationServiceDumpProto.NOTIFICATION_ASSISTANTS);
+ mAssistants.dump(proto, filter);
+ proto.end(assistantsToken);
+
+ long conditionsToken = proto.start(NotificationServiceDumpProto.CONDITION_PROVIDERS);
+ mConditionProviders.dump(proto, filter);
+ proto.end(conditionsToken);
+
+ long rankingToken = proto.start(NotificationServiceDumpProto.RANKING_CONFIG);
+ mRankingHelper.dump(proto, filter);
+ proto.end(rankingToken);
}
- proto.end(zenLog);
proto.flush();
}
@@ -3401,6 +3445,12 @@
*/
private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
@Override
+ public NotificationChannel getNotificationChannel(String pkg, int uid, String
+ channelId) {
+ return mRankingHelper.getNotificationChannel(pkg, uid, channelId, false);
+ }
+
+ @Override
public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
String tag, int id, Notification notification, int userId) {
enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
@@ -3519,6 +3569,21 @@
user, null, System.currentTimeMillis());
final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
+ if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0
+ && (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0
+ && (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) {
+ // Increase the importance of foreground service notifications unless the user had an
+ // opinion otherwise
+ if (TextUtils.isEmpty(channelId)
+ || NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
+ r.setImportance(IMPORTANCE_LOW, "Bumped for foreground service");
+ } else {
+ channel.setImportance(IMPORTANCE_LOW);
+ mRankingHelper.updateNotificationChannel(pkg, notificationUid, channel, false);
+ r.updateNotificationChannel(channel);
+ }
+ }
+
if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
r.sbn.getOverrideGroupKey() != null)) {
return;
@@ -3752,6 +3817,8 @@
MetricsLogger.action(r.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_SNOOZED)
.setType(MetricsEvent.TYPE_CLOSE)
+ .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS,
+ mDuration)
.addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
mSnoozeCriterionId == null ? 0 : 1));
boolean wasPosted = removeFromNotificationListsLocked(r);
@@ -4028,19 +4095,19 @@
if (mSystemReady && mAudioManager != null) {
Uri soundUri = record.getSound();
hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
-
long[] vibration = record.getVibration();
// Demote sound to vibration if vibration missing & phone in vibration mode.
if (vibration == null
&& hasValidSound
&& (mAudioManager.getRingerModeInternal()
- == AudioManager.RINGER_MODE_VIBRATE)) {
+ == AudioManager.RINGER_MODE_VIBRATE)
+ && mAudioManager.getStreamVolume(
+ AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) == 0) {
vibration = mFallbackVibrationPattern;
}
hasValidVibrate = vibration != null;
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
-
if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
if (DBG) Slog.v(TAG, "Interrupting!");
if (hasValidSound) {
@@ -4137,8 +4204,9 @@
boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
// do not play notifications if there is a user of exclusive audio focus
// or the device is in vibrate mode
- if (!mAudioManager.isAudioFocusExclusive() && mAudioManager.getRingerModeInternal()
- != AudioManager.RINGER_MODE_VIBRATE) {
+ if (!mAudioManager.isAudioFocusExclusive() && (mAudioManager.getRingerModeInternal()
+ != AudioManager.RINGER_MODE_VIBRATE || mAudioManager.getStreamVolume(
+ AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
final long identity = Binder.clearCallingIdentity();
try {
final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
@@ -5772,10 +5840,9 @@
final DumpFilter filter = new DumpFilter();
for (int ai = 0; ai < args.length; ai++) {
final String a = args[ai];
- if ("--proto".equals(args[0])) {
+ if ("--proto".equals(a)) {
filter.proto = true;
- }
- if ("--noredact".equals(a) || "--reveal".equals(a)) {
+ } else if ("--noredact".equals(a) || "--reveal".equals(a)) {
filter.redact = false;
} else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
if (ai < args.length-1) {
@@ -5848,8 +5915,8 @@
private class ShellCmd extends ShellCommand {
public static final String USAGE = "help\n"
- + "allow_listener COMPONENT\n"
- + "disallow_listener COMPONENT\n"
+ + "allow_listener COMPONENT [user_id]\n"
+ + "disallow_listener COMPONENT [user_id]\n"
+ "set_assistant COMPONENT\n"
+ "remove_assistant COMPONENT\n"
+ "allow_dnd PACKAGE\n"
@@ -5880,7 +5947,13 @@
pw.println("Invalid listener - must be a ComponentName");
return -1;
}
- getBinderService().setNotificationListenerAccessGranted(cn, true);
+ String userId = getNextArg();
+ if (userId == null) {
+ getBinderService().setNotificationListenerAccessGranted(cn, true);
+ } else {
+ getBinderService().setNotificationListenerAccessGrantedForUser(
+ cn, Integer.parseInt(userId), true);
+ }
}
break;
case "disallow_listener": {
@@ -5889,7 +5962,13 @@
pw.println("Invalid listener - must be a ComponentName");
return -1;
}
- getBinderService().setNotificationListenerAccessGranted(cn, false);
+ String userId = getNextArg();
+ if (userId == null) {
+ getBinderService().setNotificationListenerAccessGranted(cn, false);
+ } else {
+ getBinderService().setNotificationListenerAccessGrantedForUser(
+ cn, Integer.parseInt(userId), false);
+ }
}
break;
case "allow_assistant": {
diff --git a/services/core/java/com/android/server/notification/PriorityExtractor.java b/services/core/java/com/android/server/notification/PriorityExtractor.java
index 5d5d39d..7a287db 100644
--- a/services/core/java/com/android/server/notification/PriorityExtractor.java
+++ b/services/core/java/com/android/server/notification/PriorityExtractor.java
@@ -23,7 +23,7 @@
* Determines if the given notification can bypass Do Not Disturb.
*/
public class PriorityExtractor implements NotificationSignalExtractor {
- private static final String TAG = "ImportantTopicExtractor";
+ private static final String TAG = "PriorityExtractor";
private static final boolean DBG = false;
private RankingConfig mConfig;
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index b5ef1c6..b9c0d90 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -39,7 +39,7 @@
int uid, boolean includeDeleted);
void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
boolean fromTargetApp);
- void updateNotificationChannel(String pkg, int uid, NotificationChannel channel);
+ void updateNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromUser);
NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted);
void deleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index fc24581..d7e9cf3 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -36,10 +36,13 @@
import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.RankingHelperProto;
+import android.service.notification.RankingHelperProto.RecordProto;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseBooleanArray;
+import android.util.proto.ProtoOutputStream;
import org.json.JSONArray;
import org.json.JSONException;
@@ -228,7 +231,11 @@
if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
NotificationChannel channel = new NotificationChannel(id,
channelName, channelImportance);
- channel.populateFromXml(parser);
+ if (forRestore) {
+ channel.populateFromXmlForRestore(parser, mContext);
+ } else {
+ channel.populateFromXml(parser);
+ }
r.channels.put(id, channel);
}
}
@@ -391,7 +398,11 @@
}
for (NotificationChannel channel : r.channels.values()) {
- if (!forBackup || (forBackup && !channel.isDeleted())) {
+ if (forBackup) {
+ if (!channel.isDeleted()) {
+ channel.writeXmlForBackup(out, mContext);
+ }
+ } else {
channel.writeXml(out);
}
}
@@ -613,7 +624,8 @@
}
@Override
- public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel) {
+ public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
+ boolean fromUser) {
Preconditions.checkNotNull(updatedChannel);
Preconditions.checkNotNull(updatedChannel.getId());
Record r = getOrCreateRecord(pkg, uid);
@@ -627,7 +639,11 @@
if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
updatedChannel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
}
- lockFieldsForUpdate(channel, updatedChannel);
+ updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
+ updatedChannel.lockFields(channel.getUserLockedFields());
+ if (fromUser) {
+ lockFieldsForUpdate(channel, updatedChannel);
+ }
r.channels.put(updatedChannel.getId(), updatedChannel);
if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(updatedChannel.getId())) {
@@ -874,8 +890,6 @@
@VisibleForTesting
void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) {
- update.unlockFields(update.getUserLockedFields());
- update.lockFields(original.getUserLockedFields());
if (original.canBypassDnd() != update.canBypassDnd()) {
update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
}
@@ -912,8 +926,7 @@
pw.print(" ");
pw.println(mSignalExtractors[i]);
}
- }
- if (filter == null) {
+
pw.print(prefix);
pw.println("per-package config:");
}
@@ -925,6 +938,52 @@
dumpRecords(pw, prefix, filter, mRestoredWithoutUids);
}
+ public void dump(ProtoOutputStream proto, NotificationManagerService.DumpFilter filter) {
+ final int N = mSignalExtractors.length;
+ for (int i = 0; i < N; i++) {
+ proto.write(RankingHelperProto.NOTIFICATION_SIGNAL_EXTRACTORS,
+ mSignalExtractors[i].getClass().getSimpleName());
+ }
+ synchronized (mRecords) {
+ dumpRecords(proto, RankingHelperProto.RECORDS, filter, mRecords);
+ }
+ dumpRecords(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
+ mRestoredWithoutUids);
+ }
+
+ private static void dumpRecords(ProtoOutputStream proto, long fieldId,
+ NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) {
+ final int N = records.size();
+ long fToken;
+ for (int i = 0; i < N; i++) {
+ final Record r = records.valueAt(i);
+ if (filter == null || filter.matches(r.pkg)) {
+ fToken = proto.start(fieldId);
+
+ proto.write(RecordProto.PACKAGE, r.pkg);
+ proto.write(RecordProto.UID, r.uid);
+ proto.write(RecordProto.IMPORTANCE, r.importance);
+ proto.write(RecordProto.PRIORITY, r.priority);
+ proto.write(RecordProto.VISIBILITY, r.visibility);
+ proto.write(RecordProto.SHOW_BADGE, r.showBadge);
+
+ long token;
+ for (NotificationChannel channel : r.channels.values()) {
+ token = proto.start(RecordProto.CHANNELS);
+ channel.toProto(proto);
+ proto.end(token);
+ }
+ for (NotificationChannelGroup group : r.groups.values()) {
+ token = proto.start(RecordProto.CHANNEL_GROUPS);
+ group.toProto(proto);
+ proto.end(token);
+ }
+
+ proto.end(fToken);
+ }
+ }
+ }
+
private static void dumpRecords(PrintWriter pw, String prefix,
NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) {
final int N = records.size();
diff --git a/services/core/java/com/android/server/notification/ScheduleCalendar.java b/services/core/java/com/android/server/notification/ScheduleCalendar.java
index 9e8b2e3..40230bd 100644
--- a/services/core/java/com/android/server/notification/ScheduleCalendar.java
+++ b/services/core/java/com/android/server/notification/ScheduleCalendar.java
@@ -42,7 +42,8 @@
public void maybeSetNextAlarm(long now, long nextAlarm) {
if (mSchedule != null) {
- if (mSchedule.exitAtAlarm && now > mSchedule.nextAlarm) {
+ if (mSchedule.exitAtAlarm
+ && (now > mSchedule.nextAlarm || nextAlarm < mSchedule.nextAlarm)) {
mSchedule.nextAlarm = nextAlarm;
}
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index ffdafc5..9fcc67d 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -567,7 +567,7 @@
proto.write(ZenModeProto.ENABLED_ACTIVE_CONDITIONS, rule.toString());
}
}
- proto.write(ZenModeProto.POLICY, mConfig.toNotificationPolicy().toString());
+ mConfig.toNotificationPolicy().toProto(proto, ZenModeProto.POLICY);
proto.write(ZenModeProto.SUPPRESSED_EFFECTS, mSuppressedEffects);
}
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 415c9a9..6d8cac0 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -342,8 +342,7 @@
DexoptOptions.DEXOPT_BOOT_COMPLETE |
(downgrade ? DexoptOptions.DEXOPT_DOWNGRADE : 0);
if (is_for_primary_dex) {
- int result = pm.performDexOptWithStatus(new DexoptOptions(pkg,
- PackageManagerService.REASON_BACKGROUND_DEXOPT,
+ int result = pm.performDexOptWithStatus(new DexoptOptions(pkg, reason,
dexoptFlags));
success = result != PackageDexOptimizer.DEX_OPT_FAILED;
if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -351,8 +350,7 @@
}
} else {
success = pm.performDexOpt(new DexoptOptions(pkg,
- PackageManagerService.REASON_BACKGROUND_DEXOPT,
- dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX));
+ reason, dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX));
}
if (success) {
// Dexopt succeeded, remove package from the list of failing ones.
diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
deleted file mode 100644
index 30fda1e..0000000
--- a/services/core/java/com/android/server/pm/BasePermission.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2006 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.pm;
-
-import android.content.pm.PackageParser;
-import android.content.pm.PermissionInfo;
-import android.os.UserHandle;
-
-final class BasePermission {
- final static int TYPE_NORMAL = 0;
-
- final static int TYPE_BUILTIN = 1;
-
- final static int TYPE_DYNAMIC = 2;
-
- final String name;
-
- String sourcePackage;
-
- PackageSettingBase packageSetting;
-
- final int type;
-
- int protectionLevel;
-
- PackageParser.Permission perm;
-
- PermissionInfo pendingInfo;
-
- /** UID that owns the definition of this permission */
- int uid;
-
- /** Additional GIDs given to apps granted this permission */
- private int[] gids;
-
- /**
- * Flag indicating that {@link #gids} should be adjusted based on the
- * {@link UserHandle} the granted app is running as.
- */
- private boolean perUser;
-
- BasePermission(String _name, String _sourcePackage, int _type) {
- name = _name;
- sourcePackage = _sourcePackage;
- type = _type;
- // Default to most conservative protection level.
- protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
- }
-
- @Override
- public String toString() {
- return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name
- + "}";
- }
-
- public void setGids(int[] gids, boolean perUser) {
- this.gids = gids;
- this.perUser = perUser;
- }
-
- public int[] computeGids(int userId) {
- if (perUser) {
- final int[] userGids = new int[gids.length];
- for (int i = 0; i < gids.length; i++) {
- userGids[i] = UserHandle.getUid(userId, gids[i]);
- }
- return userGids;
- } else {
- return gids;
- }
- }
-
- public boolean isRuntime() {
- return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
- == PermissionInfo.PROTECTION_DANGEROUS;
- }
-
- public boolean isDevelopment() {
- return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
- == PermissionInfo.PROTECTION_SIGNATURE
- && (protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
- }
-
- public boolean isInstant() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
- }
-
- public boolean isRuntimeOnly() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
- }
-}
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
deleted file mode 100644
index a3811ba..0000000
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ /dev/null
@@ -1,1244 +0,0 @@
-/*
- * 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.pm;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.app.ActivityManager;
-import android.app.DownloadManager;
-import android.app.admin.DevicePolicyManager;
-import android.companion.CompanionDeviceManager;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal.PackagesProvider;
-import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider;
-import android.content.pm.PackageParser;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ResolveInfo;
-import android.media.RingtoneManager;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.Message;
-import android.os.UserHandle;
-import android.os.storage.StorageManager;
-import android.print.PrintManager;
-import android.provider.CalendarContract;
-import android.provider.ContactsContract;
-import android.provider.MediaStore;
-import android.provider.Telephony.Sms.Intents;
-import android.telephony.TelephonyManager;
-import android.security.Credentials;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Slog;
-import android.util.Xml;
-import com.android.internal.util.XmlUtils;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import static android.os.Process.FIRST_APPLICATION_UID;
-
-/**
- * This class is the policy for granting runtime permissions to
- * platform components and default handlers in the system such
- * that the device is usable out-of-the-box. For example, the
- * shell UID is a part of the system and the Phone app should
- * have phone related permission by default.
- */
-final class DefaultPermissionGrantPolicy {
- private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars
- private static final boolean DEBUG = false;
-
- private static final int DEFAULT_FLAGS =
- PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_UNINSTALLED_PACKAGES;
-
- private static final String AUDIO_MIME_TYPE = "audio/mpeg";
-
- private static final String TAG_EXCEPTIONS = "exceptions";
- private static final String TAG_EXCEPTION = "exception";
- private static final String TAG_PERMISSION = "permission";
- private static final String ATTR_PACKAGE = "package";
- private static final String ATTR_NAME = "name";
- private static final String ATTR_FIXED = "fixed";
-
- private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>();
- static {
- PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE);
- PHONE_PERMISSIONS.add(Manifest.permission.CALL_PHONE);
- PHONE_PERMISSIONS.add(Manifest.permission.READ_CALL_LOG);
- PHONE_PERMISSIONS.add(Manifest.permission.WRITE_CALL_LOG);
- PHONE_PERMISSIONS.add(Manifest.permission.ADD_VOICEMAIL);
- PHONE_PERMISSIONS.add(Manifest.permission.USE_SIP);
- PHONE_PERMISSIONS.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
- }
-
- private static final Set<String> CONTACTS_PERMISSIONS = new ArraySet<>();
- static {
- CONTACTS_PERMISSIONS.add(Manifest.permission.READ_CONTACTS);
- CONTACTS_PERMISSIONS.add(Manifest.permission.WRITE_CONTACTS);
- CONTACTS_PERMISSIONS.add(Manifest.permission.GET_ACCOUNTS);
- }
-
- private static final Set<String> LOCATION_PERMISSIONS = new ArraySet<>();
- static {
- LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
- LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
- }
-
- private static final Set<String> CALENDAR_PERMISSIONS = new ArraySet<>();
- static {
- CALENDAR_PERMISSIONS.add(Manifest.permission.READ_CALENDAR);
- CALENDAR_PERMISSIONS.add(Manifest.permission.WRITE_CALENDAR);
- }
-
- private static final Set<String> SMS_PERMISSIONS = new ArraySet<>();
- static {
- SMS_PERMISSIONS.add(Manifest.permission.SEND_SMS);
- SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_SMS);
- SMS_PERMISSIONS.add(Manifest.permission.READ_SMS);
- SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_WAP_PUSH);
- SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_MMS);
- SMS_PERMISSIONS.add(Manifest.permission.READ_CELL_BROADCASTS);
- }
-
- private static final Set<String> MICROPHONE_PERMISSIONS = new ArraySet<>();
- static {
- MICROPHONE_PERMISSIONS.add(Manifest.permission.RECORD_AUDIO);
- }
-
- private static final Set<String> CAMERA_PERMISSIONS = new ArraySet<>();
- static {
- CAMERA_PERMISSIONS.add(Manifest.permission.CAMERA);
- }
-
- private static final Set<String> SENSORS_PERMISSIONS = new ArraySet<>();
- static {
- SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
- }
-
- private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
- static {
- STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
- STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
- }
-
- private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
-
- private static final String ACTION_TRACK = "com.android.fitness.TRACK";
-
- private final PackageManagerService mService;
- private final Handler mHandler;
-
- private PackagesProvider mLocationPackagesProvider;
- private PackagesProvider mVoiceInteractionPackagesProvider;
- private PackagesProvider mSmsAppPackagesProvider;
- private PackagesProvider mDialerAppPackagesProvider;
- private PackagesProvider mSimCallManagerPackagesProvider;
- private SyncAdapterPackagesProvider mSyncAdapterPackagesProvider;
-
- private ArrayMap<String, List<DefaultPermissionGrant>> mGrantExceptions;
-
- public DefaultPermissionGrantPolicy(PackageManagerService service) {
- mService = service;
- mHandler = new Handler(mService.mHandlerThread.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS) {
- synchronized (mService.mPackages) {
- if (mGrantExceptions == null) {
- mGrantExceptions = readDefaultPermissionExceptionsLPw();
- }
- }
- }
- }
- };
- }
-
- public void setLocationPackagesProviderLPw(PackagesProvider provider) {
- mLocationPackagesProvider = provider;
- }
-
- public void setVoiceInteractionPackagesProviderLPw(PackagesProvider provider) {
- mVoiceInteractionPackagesProvider = provider;
- }
-
- public void setSmsAppPackagesProviderLPw(PackagesProvider provider) {
- mSmsAppPackagesProvider = provider;
- }
-
- public void setDialerAppPackagesProviderLPw(PackagesProvider provider) {
- mDialerAppPackagesProvider = provider;
- }
-
- public void setSimCallManagerPackagesProviderLPw(PackagesProvider provider) {
- mSimCallManagerPackagesProvider = provider;
- }
-
- public void setSyncAdapterPackagesProviderLPw(SyncAdapterPackagesProvider provider) {
- mSyncAdapterPackagesProvider = provider;
- }
-
- public void grantDefaultPermissions(int userId) {
- if (mService.hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
- grantAllRuntimePermissions(userId);
- } else {
- grantPermissionsToSysComponentsAndPrivApps(userId);
- grantDefaultSystemHandlerPermissions(userId);
- grantDefaultPermissionExceptions(userId);
- }
- }
-
- private void grantRuntimePermissionsForPackageLocked(int userId, PackageParser.Package pkg) {
- Set<String> permissions = new ArraySet<>();
- for (String permission : pkg.requestedPermissions) {
- BasePermission bp = mService.mSettings.mPermissions.get(permission);
- if (bp != null && bp.isRuntime()) {
- permissions.add(permission);
- }
- }
- if (!permissions.isEmpty()) {
- grantRuntimePermissionsLPw(pkg, permissions, true, userId);
- }
- }
-
- private void grantAllRuntimePermissions(int userId) {
- Log.i(TAG, "Granting all runtime permissions for user " + userId);
- synchronized (mService.mPackages) {
- for (PackageParser.Package pkg : mService.mPackages.values()) {
- grantRuntimePermissionsForPackageLocked(userId, pkg);
- }
- }
- }
-
- public void scheduleReadDefaultPermissionExceptions() {
- mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
- }
-
- private void grantPermissionsToSysComponentsAndPrivApps(int userId) {
- Log.i(TAG, "Granting permissions to platform components for user " + userId);
-
- synchronized (mService.mPackages) {
- for (PackageParser.Package pkg : mService.mPackages.values()) {
- if (!isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg)
- || !doesPackageSupportRuntimePermissions(pkg)
- || pkg.requestedPermissions.isEmpty()) {
- continue;
- }
- grantRuntimePermissionsForPackageLocked(userId, pkg);
- }
- }
- }
-
- private void grantDefaultSystemHandlerPermissions(int userId) {
- Log.i(TAG, "Granting permissions to default platform handlers for user " + userId);
-
- final PackagesProvider locationPackagesProvider;
- final PackagesProvider voiceInteractionPackagesProvider;
- final PackagesProvider smsAppPackagesProvider;
- final PackagesProvider dialerAppPackagesProvider;
- final PackagesProvider simCallManagerPackagesProvider;
- final SyncAdapterPackagesProvider syncAdapterPackagesProvider;
-
- synchronized (mService.mPackages) {
- locationPackagesProvider = mLocationPackagesProvider;
- voiceInteractionPackagesProvider = mVoiceInteractionPackagesProvider;
- smsAppPackagesProvider = mSmsAppPackagesProvider;
- dialerAppPackagesProvider = mDialerAppPackagesProvider;
- simCallManagerPackagesProvider = mSimCallManagerPackagesProvider;
- syncAdapterPackagesProvider = mSyncAdapterPackagesProvider;
- }
-
- String[] voiceInteractPackageNames = (voiceInteractionPackagesProvider != null)
- ? voiceInteractionPackagesProvider.getPackages(userId) : null;
- String[] locationPackageNames = (locationPackagesProvider != null)
- ? locationPackagesProvider.getPackages(userId) : null;
- String[] smsAppPackageNames = (smsAppPackagesProvider != null)
- ? smsAppPackagesProvider.getPackages(userId) : null;
- String[] dialerAppPackageNames = (dialerAppPackagesProvider != null)
- ? dialerAppPackagesProvider.getPackages(userId) : null;
- String[] simCallManagerPackageNames = (simCallManagerPackagesProvider != null)
- ? simCallManagerPackagesProvider.getPackages(userId) : null;
- String[] contactsSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
- syncAdapterPackagesProvider.getPackages(ContactsContract.AUTHORITY, userId) : null;
- String[] calendarSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
- syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY, userId) : null;
-
- synchronized (mService.mPackages) {
- // Installer
- PackageParser.Package installerPackage = getSystemPackageLPr(
- mService.mRequiredInstallerPackage);
- if (installerPackage != null
- && doesPackageSupportRuntimePermissions(installerPackage)) {
- grantRuntimePermissionsLPw(installerPackage, STORAGE_PERMISSIONS, true, userId);
- }
-
- // Verifier
- PackageParser.Package verifierPackage = getSystemPackageLPr(
- mService.mRequiredVerifierPackage);
- if (verifierPackage != null
- && doesPackageSupportRuntimePermissions(verifierPackage)) {
- grantRuntimePermissionsLPw(verifierPackage, STORAGE_PERMISSIONS, true, userId);
- grantRuntimePermissionsLPw(verifierPackage, PHONE_PERMISSIONS, false, userId);
- grantRuntimePermissionsLPw(verifierPackage, SMS_PERMISSIONS, false, userId);
- }
-
- // SetupWizard
- PackageParser.Package setupPackage = getSystemPackageLPr(
- mService.mSetupWizardPackage);
- if (setupPackage != null
- && doesPackageSupportRuntimePermissions(setupPackage)) {
- grantRuntimePermissionsLPw(setupPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(setupPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(setupPackage, LOCATION_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(setupPackage, CAMERA_PERMISSIONS, userId);
- }
-
- // Camera
- Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
- PackageParser.Package cameraPackage = getDefaultSystemHandlerActivityPackageLPr(
- cameraIntent, userId);
- if (cameraPackage != null
- && doesPackageSupportRuntimePermissions(cameraPackage)) {
- grantRuntimePermissionsLPw(cameraPackage, CAMERA_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(cameraPackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(cameraPackage, STORAGE_PERMISSIONS, userId);
- }
-
- // Media provider
- PackageParser.Package mediaStorePackage = getDefaultProviderAuthorityPackageLPr(
- MediaStore.AUTHORITY, userId);
- if (mediaStorePackage != null) {
- grantRuntimePermissionsLPw(mediaStorePackage, STORAGE_PERMISSIONS, true, userId);
- }
-
- // Downloads provider
- PackageParser.Package downloadsPackage = getDefaultProviderAuthorityPackageLPr(
- "downloads", userId);
- if (downloadsPackage != null) {
- grantRuntimePermissionsLPw(downloadsPackage, STORAGE_PERMISSIONS, true, userId);
- }
-
- // Downloads UI
- Intent downloadsUiIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
- PackageParser.Package downloadsUiPackage = getDefaultSystemHandlerActivityPackageLPr(
- downloadsUiIntent, userId);
- if (downloadsUiPackage != null
- && doesPackageSupportRuntimePermissions(downloadsUiPackage)) {
- grantRuntimePermissionsLPw(downloadsUiPackage, STORAGE_PERMISSIONS, true, userId);
- }
-
- // Storage provider
- PackageParser.Package storagePackage = getDefaultProviderAuthorityPackageLPr(
- "com.android.externalstorage.documents", userId);
- if (storagePackage != null) {
- grantRuntimePermissionsLPw(storagePackage, STORAGE_PERMISSIONS, true, userId);
- }
-
- // CertInstaller
- Intent certInstallerIntent = new Intent(Credentials.INSTALL_ACTION);
- PackageParser.Package certInstallerPackage = getDefaultSystemHandlerActivityPackageLPr(
- certInstallerIntent, userId);
- if (certInstallerPackage != null
- && doesPackageSupportRuntimePermissions(certInstallerPackage)) {
- grantRuntimePermissionsLPw(certInstallerPackage, STORAGE_PERMISSIONS, true, userId);
- }
-
- // Dialer
- if (dialerAppPackageNames == null) {
- Intent dialerIntent = new Intent(Intent.ACTION_DIAL);
- PackageParser.Package dialerPackage = getDefaultSystemHandlerActivityPackageLPr(
- dialerIntent, userId);
- if (dialerPackage != null) {
- grantDefaultPermissionsToDefaultSystemDialerAppLPr(dialerPackage, userId);
- }
- } else {
- for (String dialerAppPackageName : dialerAppPackageNames) {
- PackageParser.Package dialerPackage = getSystemPackageLPr(dialerAppPackageName);
- if (dialerPackage != null) {
- grantDefaultPermissionsToDefaultSystemDialerAppLPr(dialerPackage, userId);
- }
- }
- }
-
- // Sim call manager
- if (simCallManagerPackageNames != null) {
- for (String simCallManagerPackageName : simCallManagerPackageNames) {
- PackageParser.Package simCallManagerPackage =
- getSystemPackageLPr(simCallManagerPackageName);
- if (simCallManagerPackage != null) {
- grantDefaultPermissionsToDefaultSimCallManagerLPr(simCallManagerPackage,
- userId);
- }
- }
- }
-
- // SMS
- if (smsAppPackageNames == null) {
- Intent smsIntent = new Intent(Intent.ACTION_MAIN);
- smsIntent.addCategory(Intent.CATEGORY_APP_MESSAGING);
- PackageParser.Package smsPackage = getDefaultSystemHandlerActivityPackageLPr(
- smsIntent, userId);
- if (smsPackage != null) {
- grantDefaultPermissionsToDefaultSystemSmsAppLPr(smsPackage, userId);
- }
- } else {
- for (String smsPackageName : smsAppPackageNames) {
- PackageParser.Package smsPackage = getSystemPackageLPr(smsPackageName);
- if (smsPackage != null) {
- grantDefaultPermissionsToDefaultSystemSmsAppLPr(smsPackage, userId);
- }
- }
- }
-
- // Cell Broadcast Receiver
- Intent cbrIntent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
- PackageParser.Package cbrPackage =
- getDefaultSystemHandlerActivityPackageLPr(cbrIntent, userId);
- if (cbrPackage != null && doesPackageSupportRuntimePermissions(cbrPackage)) {
- grantRuntimePermissionsLPw(cbrPackage, SMS_PERMISSIONS, userId);
- }
-
- // Carrier Provisioning Service
- Intent carrierProvIntent = new Intent(Intents.SMS_CARRIER_PROVISION_ACTION);
- PackageParser.Package carrierProvPackage =
- getDefaultSystemHandlerServicePackageLPr(carrierProvIntent, userId);
- if (carrierProvPackage != null && doesPackageSupportRuntimePermissions(carrierProvPackage)) {
- grantRuntimePermissionsLPw(carrierProvPackage, SMS_PERMISSIONS, false, userId);
- }
-
- // Calendar
- Intent calendarIntent = new Intent(Intent.ACTION_MAIN);
- calendarIntent.addCategory(Intent.CATEGORY_APP_CALENDAR);
- PackageParser.Package calendarPackage = getDefaultSystemHandlerActivityPackageLPr(
- calendarIntent, userId);
- if (calendarPackage != null
- && doesPackageSupportRuntimePermissions(calendarPackage)) {
- grantRuntimePermissionsLPw(calendarPackage, CALENDAR_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(calendarPackage, CONTACTS_PERMISSIONS, userId);
- }
-
- // Calendar provider
- PackageParser.Package calendarProviderPackage = getDefaultProviderAuthorityPackageLPr(
- CalendarContract.AUTHORITY, userId);
- if (calendarProviderPackage != null) {
- grantRuntimePermissionsLPw(calendarProviderPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(calendarProviderPackage, CALENDAR_PERMISSIONS,
- true, userId);
- grantRuntimePermissionsLPw(calendarProviderPackage, STORAGE_PERMISSIONS, userId);
- }
-
- // Calendar provider sync adapters
- List<PackageParser.Package> calendarSyncAdapters = getHeadlessSyncAdapterPackagesLPr(
- calendarSyncAdapterPackages, userId);
- final int calendarSyncAdapterCount = calendarSyncAdapters.size();
- for (int i = 0; i < calendarSyncAdapterCount; i++) {
- PackageParser.Package calendarSyncAdapter = calendarSyncAdapters.get(i);
- if (doesPackageSupportRuntimePermissions(calendarSyncAdapter)) {
- grantRuntimePermissionsLPw(calendarSyncAdapter, CALENDAR_PERMISSIONS, userId);
- }
- }
-
- // Contacts
- Intent contactsIntent = new Intent(Intent.ACTION_MAIN);
- contactsIntent.addCategory(Intent.CATEGORY_APP_CONTACTS);
- PackageParser.Package contactsPackage = getDefaultSystemHandlerActivityPackageLPr(
- contactsIntent, userId);
- if (contactsPackage != null
- && doesPackageSupportRuntimePermissions(contactsPackage)) {
- grantRuntimePermissionsLPw(contactsPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(contactsPackage, PHONE_PERMISSIONS, userId);
- }
-
- // Contacts provider sync adapters
- List<PackageParser.Package> contactsSyncAdapters = getHeadlessSyncAdapterPackagesLPr(
- contactsSyncAdapterPackages, userId);
- final int contactsSyncAdapterCount = contactsSyncAdapters.size();
- for (int i = 0; i < contactsSyncAdapterCount; i++) {
- PackageParser.Package contactsSyncAdapter = contactsSyncAdapters.get(i);
- if (doesPackageSupportRuntimePermissions(contactsSyncAdapter)) {
- grantRuntimePermissionsLPw(contactsSyncAdapter, CONTACTS_PERMISSIONS, userId);
- }
- }
-
- // Contacts provider
- PackageParser.Package contactsProviderPackage = getDefaultProviderAuthorityPackageLPr(
- ContactsContract.AUTHORITY, userId);
- if (contactsProviderPackage != null) {
- grantRuntimePermissionsLPw(contactsProviderPackage, CONTACTS_PERMISSIONS,
- true, userId);
- grantRuntimePermissionsLPw(contactsProviderPackage, PHONE_PERMISSIONS,
- true, userId);
- grantRuntimePermissionsLPw(contactsProviderPackage, STORAGE_PERMISSIONS, userId);
- }
-
- // Device provisioning
- Intent deviceProvisionIntent = new Intent(
- DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE);
- PackageParser.Package deviceProvisionPackage =
- getDefaultSystemHandlerActivityPackageLPr(deviceProvisionIntent, userId);
- if (deviceProvisionPackage != null
- && doesPackageSupportRuntimePermissions(deviceProvisionPackage)) {
- grantRuntimePermissionsLPw(deviceProvisionPackage, CONTACTS_PERMISSIONS, userId);
- }
-
- // Maps
- Intent mapsIntent = new Intent(Intent.ACTION_MAIN);
- mapsIntent.addCategory(Intent.CATEGORY_APP_MAPS);
- PackageParser.Package mapsPackage = getDefaultSystemHandlerActivityPackageLPr(
- mapsIntent, userId);
- if (mapsPackage != null
- && doesPackageSupportRuntimePermissions(mapsPackage)) {
- grantRuntimePermissionsLPw(mapsPackage, LOCATION_PERMISSIONS, userId);
- }
-
- // Gallery
- Intent galleryIntent = new Intent(Intent.ACTION_MAIN);
- galleryIntent.addCategory(Intent.CATEGORY_APP_GALLERY);
- PackageParser.Package galleryPackage = getDefaultSystemHandlerActivityPackageLPr(
- galleryIntent, userId);
- if (galleryPackage != null
- && doesPackageSupportRuntimePermissions(galleryPackage)) {
- grantRuntimePermissionsLPw(galleryPackage, STORAGE_PERMISSIONS, userId);
- }
-
- // Email
- Intent emailIntent = new Intent(Intent.ACTION_MAIN);
- emailIntent.addCategory(Intent.CATEGORY_APP_EMAIL);
- PackageParser.Package emailPackage = getDefaultSystemHandlerActivityPackageLPr(
- emailIntent, userId);
- if (emailPackage != null
- && doesPackageSupportRuntimePermissions(emailPackage)) {
- grantRuntimePermissionsLPw(emailPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(emailPackage, CALENDAR_PERMISSIONS, userId);
- }
-
- // Browser
- PackageParser.Package browserPackage = null;
- String defaultBrowserPackage = mService.getDefaultBrowserPackageName(userId);
- if (defaultBrowserPackage != null) {
- browserPackage = getPackageLPr(defaultBrowserPackage);
- }
- if (browserPackage == null) {
- Intent browserIntent = new Intent(Intent.ACTION_MAIN);
- browserIntent.addCategory(Intent.CATEGORY_APP_BROWSER);
- browserPackage = getDefaultSystemHandlerActivityPackageLPr(
- browserIntent, userId);
- }
- if (browserPackage != null
- && doesPackageSupportRuntimePermissions(browserPackage)) {
- grantRuntimePermissionsLPw(browserPackage, LOCATION_PERMISSIONS, userId);
- }
-
- // Voice interaction
- if (voiceInteractPackageNames != null) {
- for (String voiceInteractPackageName : voiceInteractPackageNames) {
- PackageParser.Package voiceInteractPackage = getSystemPackageLPr(
- voiceInteractPackageName);
- if (voiceInteractPackage != null
- && doesPackageSupportRuntimePermissions(voiceInteractPackage)) {
- grantRuntimePermissionsLPw(voiceInteractPackage,
- CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(voiceInteractPackage,
- CALENDAR_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(voiceInteractPackage,
- MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(voiceInteractPackage,
- PHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(voiceInteractPackage,
- SMS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(voiceInteractPackage,
- LOCATION_PERMISSIONS, userId);
- }
- }
- }
-
- if (ActivityManager.isLowRamDeviceStatic()) {
- // Allow voice search on low-ram devices
- Intent globalSearchIntent = new Intent("android.search.action.GLOBAL_SEARCH");
- PackageParser.Package globalSearchPickerPackage =
- getDefaultSystemHandlerActivityPackageLPr(globalSearchIntent, userId);
-
- if (globalSearchPickerPackage != null
- && doesPackageSupportRuntimePermissions(globalSearchPickerPackage)) {
- grantRuntimePermissionsLPw(globalSearchPickerPackage,
- MICROPHONE_PERMISSIONS, true, userId);
- grantRuntimePermissionsLPw(globalSearchPickerPackage,
- LOCATION_PERMISSIONS, true, userId);
- }
- }
-
- // Voice recognition
- Intent voiceRecoIntent = new Intent("android.speech.RecognitionService");
- voiceRecoIntent.addCategory(Intent.CATEGORY_DEFAULT);
- PackageParser.Package voiceRecoPackage = getDefaultSystemHandlerServicePackageLPr(
- voiceRecoIntent, userId);
- if (voiceRecoPackage != null
- && doesPackageSupportRuntimePermissions(voiceRecoPackage)) {
- grantRuntimePermissionsLPw(voiceRecoPackage, MICROPHONE_PERMISSIONS, userId);
- }
-
- // Location
- if (locationPackageNames != null) {
- for (String packageName : locationPackageNames) {
- PackageParser.Package locationPackage = getSystemPackageLPr(packageName);
- if (locationPackage != null
- && doesPackageSupportRuntimePermissions(locationPackage)) {
- grantRuntimePermissionsLPw(locationPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(locationPackage, CALENDAR_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(locationPackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(locationPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(locationPackage, SMS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(locationPackage, LOCATION_PERMISSIONS,
- true, userId);
- grantRuntimePermissionsLPw(locationPackage, CAMERA_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(locationPackage, SENSORS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(locationPackage, STORAGE_PERMISSIONS, userId);
- }
- }
- }
-
- // Music
- Intent musicIntent = new Intent(Intent.ACTION_VIEW);
- musicIntent.addCategory(Intent.CATEGORY_DEFAULT);
- musicIntent.setDataAndType(Uri.fromFile(new File("foo.mp3")),
- AUDIO_MIME_TYPE);
- PackageParser.Package musicPackage = getDefaultSystemHandlerActivityPackageLPr(
- musicIntent, userId);
- if (musicPackage != null
- && doesPackageSupportRuntimePermissions(musicPackage)) {
- grantRuntimePermissionsLPw(musicPackage, STORAGE_PERMISSIONS, userId);
- }
-
- // Home
- Intent homeIntent = new Intent(Intent.ACTION_MAIN);
- homeIntent.addCategory(Intent.CATEGORY_HOME);
- homeIntent.addCategory(Intent.CATEGORY_LAUNCHER_APP);
- PackageParser.Package homePackage = getDefaultSystemHandlerActivityPackageLPr(
- homeIntent, userId);
- if (homePackage != null
- && doesPackageSupportRuntimePermissions(homePackage)) {
- grantRuntimePermissionsLPw(homePackage, LOCATION_PERMISSIONS, false, userId);
- }
-
- // Watches
- if (mService.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
- // Home application on watches
- Intent wearHomeIntent = new Intent(Intent.ACTION_MAIN);
- wearHomeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
-
- PackageParser.Package wearHomePackage = getDefaultSystemHandlerActivityPackageLPr(
- wearHomeIntent, userId);
-
- if (wearHomePackage != null
- && doesPackageSupportRuntimePermissions(wearHomePackage)) {
- grantRuntimePermissionsLPw(wearHomePackage, CONTACTS_PERMISSIONS, false,
- userId);
- grantRuntimePermissionsLPw(wearHomePackage, PHONE_PERMISSIONS, true, userId);
- grantRuntimePermissionsLPw(wearHomePackage, MICROPHONE_PERMISSIONS, false,
- userId);
- grantRuntimePermissionsLPw(wearHomePackage, LOCATION_PERMISSIONS, false,
- userId);
- }
-
- // Fitness tracking on watches
- Intent trackIntent = new Intent(ACTION_TRACK);
- PackageParser.Package trackPackage = getDefaultSystemHandlerActivityPackageLPr(
- trackIntent, userId);
- if (trackPackage != null
- && doesPackageSupportRuntimePermissions(trackPackage)) {
- grantRuntimePermissionsLPw(trackPackage, SENSORS_PERMISSIONS, false, userId);
- grantRuntimePermissionsLPw(trackPackage, LOCATION_PERMISSIONS, false, userId);
- }
- }
-
- // Print Spooler
- PackageParser.Package printSpoolerPackage = getSystemPackageLPr(
- PrintManager.PRINT_SPOOLER_PACKAGE_NAME);
- if (printSpoolerPackage != null
- && doesPackageSupportRuntimePermissions(printSpoolerPackage)) {
- grantRuntimePermissionsLPw(printSpoolerPackage, LOCATION_PERMISSIONS, true, userId);
- }
-
- // EmergencyInfo
- Intent emergencyInfoIntent = new Intent(TelephonyManager.ACTION_EMERGENCY_ASSISTANCE);
- PackageParser.Package emergencyInfoPckg = getDefaultSystemHandlerActivityPackageLPr(
- emergencyInfoIntent, userId);
- if (emergencyInfoPckg != null
- && doesPackageSupportRuntimePermissions(emergencyInfoPckg)) {
- grantRuntimePermissionsLPw(emergencyInfoPckg, CONTACTS_PERMISSIONS, true, userId);
- grantRuntimePermissionsLPw(emergencyInfoPckg, PHONE_PERMISSIONS, true, userId);
- }
-
- // NFC Tag viewer
- Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW);
- nfcTagIntent.setType("vnd.android.cursor.item/ndef_msg");
- PackageParser.Package nfcTagPkg = getDefaultSystemHandlerActivityPackageLPr(
- nfcTagIntent, userId);
- if (nfcTagPkg != null
- && doesPackageSupportRuntimePermissions(nfcTagPkg)) {
- grantRuntimePermissionsLPw(nfcTagPkg, CONTACTS_PERMISSIONS, false, userId);
- grantRuntimePermissionsLPw(nfcTagPkg, PHONE_PERMISSIONS, false, userId);
- }
-
- // Storage Manager
- Intent storageManagerIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
- PackageParser.Package storageManagerPckg = getDefaultSystemHandlerActivityPackageLPr(
- storageManagerIntent, userId);
- if (storageManagerPckg != null
- && doesPackageSupportRuntimePermissions(storageManagerPckg)) {
- grantRuntimePermissionsLPw(storageManagerPckg, STORAGE_PERMISSIONS, true, userId);
- }
-
- // Companion devices
- PackageParser.Package companionDeviceDiscoveryPackage = getSystemPackageLPr(
- CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME);
- if (companionDeviceDiscoveryPackage != null
- && doesPackageSupportRuntimePermissions(companionDeviceDiscoveryPackage)) {
- grantRuntimePermissionsLPw(companionDeviceDiscoveryPackage,
- LOCATION_PERMISSIONS, true, userId);
- }
-
- // Ringtone Picker
- Intent ringtonePickerIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
- PackageParser.Package ringtonePickerPackage =
- getDefaultSystemHandlerActivityPackageLPr(ringtonePickerIntent, userId);
- if (ringtonePickerPackage != null
- && doesPackageSupportRuntimePermissions(ringtonePickerPackage)) {
- grantRuntimePermissionsLPw(ringtonePickerPackage,
- STORAGE_PERMISSIONS, true, userId);
- }
-
- mService.mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
- }
- }
-
- private void grantDefaultPermissionsToDefaultSystemDialerAppLPr(
- PackageParser.Package dialerPackage, int userId) {
- if (doesPackageSupportRuntimePermissions(dialerPackage)) {
- boolean isPhonePermFixed =
- mService.hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
- grantRuntimePermissionsLPw(
- dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId);
- grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(dialerPackage, CAMERA_PERMISSIONS, userId);
- }
- }
-
- private void grantDefaultPermissionsToDefaultSystemSmsAppLPr(
- PackageParser.Package smsPackage, int userId) {
- if (doesPackageSupportRuntimePermissions(smsPackage)) {
- grantRuntimePermissionsLPw(smsPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(smsPackage, CONTACTS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(smsPackage, SMS_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(smsPackage, STORAGE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(smsPackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(smsPackage, CAMERA_PERMISSIONS, userId);
- }
- }
-
- public void grantDefaultPermissionsToDefaultSmsAppLPr(String packageName, int userId) {
- Log.i(TAG, "Granting permissions to default sms app for user:" + userId);
- if (packageName == null) {
- return;
- }
- PackageParser.Package smsPackage = getPackageLPr(packageName);
- if (smsPackage != null && doesPackageSupportRuntimePermissions(smsPackage)) {
- grantRuntimePermissionsLPw(smsPackage, PHONE_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(smsPackage, CONTACTS_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(smsPackage, SMS_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(smsPackage, STORAGE_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(smsPackage, MICROPHONE_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(smsPackage, CAMERA_PERMISSIONS, false, true, userId);
- }
- }
-
- public void grantDefaultPermissionsToDefaultDialerAppLPr(String packageName, int userId) {
- Log.i(TAG, "Granting permissions to default dialer app for user:" + userId);
- if (packageName == null) {
- return;
- }
- PackageParser.Package dialerPackage = getPackageLPr(packageName);
- if (dialerPackage != null
- && doesPackageSupportRuntimePermissions(dialerPackage)) {
- grantRuntimePermissionsLPw(dialerPackage, PHONE_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, false, true, userId);
- grantRuntimePermissionsLPw(dialerPackage, CAMERA_PERMISSIONS, false, true, userId);
- }
- }
-
- private void grantDefaultPermissionsToDefaultSimCallManagerLPr(
- PackageParser.Package simCallManagerPackage, int userId) {
- Log.i(TAG, "Granting permissions to sim call manager for user:" + userId);
- if (doesPackageSupportRuntimePermissions(simCallManagerPackage)) {
- grantRuntimePermissionsLPw(simCallManagerPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(simCallManagerPackage, MICROPHONE_PERMISSIONS, userId);
- }
- }
-
- public void grantDefaultPermissionsToDefaultSimCallManagerLPr(String packageName, int userId) {
- if (packageName == null) {
- return;
- }
- PackageParser.Package simCallManagerPackage = getPackageLPr(packageName);
- if (simCallManagerPackage != null) {
- grantDefaultPermissionsToDefaultSimCallManagerLPr(simCallManagerPackage, userId);
- }
- }
-
- public void grantDefaultPermissionsToEnabledCarrierAppsLPr(String[] packageNames, int userId) {
- Log.i(TAG, "Granting permissions to enabled carrier apps for user:" + userId);
- if (packageNames == null) {
- return;
- }
- for (String packageName : packageNames) {
- PackageParser.Package carrierPackage = getSystemPackageLPr(packageName);
- if (carrierPackage != null
- && doesPackageSupportRuntimePermissions(carrierPackage)) {
- grantRuntimePermissionsLPw(carrierPackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(carrierPackage, LOCATION_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(carrierPackage, SMS_PERMISSIONS, userId);
- }
- }
- }
-
- public void grantDefaultPermissionsToEnabledImsServicesLPr(String[] packageNames, int userId) {
- Log.i(TAG, "Granting permissions to enabled ImsServices for user:" + userId);
- if (packageNames == null) {
- return;
- }
- for (String packageName : packageNames) {
- PackageParser.Package imsServicePackage = getSystemPackageLPr(packageName);
- if (imsServicePackage != null
- && doesPackageSupportRuntimePermissions(imsServicePackage)) {
- grantRuntimePermissionsLPw(imsServicePackage, PHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(imsServicePackage, MICROPHONE_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(imsServicePackage, LOCATION_PERMISSIONS, userId);
- grantRuntimePermissionsLPw(imsServicePackage, CAMERA_PERMISSIONS, userId);
- }
- }
- }
-
- public void grantDefaultPermissionsToDefaultBrowserLPr(String packageName, int userId) {
- Log.i(TAG, "Granting permissions to default browser for user:" + userId);
- if (packageName == null) {
- return;
- }
- PackageParser.Package browserPackage = getSystemPackageLPr(packageName);
- if (browserPackage != null
- && doesPackageSupportRuntimePermissions(browserPackage)) {
- grantRuntimePermissionsLPw(browserPackage, LOCATION_PERMISSIONS, false, false, userId);
- }
- }
-
- private PackageParser.Package getDefaultSystemHandlerActivityPackageLPr(
- Intent intent, int userId) {
- ResolveInfo handler = mService.resolveIntent(intent,
- intent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS, userId);
- if (handler == null || handler.activityInfo == null) {
- return null;
- }
- ActivityInfo activityInfo = handler.activityInfo;
- if (activityInfo.packageName.equals(mService.mResolveActivity.packageName)
- && activityInfo.name.equals(mService.mResolveActivity.name)) {
- return null;
- }
- return getSystemPackageLPr(handler.activityInfo.packageName);
- }
-
- private PackageParser.Package getDefaultSystemHandlerServicePackageLPr(
- Intent intent, int userId) {
- List<ResolveInfo> handlers = mService.queryIntentServices(intent,
- intent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS, userId)
- .getList();
- if (handlers == null) {
- return null;
- }
- final int handlerCount = handlers.size();
- for (int i = 0; i < handlerCount; i++) {
- ResolveInfo handler = handlers.get(i);
- PackageParser.Package handlerPackage = getSystemPackageLPr(
- handler.serviceInfo.packageName);
- if (handlerPackage != null) {
- return handlerPackage;
- }
- }
- return null;
- }
-
- private List<PackageParser.Package> getHeadlessSyncAdapterPackagesLPr(
- String[] syncAdapterPackageNames, int userId) {
- List<PackageParser.Package> syncAdapterPackages = new ArrayList<>();
-
- Intent homeIntent = new Intent(Intent.ACTION_MAIN);
- homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-
- for (String syncAdapterPackageName : syncAdapterPackageNames) {
- homeIntent.setPackage(syncAdapterPackageName);
-
- ResolveInfo homeActivity = mService.resolveIntent(homeIntent,
- homeIntent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS,
- userId);
- if (homeActivity != null) {
- continue;
- }
-
- PackageParser.Package syncAdapterPackage = getSystemPackageLPr(syncAdapterPackageName);
- if (syncAdapterPackage != null) {
- syncAdapterPackages.add(syncAdapterPackage);
- }
- }
-
- return syncAdapterPackages;
- }
-
- private PackageParser.Package getDefaultProviderAuthorityPackageLPr(
- String authority, int userId) {
- ProviderInfo provider = mService.resolveContentProvider(authority, DEFAULT_FLAGS, userId);
- if (provider != null) {
- return getSystemPackageLPr(provider.packageName);
- }
- return null;
- }
-
- private PackageParser.Package getPackageLPr(String packageName) {
- return mService.mPackages.get(packageName);
- }
-
- private PackageParser.Package getSystemPackageLPr(String packageName) {
- PackageParser.Package pkg = getPackageLPr(packageName);
- if (pkg != null && pkg.isSystemApp()) {
- return !isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg) ? pkg : null;
- }
- return null;
- }
-
- private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions,
- int userId) {
- grantRuntimePermissionsLPw(pkg, permissions, false, false, userId);
- }
-
- private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions,
- boolean systemFixed, int userId) {
- grantRuntimePermissionsLPw(pkg, permissions, systemFixed, false, userId);
- }
-
- private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions,
- boolean systemFixed, boolean isDefaultPhoneOrSms, int userId) {
- if (pkg.requestedPermissions.isEmpty()) {
- return;
- }
-
- List<String> requestedPermissions = pkg.requestedPermissions;
- Set<String> grantablePermissions = null;
-
- // If this is the default Phone or SMS app we grant permissions regardless
- // whether the version on the system image declares the permission as used since
- // selecting the app as the default Phone or SMS the user makes a deliberate
- // choice to grant this app the permissions needed to function. For all other
- // apps, (default grants on first boot and user creation) we don't grant default
- // permissions if the version on the system image does not declare them.
- if (!isDefaultPhoneOrSms && pkg.isUpdatedSystemApp()) {
- PackageSetting sysPs = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);
- if (sysPs != null && sysPs.pkg != null) {
- if (sysPs.pkg.requestedPermissions.isEmpty()) {
- return;
- }
- if (!requestedPermissions.equals(sysPs.pkg.requestedPermissions)) {
- grantablePermissions = new ArraySet<>(requestedPermissions);
- requestedPermissions = sysPs.pkg.requestedPermissions;
- }
- }
- }
-
- final int grantablePermissionCount = requestedPermissions.size();
- for (int i = 0; i < grantablePermissionCount; i++) {
- String permission = requestedPermissions.get(i);
-
- // If there is a disabled system app it may request a permission the updated
- // version ot the data partition doesn't, In this case skip the permission.
- if (grantablePermissions != null && !grantablePermissions.contains(permission)) {
- continue;
- }
-
- if (permissions.contains(permission)) {
- final int flags = mService.getPermissionFlags(permission, pkg.packageName, userId);
-
- // If any flags are set to the permission, then it is either set in
- // its current state by the system or device/profile owner or the user.
- // In all these cases we do not want to clobber the current state.
- // Unless the caller wants to override user choices. The override is
- // to make sure we can grant the needed permission to the default
- // sms and phone apps after the user chooses this in the UI.
- if (flags == 0 || isDefaultPhoneOrSms) {
- // Never clobber policy or system.
- final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
- | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
- if ((flags & fixedFlags) != 0) {
- continue;
- }
-
- mService.grantRuntimePermission(pkg.packageName, permission, userId);
- if (DEBUG) {
- Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ")
- + permission + " to default handler " + pkg.packageName);
- }
-
- int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
- if (systemFixed) {
- newFlags |= PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
- }
-
- mService.updatePermissionFlags(permission, pkg.packageName,
- newFlags, newFlags, userId);
- }
-
- // If a component gets a permission for being the default handler A
- // and also default handler B, we grant the weaker grant form.
- if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0
- && (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
- && !systemFixed) {
- if (DEBUG) {
- Log.i(TAG, "Granted not fixed " + permission + " to default handler "
- + pkg.packageName);
- }
- mService.updatePermissionFlags(permission, pkg.packageName,
- PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, userId);
- }
- }
- }
- }
-
- private boolean isSysComponentOrPersistentPlatformSignedPrivAppLPr(PackageParser.Package pkg) {
- if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
- return true;
- }
- if (!pkg.isPrivilegedApp()) {
- return false;
- }
- PackageSetting sysPkg = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);
- if (sysPkg != null && sysPkg.pkg != null) {
- if ((sysPkg.pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
- return false;
- }
- } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
- return false;
- }
- return PackageManagerService.compareSignatures(mService.mPlatformPackage.mSignatures,
- pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
- }
-
- private void grantDefaultPermissionExceptions(int userId) {
- synchronized (mService.mPackages) {
- mHandler.removeMessages(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
-
- if (mGrantExceptions == null) {
- mGrantExceptions = readDefaultPermissionExceptionsLPw();
- }
-
- // mGrantExceptions is null only before the first read and then
- // it serves as a cache of the default grants that should be
- // performed for every user. If there is an entry then the app
- // is on the system image and supports runtime permissions.
- Set<String> permissions = null;
- final int exceptionCount = mGrantExceptions.size();
- for (int i = 0; i < exceptionCount; i++) {
- String packageName = mGrantExceptions.keyAt(i);
- PackageParser.Package pkg = getSystemPackageLPr(packageName);
- List<DefaultPermissionGrant> permissionGrants = mGrantExceptions.valueAt(i);
- final int permissionGrantCount = permissionGrants.size();
- for (int j = 0; j < permissionGrantCount; j++) {
- DefaultPermissionGrant permissionGrant = permissionGrants.get(j);
- if (permissions == null) {
- permissions = new ArraySet<>();
- } else {
- permissions.clear();
- }
- permissions.add(permissionGrant.name);
- grantRuntimePermissionsLPw(pkg, permissions,
- permissionGrant.fixed, userId);
- }
- }
- }
- }
-
- private File[] getDefaultPermissionFiles() {
- ArrayList<File> ret = new ArrayList<File>();
- File dir = new File(Environment.getRootDirectory(), "etc/default-permissions");
- if (dir.isDirectory() && dir.canRead()) {
- Collections.addAll(ret, dir.listFiles());
- }
- dir = new File(Environment.getVendorDirectory(), "etc/default-permissions");
- if (dir.isDirectory() && dir.canRead()) {
- Collections.addAll(ret, dir.listFiles());
- }
- return ret.isEmpty() ? null : ret.toArray(new File[0]);
- }
-
- private @NonNull ArrayMap<String, List<DefaultPermissionGrant>>
- readDefaultPermissionExceptionsLPw() {
- File[] files = getDefaultPermissionFiles();
- if (files == null) {
- return new ArrayMap<>(0);
- }
-
- ArrayMap<String, List<DefaultPermissionGrant>> grantExceptions = new ArrayMap<>();
-
- // Iterate over the files in the directory and scan .xml files
- for (File file : files) {
- if (!file.getPath().endsWith(".xml")) {
- Slog.i(TAG, "Non-xml file " + file + " in " + file.getParent() + " directory, ignoring");
- continue;
- }
- if (!file.canRead()) {
- Slog.w(TAG, "Default permissions file " + file + " cannot be read");
- continue;
- }
- try (
- InputStream str = new BufferedInputStream(new FileInputStream(file))
- ) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(str, null);
- parse(parser, grantExceptions);
- } catch (XmlPullParserException | IOException e) {
- Slog.w(TAG, "Error reading default permissions file " + file, e);
- }
- }
-
- return grantExceptions;
- }
-
- private void parse(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
- outGrantExceptions) throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
- if (TAG_EXCEPTIONS.equals(parser.getName())) {
- parseExceptions(parser, outGrantExceptions);
- } else {
- Log.e(TAG, "Unknown tag " + parser.getName());
- }
- }
- }
-
- private void parseExceptions(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
- outGrantExceptions) throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
- if (TAG_EXCEPTION.equals(parser.getName())) {
- String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
-
- List<DefaultPermissionGrant> packageExceptions =
- outGrantExceptions.get(packageName);
- if (packageExceptions == null) {
- // The package must be on the system image
- PackageParser.Package pkg = getSystemPackageLPr(packageName);
- if (pkg == null) {
- Log.w(TAG, "Unknown package:" + packageName);
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
-
- // The package must support runtime permissions
- if (!doesPackageSupportRuntimePermissions(pkg)) {
- Log.w(TAG, "Skipping non supporting runtime permissions package:"
- + packageName);
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- packageExceptions = new ArrayList<>();
- outGrantExceptions.put(packageName, packageExceptions);
- }
-
- parsePermission(parser, packageExceptions);
- } else {
- Log.e(TAG, "Unknown tag " + parser.getName() + "under <exceptions>");
- }
- }
- }
-
- private void parsePermission(XmlPullParser parser, List<DefaultPermissionGrant>
- outPackageExceptions) throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- if (TAG_PERMISSION.contains(parser.getName())) {
- String name = parser.getAttributeValue(null, ATTR_NAME);
- if (name == null) {
- Log.w(TAG, "Mandatory name attribute missing for permission tag");
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
-
- final boolean fixed = XmlUtils.readBooleanAttribute(parser, ATTR_FIXED);
-
- DefaultPermissionGrant exception = new DefaultPermissionGrant(name, fixed);
- outPackageExceptions.add(exception);
- } else {
- Log.e(TAG, "Unknown tag " + parser.getName() + "under <exception>");
- }
- }
- }
-
- private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) {
- return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
- }
-
- private static final class DefaultPermissionGrant {
- final String name;
- final boolean fixed;
-
- public DefaultPermissionGrant(String name, boolean fixed) {
- this.name = name;
- this.fixed = fixed;
- }
- }
-}
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
new file mode 100644
index 0000000..7ebef83
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 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.pm;
+
+public final class DumpState {
+ public static final int DUMP_LIBS = 1 << 0;
+ public static final int DUMP_FEATURES = 1 << 1;
+ public static final int DUMP_ACTIVITY_RESOLVERS = 1 << 2;
+ public static final int DUMP_SERVICE_RESOLVERS = 1 << 3;
+ public static final int DUMP_RECEIVER_RESOLVERS = 1 << 4;
+ public static final int DUMP_CONTENT_RESOLVERS = 1 << 5;
+ public static final int DUMP_PERMISSIONS = 1 << 6;
+ public static final int DUMP_PACKAGES = 1 << 7;
+ public static final int DUMP_SHARED_USERS = 1 << 8;
+ public static final int DUMP_MESSAGES = 1 << 9;
+ public static final int DUMP_PROVIDERS = 1 << 10;
+ public static final int DUMP_VERIFIERS = 1 << 11;
+ public static final int DUMP_PREFERRED = 1 << 12;
+ public static final int DUMP_PREFERRED_XML = 1 << 13;
+ public static final int DUMP_KEYSETS = 1 << 14;
+ public static final int DUMP_VERSION = 1 << 15;
+ public static final int DUMP_INSTALLS = 1 << 16;
+ public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 17;
+ public static final int DUMP_DOMAIN_PREFERRED = 1 << 18;
+ public static final int DUMP_FROZEN = 1 << 19;
+ public static final int DUMP_DEXOPT = 1 << 20;
+ public static final int DUMP_COMPILER_STATS = 1 << 21;
+ public static final int DUMP_CHANGES = 1 << 22;
+ public static final int DUMP_VOLUMES = 1 << 23;
+
+ public static final int OPTION_SHOW_FILTERS = 1 << 0;
+
+ private int mTypes;
+
+ private int mOptions;
+
+ private boolean mTitlePrinted;
+
+ private SharedUserSetting mSharedUser;
+
+ public boolean isDumping(int type) {
+ if (mTypes == 0 && type != DUMP_PREFERRED_XML) {
+ return true;
+ }
+
+ return (mTypes & type) != 0;
+ }
+
+ public void setDump(int type) {
+ mTypes |= type;
+ }
+
+ public boolean isOptionEnabled(int option) {
+ return (mOptions & option) != 0;
+ }
+
+ public void setOptionEnabled(int option) {
+ mOptions |= option;
+ }
+
+ public boolean onTitlePrinted() {
+ final boolean printed = mTitlePrinted;
+ mTitlePrinted = true;
+ return printed;
+ }
+
+ public boolean getTitlePrinted() {
+ return mTitlePrinted;
+ }
+
+ public void setTitlePrinted(boolean enabled) {
+ mTitlePrinted = enabled;
+ }
+
+ public SharedUserSetting getSharedUser() {
+ return mSharedUser;
+ }
+
+ public void setSharedUser(SharedUserSetting user) {
+ mSharedUser = user;
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index e1e5b35..c964f91 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -49,6 +49,8 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
+import com.android.server.pm.permission.BasePermission;
+
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -878,8 +880,9 @@
final long identity = Binder.clearCallingIdentity();
try {
for (String grantedPermission : appInfo.getGrantedPermissions()) {
- BasePermission bp = mService.mSettings.mPermissions.get(grantedPermission);
- if (bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant()) {
+ final boolean propagatePermission =
+ mService.mSettings.canPropagatePermissionToInstantApp(grantedPermission);
+ if (propagatePermission) {
mService.grantRuntimePermission(packageName, grantedPermission, userId);
}
}
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 49d3c8b..3574466 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -565,7 +565,7 @@
}
public void dumpLPr(PrintWriter pw, String packageName,
- PackageManagerService.DumpState dumpState) {
+ DumpState dumpState) {
boolean printedHeader = false;
for (ArrayMap.Entry<String, PackageSetting> e : mPackages.entrySet()) {
String keySetPackage = e.getKey();
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 4fafe34..8ebeeae 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -23,6 +23,7 @@
import android.os.FileUtils;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.WorkSource;
import android.util.Log;
@@ -103,7 +104,17 @@
}
static boolean canOptimizePackage(PackageParser.Package pkg) {
- return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ // We do not dexopt a package with no code.
+ if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) {
+ return false;
+ }
+
+ // We do not dexopt a priv-app package when pm.dexopt.priv-apps is false.
+ if (pkg.isPrivilegedApp()) {
+ return SystemProperties.getBoolean("pm.dexopt.priv-apps", true);
+ }
+
+ return true;
}
/**
@@ -354,18 +365,13 @@
+ " dexoptFlags=" + printDexoptFlags(dexoptFlags)
+ " target-filter=" + compilerFilter);
- String classLoaderContext;
- if (dexUseInfo.isUnknownClassLoaderContext() ||
- dexUseInfo.isUnsupportedClassLoaderContext() ||
- dexUseInfo.isVariableClassLoaderContext()) {
- // If we have an unknown (not yet set), unsupported (custom class loaders), or a
- // variable class loader chain, compile without a context and mark the oat file with
- // SKIP_SHARED_LIBRARY_CHECK. Note that his might lead to a incorrect compilation.
- // TODO(calin): We should just extract in this case.
- classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
- } else {
- classLoaderContext = dexUseInfo.getClassLoaderContext();
- }
+ // TODO(calin): b/64530081 b/66984396. Use SKIP_SHARED_LIBRARY_CHECK for the context
+ // (instead of dexUseInfo.getClassLoaderContext()) in order to compile secondary dex files
+ // in isolation (and avoid to extract/verify the main apk if it's in the class path).
+ // Note this trades correctness for performance since the resulting slow down is
+ // unacceptable in some cases until b/64530081 is fixed.
+ String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+
try {
for (String isa : dexUseInfo.getLoaderIsas()) {
// Reuse the same dexopt path as for the primary apks. We don't need all the
@@ -425,7 +431,7 @@
}
if (useInfo.isUsedByOtherApps(path)) {
- pw.println("used be other apps: " + useInfo.getLoadingPackages(path));
+ pw.println("used by other apps: " + useInfo.getLoadingPackages(path));
}
Map<String, PackageDexUsage.DexUseInfo> dexUseInfoMap = useInfo.getDexUseInfoMap();
@@ -441,7 +447,7 @@
// TODO(calin): get the status of the oat file (needs installd call)
pw.println("class loader context: " + dexUseInfo.getClassLoaderContext());
if (dexUseInfo.isUsedByOtherApps()) {
- pw.println("used be other apps: " + dexUseInfo.getLoadingPackages());
+ pw.println("used by other apps: " + dexUseInfo.getLoadingPackages());
}
pw.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 1fa37b9..09f9cb8 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -80,6 +80,8 @@
import com.android.internal.util.ImageUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
+import com.android.server.LocalServices;
+import com.android.server.pm.permission.PermissionManagerInternal;
import libcore.io.IoUtils;
@@ -122,6 +124,7 @@
private final Context mContext;
private final PackageManagerService mPm;
+ private final PermissionManagerInternal mPermissionManager;
private AppOpsManager mAppOps;
@@ -177,6 +180,7 @@
public PackageInstallerService(Context context, PackageManagerService pm) {
mContext = context;
mPm = pm;
+ mPermissionManager = LocalServices.getService(PermissionManagerInternal.class);
mInstallThread = new HandlerThread(TAG);
mInstallThread.start();
@@ -243,35 +247,6 @@
}
}
- public void onSecureContainersAvailable() {
- synchronized (mSessions) {
- final ArraySet<String> unclaimed = new ArraySet<>();
- for (String cid : PackageHelper.getSecureContainerList()) {
- if (isStageName(cid)) {
- unclaimed.add(cid);
- }
- }
-
- // Ignore stages claimed by active sessions
- for (int i = 0; i < mSessions.size(); i++) {
- final PackageInstallerSession session = mSessions.valueAt(i);
- final String cid = session.stageCid;
-
- if (unclaimed.remove(cid)) {
- // Claimed by active session, mount it
- PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
- Process.SYSTEM_UID);
- }
- }
-
- // Clean up orphaned staging containers
- for (String cid : unclaimed) {
- Slog.w(TAG, "Deleting orphan container " + cid);
- PackageHelper.destroySdDir(cid);
- }
- }
- }
-
public static boolean isStageName(String name) {
final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
@@ -426,7 +401,8 @@
private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
throws IOException {
final int callingUid = Binder.getCallingUid();
- mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");
+ mPermissionManager.enforceCrossUserPermission(
+ callingUid, userId, true, true, "createSession");
if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
throw new SecurityException("User restriction prevents installing");
@@ -671,13 +647,6 @@
return "smdl" + sessionId + ".tmp";
}
- static void prepareExternalStageCid(String stageCid, long sizeBytes) throws IOException {
- if (PackageHelper.createSdDir(sizeBytes, stageCid, PackageManagerService.getEncryptKey(),
- Process.SYSTEM_UID, true) == null) {
- throw new IOException("Failed to create session cid: " + stageCid);
- }
- }
-
@Override
public SessionInfo getSessionInfo(int sessionId) {
synchronized (mSessions) {
@@ -688,7 +657,8 @@
@Override
public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
- mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getAllSessions");
+ mPermissionManager.enforceCrossUserPermission(
+ Binder.getCallingUid(), userId, true, false, "getAllSessions");
final List<SessionInfo> result = new ArrayList<>();
synchronized (mSessions) {
@@ -704,7 +674,8 @@
@Override
public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) {
- mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getMySessions");
+ mPermissionManager.enforceCrossUserPermission(
+ Binder.getCallingUid(), userId, true, false, "getMySessions");
mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
final List<SessionInfo> result = new ArrayList<>();
@@ -726,7 +697,7 @@
public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
IntentSender statusReceiver, int userId) throws RemoteException {
final int callingUid = Binder.getCallingUid();
- mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
mAppOps.checkPackage(callingUid, callerPackageName);
}
@@ -775,7 +746,8 @@
@Override
public void registerCallback(IPackageInstallerCallback callback, int userId) {
- mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "registerCallback");
+ mPermissionManager.enforceCrossUserPermission(
+ Binder.getCallingUid(), userId, true, false, "registerCallback");
mCallbacks.register(callback, userId);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index ff6e5b3..d62f093 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -36,7 +36,6 @@
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
import static com.android.internal.util.XmlUtils.writeUriAttribute;
-import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid;
import static com.android.server.pm.PackageInstallerService.prepareStageDir;
import android.Manifest;
@@ -481,12 +480,7 @@
if (stageDir != null) {
mResolvedStageDir = stageDir;
} else {
- final String path = PackageHelper.getSdDir(stageCid);
- if (path != null) {
- mResolvedStageDir = new File(path);
- } else {
- throw new IOException("Failed to resolve path to container " + stageCid);
- }
+ throw new IOException("Missing stageDir");
}
}
return mResolvedStageDir;
@@ -880,14 +874,6 @@
return;
}
- if (stageCid != null) {
- // Figure out the final installed size and resize the container once
- // and for all. Internally the parser handles straddling between two
- // locations when inheriting.
- final long finalSize = calculateInstalledSize();
- resizeContainer(stageCid, finalSize);
- }
-
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
@@ -924,11 +910,6 @@
// Unpack native libraries
extractNativeLibraries(mResolvedStageDir, params.abiOverride);
- // Container is ready to go, let's seal it up!
- if (stageCid != null) {
- finalizeAndFixContainer(stageCid);
- }
-
// We've reached point of no return; call into PMS to install the stage.
// Regardless of success or failure we always destroy session.
final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
@@ -953,7 +934,7 @@
}
mRelinquished = true;
- mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
+ mPm.installStage(mPackageName, stageDir, localObserver, params,
mInstallerPackageName, mInstallerUid, user, mCertificates);
}
@@ -1212,11 +1193,9 @@
// straddled between the inherited and staged APKs.
final PackageLite pkg = new PackageLite(null, baseApk, null, null, null, null,
splitPaths.toArray(new String[splitPaths.size()]), null);
- final boolean isForwardLocked =
- (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
try {
- return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride);
+ return PackageHelper.calculateInstalledSize(pkg, params.abiOverride);
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Failed to calculate install size", e);
@@ -1345,52 +1324,6 @@
}
}
- private static void resizeContainer(String cid, long targetSize)
- throws PackageManagerException {
- String path = PackageHelper.getSdDir(cid);
- if (path == null) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to find mounted " + cid);
- }
-
- final long currentSize = new File(path).getTotalSpace();
- if (currentSize > targetSize) {
- Slog.w(TAG, "Current size " + currentSize + " is larger than target size "
- + targetSize + "; skipping resize");
- return;
- }
-
- if (!PackageHelper.unMountSdDir(cid)) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to unmount " + cid + " before resize");
- }
-
- if (!PackageHelper.resizeSdDir(targetSize, cid,
- PackageManagerService.getEncryptKey())) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to resize " + cid + " to " + targetSize + " bytes");
- }
-
- path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
- Process.SYSTEM_UID, false);
- if (path == null) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to mount " + cid + " after resize");
- }
- }
-
- private void finalizeAndFixContainer(String cid) throws PackageManagerException {
- if (!PackageHelper.finalizeSdDir(cid)) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to finalize container " + cid);
- }
-
- if (!PackageHelper.fixSdPermissions(cid, defaultContainerGid, null)) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to fix permissions on container " + cid);
- }
- }
-
void setPermissionsResult(boolean accepted) {
if (!mSealed) {
throw new SecurityException("Must be sealed to accept permissions");
@@ -1419,20 +1352,8 @@
if (!mPrepared) {
if (stageDir != null) {
prepareStageDir(stageDir);
- } else if (stageCid != null) {
- final long identity = Binder.clearCallingIdentity();
- try {
- prepareExternalStageCid(stageCid, params.sizeBytes);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- // TODO: deliver more granular progress for ASEC allocation
- mInternalProgress = 0.25f;
- computeProgressLocked(true);
} else {
- throw new IllegalArgumentException(
- "Exactly one of stageDir or stageCid stage must be set");
+ throw new IllegalArgumentException("stageDir must be set");
}
mPrepared = true;
@@ -1534,9 +1455,6 @@
} catch (InstallerException ignored) {
}
}
- if (stageCid != null) {
- PackageHelper.destroySdDir(stageCid);
- }
}
void dump(IndentingPrintWriter pw) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8852a4d..7d1a647 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -55,7 +55,6 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_USER_RESTRICTED;
import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
-import static android.content.pm.PackageManager.INSTALL_FORWARD_LOCK;
import static android.content.pm.PackageManager.INSTALL_INTERNAL;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
@@ -103,10 +102,9 @@
import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
-import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
-import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
-import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
-
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
import android.Manifest;
@@ -160,10 +158,11 @@
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ActivityIntentInfo;
+import android.content.pm.PackageParser.Package;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageStats;
@@ -230,7 +229,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Base64;
-import android.util.TimingsTraceLog;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.ExceptionUtils;
@@ -244,6 +242,7 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import android.util.TimingsTraceLog;
import android.util.Xml;
import android.util.jar.StrictJarFile;
import android.util.proto.ProtoOutputStream;
@@ -283,12 +282,19 @@
import com.android.server.Watchdog;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.PermissionsState.PermissionState;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
+import com.android.server.pm.permission.BasePermission;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
+import com.android.server.pm.permission.PermissionManagerService;
+import com.android.server.pm.permission.PermissionManagerInternal;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
+import com.android.server.pm.permission.PermissionsState;
+import com.android.server.pm.permission.PermissionsState.PermissionState;
import com.android.server.storage.DeviceStorageMonitorInternal;
import dalvik.system.CloseGuard;
@@ -338,6 +344,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -385,7 +392,7 @@
public class PackageManagerService extends IPackageManager.Stub
implements PackageSender {
static final String TAG = "PackageManager";
- static final boolean DEBUG_SETTINGS = false;
+ public static final boolean DEBUG_SETTINGS = false;
static final boolean DEBUG_PREFERRED = false;
static final boolean DEBUG_UPGRADE = false;
static final boolean DEBUG_DOMAIN_VERIFICATION = false;
@@ -396,7 +403,7 @@
private static final boolean DEBUG_SHOW_INFO = false;
private static final boolean DEBUG_PACKAGE_INFO = false;
private static final boolean DEBUG_INTENT_MATCHING = false;
- private static final boolean DEBUG_PACKAGE_SCANNING = false;
+ public static final boolean DEBUG_PACKAGE_SCANNING = false;
private static final boolean DEBUG_VERIFY = false;
private static final boolean DEBUG_FILTERS = false;
private static final boolean DEBUG_PERMISSIONS = false;
@@ -427,9 +434,6 @@
private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
private static final int SHELL_UID = Process.SHELL_UID;
- // Cap the size of permission trees that 3rd party apps can define
- private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768; // characters of text
-
// Suffix used during package installation when copying/moving
// package apks to install directory.
private static final String INSTALL_PACKAGE_SUFFIX = "-";
@@ -655,9 +659,6 @@
@GuardedBy("mPackages")
private boolean mDexOptDialogShown;
- /** The location for ASEC container files on internal storage. */
- final String mAsecInternalPath;
-
// Used for privilege escalation. MUST NOT BE CALLED WITH mPackages
// LOCK HELD. Can be called with mInstallLock held.
@GuardedBy("mInstallLock")
@@ -863,7 +864,7 @@
String targetPath) {
return getStaticOverlayPaths(targetPackageName, targetPath);
}
- };
+ }
class ParallelPackageParserCallback extends PackageParserCallback {
List<PackageParser.Package> mOverlayPackages = null;
@@ -1005,7 +1006,9 @@
final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
= new SparseArray<IntentFilterVerificationState>();
+ // TODO remove this and go through mPermissonManager directly
final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
+ private final PermissionManagerInternal mPermissionManager;
// List of packages names to keep cached, even if they are uninstalled for all users
private List<String> mKeepUninstalledPackages;
@@ -1316,7 +1319,6 @@
static final int POST_INSTALL = 9;
static final int MCS_RECONNECT = 10;
static final int MCS_GIVE_UP = 11;
- static final int UPDATED_MEDIA_STATUS = 12;
static final int WRITE_SETTINGS = 13;
static final int WRITE_PACKAGE_RESTRICTIONS = 14;
static final int PACKAGE_VERIFIED = 15;
@@ -1715,32 +1717,6 @@
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
} break;
- case UPDATED_MEDIA_STATUS: {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Got message UPDATED_MEDIA_STATUS");
- boolean reportStatus = msg.arg1 == 1;
- boolean doGc = msg.arg2 == 1;
- if (DEBUG_SD_INSTALL) Log.i(TAG, "reportStatus=" + reportStatus + ", doGc = " + doGc);
- if (doGc) {
- // Force a gc to clear up stale containers.
- Runtime.getRuntime().gc();
- }
- if (msg.obj != null) {
- @SuppressWarnings("unchecked")
- Set<AsecInstallArgs> args = (Set<AsecInstallArgs>) msg.obj;
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading all containers");
- // Unload containers
- unloadAllContainers(args);
- }
- if (reportStatus) {
- try {
- if (DEBUG_SD_INSTALL) Log.i(TAG,
- "Invoking StorageManagerService call back");
- PackageHelper.getStorageManager().finishMediaUpdate();
- } catch (RemoteException e) {
- Log.e(TAG, "StorageManagerService not running?");
- }
- }
- } break;
case WRITE_SETTINGS: {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mPackages) {
@@ -1911,6 +1887,69 @@
}
}
+ private PermissionCallback mPermissionCallback = new PermissionCallback() {
+ @Override
+ public void onGidsChanged(int appId, int userId) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
+ }
+ });
+ }
+ @Override
+ public void onPermissionGranted(int uid, int userId) {
+ mOnPermissionChangeListeners.onPermissionsChanged(uid);
+
+ // Not critical; if this is lost, the application has to request again.
+ synchronized (mPackages) {
+ mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+ }
+ }
+ @Override
+ public void onInstallPermissionGranted() {
+ synchronized (mPackages) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ @Override
+ public void onPermissionRevoked(int uid, int userId) {
+ mOnPermissionChangeListeners.onPermissionsChanged(uid);
+
+ synchronized (mPackages) {
+ // Critical; after this call the application should never have the permission
+ mSettings.writeRuntimePermissionsForUserLPr(userId, true);
+ }
+
+ final int appId = UserHandle.getAppId(uid);
+ killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
+ }
+ @Override
+ public void onInstallPermissionRevoked() {
+ synchronized (mPackages) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ @Override
+ public void onPermissionUpdated(int userId) {
+ synchronized (mPackages) {
+ mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+ }
+ }
+ @Override
+ public void onInstallPermissionUpdated() {
+ synchronized (mPackages) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ @Override
+ public void onPermissionRemoved() {
+ synchronized (mPackages) {
+ mSettings.writeLPr();
+ }
+ }
+ };
+
private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
boolean killApp, boolean virtualPreload, String[] grantedPermissions,
boolean launchedForRestore, String installerPackage,
@@ -1927,7 +1966,10 @@
// review flag which is used to emulate runtime permissions for
// legacy apps.
if (grantPermissions) {
- grantRequestedRuntimePermissions(res.pkg, res.newUsers, grantedPermissions);
+ final int callingUid = Binder.getCallingUid();
+ mPermissionManager.grantRequestedRuntimePermissions(
+ res.pkg, res.newUsers, grantedPermissions, callingUid,
+ mPermissionCallback);
}
final boolean update = res.removedInfo != null
@@ -1943,9 +1985,9 @@
// app that had no children, we grant requested runtime permissions to the new
// children if the parent on the system image had them already granted.
if (res.pkg.parentPackage != null) {
- synchronized (mPackages) {
- grantRuntimePermissionsGrantedToDisabledPrivSysPackageParentLPw(res.pkg);
- }
+ final int callingUid = Binder.getCallingUid();
+ mPermissionManager.grantRuntimePermissionsGrantedToDisabledPackage(
+ res.pkg, callingUid, mPermissionCallback);
}
synchronized (mPackages) {
@@ -2110,39 +2152,6 @@
}
}
- private void grantRuntimePermissionsGrantedToDisabledPrivSysPackageParentLPw(
- PackageParser.Package pkg) {
- if (pkg.parentPackage == null) {
- return;
- }
- if (pkg.requestedPermissions == null) {
- return;
- }
- final PackageSetting disabledSysParentPs = mSettings
- .getDisabledSystemPkgLPr(pkg.parentPackage.packageName);
- if (disabledSysParentPs == null || disabledSysParentPs.pkg == null
- || !disabledSysParentPs.isPrivileged()
- || (disabledSysParentPs.childPackageNames != null
- && !disabledSysParentPs.childPackageNames.isEmpty())) {
- return;
- }
- final int[] allUserIds = sUserManager.getUserIds();
- final int permCount = pkg.requestedPermissions.size();
- for (int i = 0; i < permCount; i++) {
- String permission = pkg.requestedPermissions.get(i);
- BasePermission bp = mSettings.mPermissions.get(permission);
- if (bp == null || !(bp.isRuntime() || bp.isDevelopment())) {
- continue;
- }
- for (int userId : allUserIds) {
- if (disabledSysParentPs.getPermissionsState().hasRuntimePermission(
- permission, userId)) {
- grantRuntimePermission(pkg.packageName, permission, userId);
- }
- }
- }
- }
-
private StorageEventListener mStorageListener = new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
@@ -2165,14 +2174,6 @@
unloadPrivatePackages(vol);
}
}
-
- if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.isPrimary()) {
- if (vol.state == VolumeInfo.STATE_MOUNTED) {
- updateExternalMediaStatus(true, false);
- } else if (vol.state == VolumeInfo.STATE_EJECTING) {
- updateExternalMediaStatus(false, false);
- }
- }
}
@Override
@@ -2203,58 +2204,6 @@
}
};
- private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
- String[] grantedPermissions) {
- for (int userId : userIds) {
- grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions);
- }
- }
-
- private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId,
- String[] grantedPermissions) {
- PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null) {
- return;
- }
-
- PermissionsState permissionsState = ps.getPermissionsState();
-
- final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
- | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
-
- final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
- >= Build.VERSION_CODES.M;
-
- final boolean instantApp = isInstantApp(pkg.packageName, userId);
-
- for (String permission : pkg.requestedPermissions) {
- final BasePermission bp;
- synchronized (mPackages) {
- bp = mSettings.mPermissions.get(permission);
- }
- if (bp != null && (bp.isRuntime() || bp.isDevelopment())
- && (!instantApp || bp.isInstant())
- && (supportsRuntimePermissions || !bp.isRuntimeOnly())
- && (grantedPermissions == null
- || ArrayUtils.contains(grantedPermissions, permission))) {
- final int flags = permissionsState.getPermissionFlags(permission, userId);
- if (supportsRuntimePermissions) {
- // Installer cannot change immutable permissions.
- if ((flags & immutableFlags) == 0) {
- grantRuntimePermission(pkg.packageName, permission, userId);
- }
- } else if (mPermissionReviewRequired) {
- // In permission review mode we clear the review flag when we
- // are asked to install the app with all permissions granted.
- if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- updatePermissionFlags(permission, pkg.packageName,
- PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, userId);
- }
- }
- }
- }
- }
-
Bundle extrasForInstallResult(PackageInstalledInfo res) {
Bundle extras = null;
switch (res.returnCode) {
@@ -2423,7 +2372,29 @@
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
mMetrics = new DisplayMetrics();
- mSettings = new Settings(mPackages);
+ mInstaller = installer;
+
+ // Create sub-components that provide services / data. Order here is important.
+ synchronized (mInstallLock) {
+ synchronized (mPackages) {
+ // Expose private service for system components to use.
+ LocalServices.addService(
+ PackageManagerInternal.class, new PackageManagerInternalImpl());
+ sUserManager = new UserManagerService(context, this,
+ new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
+ mPermissionManager = PermissionManagerService.create(context,
+ new DefaultPermissionGrantedCallback() {
+ @Override
+ public void onDefaultRuntimePermissionsGranted(int userId) {
+ synchronized(mPackages) {
+ mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
+ }
+ }
+ }, mPackages /*externalLock*/);
+ mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
+ mSettings = new Settings(mPermissionManager.getPermissionSettings(), mPackages);
+ }
+ }
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
@@ -2454,7 +2425,6 @@
mSeparateProcesses = null;
}
- mInstaller = installer;
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
@@ -2483,32 +2453,12 @@
mHandler = new PackageHandler(mHandlerThread.getLooper());
mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
-
- mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this);
mInstantAppRegistry = new InstantAppRegistry(this);
File dataDir = Environment.getDataDirectory();
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
- mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
- sUserManager = new UserManagerService(context, this,
- new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
-
- // Propagate permission configuration in to package manager.
- ArrayMap<String, SystemConfig.PermissionEntry> permConfig
- = systemConfig.getPermissions();
- for (int i=0; i<permConfig.size(); i++) {
- SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
- BasePermission bp = mSettings.mPermissions.get(perm.name);
- if (bp == null) {
- bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
- mSettings.mPermissions.put(perm.name, bp);
- }
- if (perm.gids != null) {
- bp.setGids(perm.gids, perm.perUser);
- }
- }
ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
@@ -3111,8 +3061,6 @@
// once we have a booted system.
mInstaller.setWarnIfHeld(mPackages);
- // Expose private service for system components to use.
- LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -3315,6 +3263,24 @@
removeCodePathLI(dstCodePath);
return null;
}
+
+ // If we have a profile for a compressed APK, copy it to the reference location.
+ // Since the package is the stub one, remove the stub suffix to get the normal package and
+ // APK name.
+ File profileFile = new File(getPrebuildProfilePath(pkg).replace(STUB_SUFFIX, ""));
+ if (profileFile.exists()) {
+ try {
+ // We could also do this lazily before calling dexopt in
+ // PackageDexOptimizer to prevent this happening on first boot. The issue
+ // is that we don't have a good way to say "do this only once".
+ if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
+ pkg.applicationInfo.uid, pkg.packageName)) {
+ Log.e(TAG, "decompressPackage failed to copy system profile!");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e);
+ }
+ }
return dstCodePath;
}
@@ -3910,7 +3876,7 @@
public boolean isPackageAvailable(String packageName, int userId) {
if (!sUserManager.exists(userId)) return false;
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "is package available");
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
@@ -3953,7 +3919,7 @@
int flags, int filterCallingUid, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForPackage(flags, userId, packageName);
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get package info");
// reader
@@ -4215,7 +4181,7 @@
if (!sUserManager.exists(userId)) return -1;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId, packageName);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid");
// reader
@@ -4245,7 +4211,7 @@
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId, packageName);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids");
// reader
@@ -4272,116 +4238,23 @@
return null;
}
- static PermissionInfo generatePermissionInfo(BasePermission bp, int flags) {
- if (bp.perm != null) {
- return PackageParser.generatePermissionInfo(bp.perm, flags);
- }
- PermissionInfo pi = new PermissionInfo();
- pi.name = bp.name;
- pi.packageName = bp.sourcePackage;
- pi.nonLocalizedLabel = bp.name;
- pi.protectionLevel = bp.protectionLevel;
- return pi;
- }
-
@Override
public PermissionInfo getPermissionInfo(String name, String packageName, int flags) {
- final int callingUid = Binder.getCallingUid();
- if (getInstantAppPackageName(callingUid) != null) {
- return null;
- }
- // reader
- synchronized (mPackages) {
- final BasePermission p = mSettings.mPermissions.get(name);
- if (p == null) {
- return null;
- }
- // If the caller is an app that targets pre 26 SDK drop protection flags.
- PermissionInfo permissionInfo = generatePermissionInfo(p, flags);
- if (permissionInfo != null) {
- final int protectionLevel = adjustPermissionProtectionFlagsLPr(
- permissionInfo.protectionLevel, packageName, callingUid);
- if (permissionInfo.protectionLevel != protectionLevel) {
- // If we return different protection level, don't use the cached info
- if (p.perm != null && p.perm.info == permissionInfo) {
- permissionInfo = new PermissionInfo(permissionInfo);
- }
- permissionInfo.protectionLevel = protectionLevel;
- }
- }
- return permissionInfo;
- }
- }
-
- private int adjustPermissionProtectionFlagsLPr(int protectionLevel,
- String packageName, int uid) {
- // Signature permission flags area always reported
- final int protectionLevelMasked = protectionLevel
- & (PermissionInfo.PROTECTION_NORMAL
- | PermissionInfo.PROTECTION_DANGEROUS
- | PermissionInfo.PROTECTION_SIGNATURE);
- if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) {
- return protectionLevel;
- }
-
- // System sees all flags.
- final int appId = UserHandle.getAppId(uid);
- if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID
- || appId == Process.SHELL_UID) {
- return protectionLevel;
- }
-
- // Normalize package name to handle renamed packages and static libs
- packageName = resolveInternalPackageNameLPr(packageName,
- PackageManager.VERSION_CODE_HIGHEST);
-
- // Apps that target O see flags for all protection levels.
- final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
- return protectionLevel;
- }
- if (ps.appId != appId) {
- return protectionLevel;
- }
-
- final PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- return protectionLevel;
- }
- if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
- return protectionLevelMasked;
- }
-
- return protectionLevel;
+ return mPermissionManager.getPermissionInfo(name, packageName, flags, getCallingUid());
}
@Override
- public @Nullable ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String group,
+ public @Nullable ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String groupName,
int flags) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return null;
- }
- // reader
+ // TODO Move this to PermissionManager when mPermissionGroups is moved there
synchronized (mPackages) {
- if (group != null && !mPermissionGroups.containsKey(group)) {
+ if (groupName != null && !mPermissionGroups.containsKey(groupName)) {
// This is thrown as NameNotFoundException
return null;
}
-
- ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
- for (BasePermission p : mSettings.mPermissions.values()) {
- if (group == null) {
- if (p.perm == null || p.perm.info.group == null) {
- out.add(generatePermissionInfo(p, flags));
- }
- } else {
- if (p.perm != null && group.equals(p.perm.info.group)) {
- out.add(PackageParser.generatePermissionInfo(p.perm, flags));
- }
- }
- }
- return new ParceledListSlice<>(out);
}
+ return new ParceledListSlice<>(
+ mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid()));
}
@Override
@@ -4456,7 +4329,7 @@
int filterCallingUid, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForApplication(flags, userId, packageName);
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get application info");
// writer
@@ -4765,7 +4638,8 @@
triaged = false;
}
if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
+ mPermissionManager.enforceCrossUserPermission(
+ Binder.getCallingUid(), userId, false, false,
"MATCH_ANY_USER flag requires INTERACT_ACROSS_USERS permission at "
+ Debug.getCallers(5));
} else if ((flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0 && isCallerSystemUser
@@ -4894,7 +4768,7 @@
int filterCallingUid, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId, component);
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get activity info");
synchronized (mPackages) {
PackageParser.Activity a = mActivities.mActivities.get(component);
@@ -4953,7 +4827,7 @@
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId, component);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get receiver info");
synchronized (mPackages) {
PackageParser.Activity a = mReceivers.mActivities.get(component);
@@ -5090,7 +4964,7 @@
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId, component);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get service info");
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
@@ -5114,7 +4988,7 @@
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId, component);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get provider info");
synchronized (mPackages) {
PackageParser.Provider p = mProviders.mProviders.get(component);
@@ -5277,39 +5151,7 @@
@Override
public int checkPermission(String permName, String pkgName, int userId) {
- if (!sUserManager.exists(userId)) {
- return PackageManager.PERMISSION_DENIED;
- }
- final int callingUid = Binder.getCallingUid();
-
- synchronized (mPackages) {
- final PackageParser.Package p = mPackages.get(pkgName);
- if (p != null && p.mExtras != null) {
- final PackageSetting ps = (PackageSetting) p.mExtras;
- if (filterAppAccessLPr(ps, callingUid, userId)) {
- return PackageManager.PERMISSION_DENIED;
- }
- final boolean instantApp = ps.getInstantApp(userId);
- final PermissionsState permissionsState = ps.getPermissionsState();
- if (permissionsState.hasPermission(permName, userId)) {
- if (instantApp) {
- BasePermission bp = mSettings.mPermissions.get(permName);
- if (bp != null && bp.isInstant()) {
- return PackageManager.PERMISSION_GRANTED;
- }
- } else {
- return PackageManager.PERMISSION_GRANTED;
- }
- }
- // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
- if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
- .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- }
- }
-
- return PackageManager.PERMISSION_DENIED;
+ return mPermissionManager.checkPermission(permName, pkgName, getCallingUid(), userId);
}
@Override
@@ -5340,8 +5182,7 @@
final PermissionsState permissionsState = settingBase.getPermissionsState();
if (permissionsState.hasPermission(permName, userId)) {
if (isUidInstantApp) {
- BasePermission bp = mSettings.mPermissions.get(permName);
- if (bp != null && bp.isInstant()) {
+ if (mPermissionManager.isPermissionInstant(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
@@ -5410,448 +5251,49 @@
}
}
- /**
- * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS
- * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller.
- * @param checkShell whether to prevent shell from access if there's a debugging restriction
- * @param message the message to log on security exception
- */
- void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission,
- boolean checkShell, String message) {
- if (userId < 0) {
- throw new IllegalArgumentException("Invalid userId " + userId);
- }
- if (checkShell) {
- enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
- }
- if (userId == UserHandle.getUserId(callingUid)) return;
- if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
- if (requireFullPermission) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
- } else {
- try {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
- } catch (SecurityException se) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS, message);
- }
- }
- }
- }
-
- void enforceShellRestriction(String restriction, int callingUid, int userHandle) {
- if (callingUid == Process.SHELL_UID) {
- if (userHandle >= 0
- && sUserManager.hasUserRestriction(restriction, userHandle)) {
- throw new SecurityException("Shell does not have permission to access user "
- + userHandle);
- } else if (userHandle < 0) {
- Slog.e(TAG, "Unable to check shell permission for user " + userHandle + "\n\t"
- + Debug.getCallers(3));
- }
- }
- }
-
- private BasePermission findPermissionTreeLP(String permName) {
- for(BasePermission bp : mSettings.mPermissionTrees.values()) {
- if (permName.startsWith(bp.name) &&
- permName.length() > bp.name.length() &&
- permName.charAt(bp.name.length()) == '.') {
- return bp;
- }
- }
- return null;
- }
-
- private BasePermission checkPermissionTreeLP(String permName) {
- if (permName != null) {
- BasePermission bp = findPermissionTreeLP(permName);
- if (bp != null) {
- if (bp.uid == UserHandle.getAppId(Binder.getCallingUid())) {
- return bp;
- }
- throw new SecurityException("Calling uid "
- + Binder.getCallingUid()
- + " is not allowed to add to permission tree "
- + bp.name + " owned by uid " + bp.uid);
- }
- }
- throw new SecurityException("No permission tree found for " + permName);
- }
-
- static boolean compareStrings(CharSequence s1, CharSequence s2) {
- if (s1 == null) {
- return s2 == null;
- }
- if (s2 == null) {
- return false;
- }
- if (s1.getClass() != s2.getClass()) {
- return false;
- }
- return s1.equals(s2);
- }
-
- static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
- if (pi1.icon != pi2.icon) return false;
- if (pi1.logo != pi2.logo) return false;
- if (pi1.protectionLevel != pi2.protectionLevel) return false;
- if (!compareStrings(pi1.name, pi2.name)) return false;
- if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
- // We'll take care of setting this one.
- if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
- // These are not currently stored in settings.
- //if (!compareStrings(pi1.group, pi2.group)) return false;
- //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
- //if (pi1.labelRes != pi2.labelRes) return false;
- //if (pi1.descriptionRes != pi2.descriptionRes) return false;
- return true;
- }
-
- int permissionInfoFootprint(PermissionInfo info) {
- int size = info.name.length();
- if (info.nonLocalizedLabel != null) size += info.nonLocalizedLabel.length();
- if (info.nonLocalizedDescription != null) size += info.nonLocalizedDescription.length();
- return size;
- }
-
- int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
- int size = 0;
- for (BasePermission perm : mSettings.mPermissions.values()) {
- if (perm.uid == tree.uid) {
- size += perm.name.length() + permissionInfoFootprint(perm.perm.info);
- }
- }
- return size;
- }
-
- void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) {
- // We calculate the max size of permissions defined by this uid and throw
- // if that plus the size of 'info' would exceed our stated maximum.
- if (tree.uid != Process.SYSTEM_UID) {
- final int curTreeSize = calculateCurrentPermissionFootprintLocked(tree);
- if (curTreeSize + permissionInfoFootprint(info) > MAX_PERMISSION_TREE_FOOTPRINT) {
- throw new SecurityException("Permission tree size cap exceeded");
- }
- }
- }
-
- boolean addPermissionLocked(PermissionInfo info, boolean async) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- throw new SecurityException("Instant apps can't add permissions");
- }
- if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
- throw new SecurityException("Label must be specified in permission");
- }
- BasePermission tree = checkPermissionTreeLP(info.name);
- BasePermission bp = mSettings.mPermissions.get(info.name);
- boolean added = bp == null;
- boolean changed = true;
- int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
- if (added) {
- enforcePermissionCapLocked(info, tree);
- bp = new BasePermission(info.name, tree.sourcePackage,
- BasePermission.TYPE_DYNAMIC);
- } else if (bp.type != BasePermission.TYPE_DYNAMIC) {
- throw new SecurityException(
- "Not allowed to modify non-dynamic permission "
- + info.name);
- } else {
- if (bp.protectionLevel == fixedLevel
- && bp.perm.owner.equals(tree.perm.owner)
- && bp.uid == tree.uid
- && comparePermissionInfos(bp.perm.info, info)) {
- changed = false;
- }
- }
- bp.protectionLevel = fixedLevel;
- info = new PermissionInfo(info);
- info.protectionLevel = fixedLevel;
- bp.perm = new PackageParser.Permission(tree.perm.owner, info);
- bp.perm.info.packageName = tree.perm.info.packageName;
- bp.uid = tree.uid;
- if (added) {
- mSettings.mPermissions.put(info.name, bp);
- }
- if (changed) {
- if (!async) {
- mSettings.writeLPr();
- } else {
- scheduleWriteSettingsLocked();
- }
- }
- return added;
+ boolean addPermission(PermissionInfo info, final boolean async) {
+ return mPermissionManager.addPermission(
+ info, async, getCallingUid(), new PermissionCallback() {
+ @Override
+ public void onPermissionChanged() {
+ if (!async) {
+ mSettings.writeLPr();
+ } else {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ });
}
@Override
public boolean addPermission(PermissionInfo info) {
synchronized (mPackages) {
- return addPermissionLocked(info, false);
+ return addPermission(info, false);
}
}
@Override
public boolean addPermissionAsync(PermissionInfo info) {
synchronized (mPackages) {
- return addPermissionLocked(info, true);
+ return addPermission(info, true);
}
}
@Override
- public void removePermission(String name) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- throw new SecurityException("Instant applications don't have access to this method");
- }
- synchronized (mPackages) {
- checkPermissionTreeLP(name);
- BasePermission bp = mSettings.mPermissions.get(name);
- if (bp != null) {
- if (bp.type != BasePermission.TYPE_DYNAMIC) {
- throw new SecurityException(
- "Not allowed to modify non-dynamic permission "
- + name);
- }
- mSettings.mPermissions.remove(name);
- mSettings.writeLPr();
- }
- }
- }
-
- private static void enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(
- PackageParser.Package pkg, BasePermission bp) {
- int index = pkg.requestedPermissions.indexOf(bp.name);
- if (index == -1) {
- throw new SecurityException("Package " + pkg.packageName
- + " has not requested permission " + bp.name);
- }
- if (!bp.isRuntime() && !bp.isDevelopment()) {
- throw new SecurityException("Permission " + bp.name
- + " is not a changeable permission type");
- }
+ public void removePermission(String permName) {
+ mPermissionManager.removePermission(permName, getCallingUid(), mPermissionCallback);
}
@Override
- public void grantRuntimePermission(String packageName, String name, final int userId) {
- grantRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
- }
-
- private void grantRuntimePermission(String packageName, String name, final int userId,
- boolean overridePolicy) {
- if (!sUserManager.exists(userId)) {
- Log.e(TAG, "No such user:" + userId);
- return;
- }
- final int callingUid = Binder.getCallingUid();
-
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
- "grantRuntimePermission");
-
- enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, true /* checkShell */,
- "grantRuntimePermission");
-
- final int uid;
- final PackageSetting ps;
-
- synchronized (mPackages) {
- final PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- final BasePermission bp = mSettings.mPermissions.get(name);
- if (bp == null) {
- throw new IllegalArgumentException("Unknown permission: " + name);
- }
- ps = (PackageSetting) pkg.mExtras;
- if (ps == null
- || filterAppAccessLPr(ps, callingUid, userId)) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
-
- enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);
-
- // If a permission review is required for legacy apps we represent
- // their permissions as always granted runtime ones since we need
- // to keep the review required permission flag per user while an
- // install permission's state is shared across all users.
- if (mPermissionReviewRequired
- && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
- && bp.isRuntime()) {
- return;
- }
-
- uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
-
- final PermissionsState permissionsState = ps.getPermissionsState();
-
- final int flags = permissionsState.getPermissionFlags(name, userId);
- if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
- throw new SecurityException("Cannot grant system fixed permission "
- + name + " for package " + packageName);
- }
- if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
- throw new SecurityException("Cannot grant policy fixed permission "
- + name + " for package " + packageName);
- }
-
- if (bp.isDevelopment()) {
- // Development permissions must be handled specially, since they are not
- // normal runtime permissions. For now they apply to all users.
- if (permissionsState.grantInstallPermission(bp) !=
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- scheduleWriteSettingsLocked();
- }
- return;
- }
-
- if (ps.getInstantApp(userId) && !bp.isInstant()) {
- throw new SecurityException("Cannot grant non-ephemeral permission"
- + name + " for package " + packageName);
- }
-
- if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
- Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
- return;
- }
-
- final int result = permissionsState.grantRuntimePermission(bp, userId);
- switch (result) {
- case PermissionsState.PERMISSION_OPERATION_FAILURE: {
- return;
- }
-
- case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
- final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
- }
- });
- }
- break;
- }
-
- if (bp.isRuntime()) {
- logPermissionGranted(mContext, name, packageName);
- }
-
- mOnPermissionChangeListeners.onPermissionsChanged(uid);
-
- // Not critical if that is lost - app has to request again.
- mSettings.writeRuntimePermissionsForUserLPr(userId, false);
- }
-
- // Only need to do this if user is initialized. Otherwise it's a new user
- // and there are no processes running as the user yet and there's no need
- // to make an expensive call to remount processes for the changed permissions.
- if (READ_EXTERNAL_STORAGE.equals(name)
- || WRITE_EXTERNAL_STORAGE.equals(name)) {
- final long token = Binder.clearCallingIdentity();
- try {
- if (sUserManager.isInitialized(userId)) {
- StorageManagerInternal storageManagerInternal = LocalServices.getService(
- StorageManagerInternal.class);
- storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
+ public void grantRuntimePermission(String packageName, String permName, final int userId) {
+ mPermissionManager.grantRuntimePermission(permName, packageName, false /*overridePolicy*/,
+ getCallingUid(), userId, mPermissionCallback);
}
@Override
- public void revokeRuntimePermission(String packageName, String name, int userId) {
- revokeRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
- }
-
- private void revokeRuntimePermission(String packageName, String name, int userId,
- boolean overridePolicy) {
- if (!sUserManager.exists(userId)) {
- Log.e(TAG, "No such user:" + userId);
- return;
- }
-
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
- "revokeRuntimePermission");
-
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
- true /* requireFullPermission */, true /* checkShell */,
- "revokeRuntimePermission");
-
- final int appId;
-
- synchronized (mPackages) {
- final PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null
- || filterAppAccessLPr(ps, Binder.getCallingUid(), userId)) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- final BasePermission bp = mSettings.mPermissions.get(name);
- if (bp == null) {
- throw new IllegalArgumentException("Unknown permission: " + name);
- }
-
- enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);
-
- // If a permission review is required for legacy apps we represent
- // their permissions as always granted runtime ones since we need
- // to keep the review required permission flag per user while an
- // install permission's state is shared across all users.
- if (mPermissionReviewRequired
- && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
- && bp.isRuntime()) {
- return;
- }
-
- final PermissionsState permissionsState = ps.getPermissionsState();
-
- final int flags = permissionsState.getPermissionFlags(name, userId);
- if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
- throw new SecurityException("Cannot revoke system fixed permission "
- + name + " for package " + packageName);
- }
- if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
- throw new SecurityException("Cannot revoke policy fixed permission "
- + name + " for package " + packageName);
- }
-
- if (bp.isDevelopment()) {
- // Development permissions must be handled specially, since they are not
- // normal runtime permissions. For now they apply to all users.
- if (permissionsState.revokeInstallPermission(bp) !=
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- scheduleWriteSettingsLocked();
- }
- return;
- }
-
- if (permissionsState.revokeRuntimePermission(bp, userId) ==
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- return;
- }
-
- if (bp.isRuntime()) {
- logPermissionRevoked(mContext, name, packageName);
- }
-
- mOnPermissionChangeListeners.onPermissionsChanged(pkg.applicationInfo.uid);
-
- // Critical, after this call app should never have the permission.
- mSettings.writeRuntimePermissionsForUserLPr(userId, true);
-
- appId = UserHandle.getAppId(pkg.applicationInfo.uid);
- }
-
- killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
+ public void revokeRuntimePermission(String packageName, String permName, int userId) {
+ mPermissionManager.revokeRuntimePermission(permName, packageName, false /*overridePolicy*/,
+ getCallingUid(), userId, mPermissionCallback);
}
/**
@@ -5944,91 +5386,16 @@
}
@Override
- public int getPermissionFlags(String name, String packageName, int userId) {
- if (!sUserManager.exists(userId)) {
- return 0;
- }
-
- enforceGrantRevokeRuntimePermissionPermissions("getPermissionFlags");
-
- final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, false /* checkShell */,
- "getPermissionFlags");
-
- synchronized (mPackages) {
- final PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- return 0;
- }
- final BasePermission bp = mSettings.mPermissions.get(name);
- if (bp == null) {
- return 0;
- }
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null
- || filterAppAccessLPr(ps, callingUid, userId)) {
- return 0;
- }
- PermissionsState permissionsState = ps.getPermissionsState();
- return permissionsState.getPermissionFlags(name, userId);
- }
+ public int getPermissionFlags(String permName, String packageName, int userId) {
+ return mPermissionManager.getPermissionFlags(permName, packageName, getCallingUid(), userId);
}
@Override
- public void updatePermissionFlags(String name, String packageName, int flagMask,
+ public void updatePermissionFlags(String permName, String packageName, int flagMask,
int flagValues, int userId) {
- if (!sUserManager.exists(userId)) {
- return;
- }
-
- enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlags");
-
- final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, true /* checkShell */,
- "updatePermissionFlags");
-
- // Only the system can change these flags and nothing else.
- if (getCallingUid() != Process.SYSTEM_UID) {
- flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
- flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
- flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
- flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
- flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
- }
-
- synchronized (mPackages) {
- final PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null
- || filterAppAccessLPr(ps, callingUid, userId)) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
-
- final BasePermission bp = mSettings.mPermissions.get(name);
- if (bp == null) {
- throw new IllegalArgumentException("Unknown permission: " + name);
- }
-
- PermissionsState permissionsState = ps.getPermissionsState();
-
- boolean hadState = permissionsState.getRuntimePermissionState(name, userId) != null;
-
- if (permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues)) {
- // Install and runtime permissions are stored in different places,
- // so figure out what permission changed and persist the change.
- if (permissionsState.getInstallPermissionState(name) != null) {
- scheduleWriteSettingsLocked();
- } else if (permissionsState.getRuntimePermissionState(name, userId) != null
- || hadState) {
- mSettings.writeRuntimePermissionsForUserLPr(userId, false);
- }
- }
- }
+ mPermissionManager.updatePermissionFlags(
+ permName, packageName, flagMask, flagValues, getCallingUid(), userId,
+ mPermissionCallback);
}
/**
@@ -6037,52 +5404,16 @@
*/
@Override
public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) {
- if (!sUserManager.exists(userId)) {
- return;
- }
-
- enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlagsForAllApps");
-
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
- true /* requireFullPermission */, true /* checkShell */,
- "updatePermissionFlagsForAllApps");
-
- // Only the system can change system fixed flags.
- if (getCallingUid() != Process.SYSTEM_UID) {
- flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
- flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
- }
-
synchronized (mPackages) {
- boolean changed = false;
- final int packageCount = mPackages.size();
- for (int pkgIndex = 0; pkgIndex < packageCount; pkgIndex++) {
- final PackageParser.Package pkg = mPackages.valueAt(pkgIndex);
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null) {
- continue;
- }
- PermissionsState permissionsState = ps.getPermissionsState();
- changed |= permissionsState.updatePermissionFlagsForAllPermissions(
- userId, flagMask, flagValues);
- }
+ final boolean changed = mPermissionManager.updatePermissionFlagsForAllApps(
+ flagMask, flagValues, getCallingUid(), userId, mPackages.values(),
+ mPermissionCallback);
if (changed) {
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
}
}
- private void enforceGrantRevokeRuntimePermissionPermissions(String message) {
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
- != PackageManager.PERMISSION_GRANTED
- && mContext.checkCallingOrSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(message + " requires "
- + Manifest.permission.GRANT_RUNTIME_PERMISSIONS + " or "
- + Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
- }
- }
-
@Override
public boolean shouldShowRequestPermissionRationale(String permissionName,
String packageName, int userId) {
@@ -6274,7 +5605,7 @@
* <br />
* {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ.
*/
- static int compareSignatures(Signature[] s1, Signature[] s2) {
+ public static int compareSignatures(Signature[] s1, Signature[] s2) {
if (s1 == null) {
return s2 == null
? PackageManager.SIGNATURE_NEITHER_SIGNED
@@ -6628,9 +5959,14 @@
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
return resolveIntentInternal(
- intent, resolvedType, flags, userId, false /*includeInstantApps*/);
+ intent, resolvedType, flags, userId, false /*resolveForStart*/);
}
+ /**
+ * Normally instant apps can only be resolved when they're visible to the caller.
+ * However, if {@code resolveForStart} is {@code true}, all instant apps are visible
+ * since we need to allow the system to start any installed application.
+ */
private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
int flags, int userId, boolean resolveForStart) {
try {
@@ -6639,7 +5975,7 @@
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForResolve(flags, userId, intent, callingUid, resolveForStart);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
@@ -7220,7 +6556,7 @@
boolean resolveForStart, boolean allowDynamicSplits) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */,
"query intent activities");
final String pkgName = intent.getPackage();
@@ -7975,7 +7311,7 @@
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForResolve(flags, userId, intent, callingUid,
false /*includeInstantApps*/);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent activity options");
final String resultsAction = intent.getAction();
@@ -8156,7 +7492,7 @@
String resolvedType, int flags, int userId, boolean allowDynamicSplits) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
@@ -8268,7 +7604,7 @@
String resolvedType, int flags, int userId, int callingUid,
boolean includeInstantApps) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
@@ -8508,7 +7844,7 @@
if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForPackage(flags, userId, null);
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"get installed packages");
@@ -8595,7 +7931,7 @@
String[] permissions, int flags, int userId) {
if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForPackage(flags, userId, permissions);
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"get packages holding permissions");
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
@@ -8700,7 +8036,7 @@
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
"getEphemeralApplications");
}
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getEphemeralApplications");
synchronized (mPackages) {
@@ -8715,7 +8051,7 @@
@Override
public boolean isInstantApp(String packageName, int userId) {
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"isInstantApp");
if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
@@ -8748,7 +8084,7 @@
return null;
}
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getInstantAppCookie");
if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
@@ -8766,7 +8102,7 @@
return true;
}
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, true /* checkShell */,
"setInstantAppCookie");
if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
@@ -8788,7 +8124,7 @@
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
"getInstantAppIcon");
}
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getInstantAppIcon");
@@ -8848,6 +8184,10 @@
@Override
public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
+ return resolveContentProviderInternal(name, flags, userId);
+ }
+
+ private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId, name);
final String instantAppPkgName = getInstantAppPackageName(Binder.getCallingUid());
@@ -9106,7 +8446,7 @@
return fname;
}
- static void reportSettingsProblem(int priority, String msg) {
+ public static void reportSettingsProblem(int priority, String msg) {
logCriticalInfo(priority, msg);
}
@@ -9739,7 +9079,7 @@
* and {@code numberOfPackagesFailed}.
*/
private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog,
- String compilerFilter, boolean bootComplete) {
+ final String compilerFilter, boolean bootComplete) {
int numberOfPackagesVisited = 0;
int numberOfPackagesOptimized = 0;
@@ -9750,6 +9090,8 @@
for (PackageParser.Package pkg : pkgs) {
numberOfPackagesVisited++;
+ boolean useProfileForDexopt = false;
+
if ((isFirstBoot() || isUpgrade()) && isSystemApp(pkg)) {
// Copy over initial preopt profiles since we won't get any JIT samples for methods
// that are already compiled.
@@ -9763,11 +9105,30 @@
if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
pkg.applicationInfo.uid, pkg.packageName)) {
Log.e(TAG, "Installer failed to copy system profile!");
+ } else {
+ // Disabled as this causes speed-profile compilation during first boot
+ // even if things are already compiled.
+ // useProfileForDexopt = true;
}
} catch (Exception e) {
Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ",
e);
}
+ } else {
+ PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);
+ // Handle compressed APKs in this path. Only do this for stubs with profiles to
+ // minimize the number off apps being speed-profile compiled during first boot.
+ // The other paths will not change the filter.
+ if (disabledPs != null && disabledPs.pkg.isStub) {
+ // The package is the stub one, remove the stub suffix to get the normal
+ // package and APK names.
+ String systemProfilePath =
+ getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, "");
+ File systemProfile = new File(systemProfilePath);
+ // Use the profile for compilation if there exists one for the same package
+ // in the system partition.
+ useProfileForDexopt = systemProfile.exists();
+ }
}
}
@@ -9796,17 +9157,13 @@
}
}
- // If the OTA updates a system app which was previously preopted to a non-preopted state
- // the app might end up being verified at runtime. That's because by default the apps
- // are verify-profile but for preopted apps there's no profile.
- // Do a hacky check to ensure that if we have no profiles (a reasonable indication
- // that before the OTA the app was preopted) the app gets compiled with a non-profile
- // filter (by default 'quicken').
- // Note that at this stage unused apps are already filtered.
- if (isSystemApp(pkg) &&
- DexFile.isProfileGuidedCompilerFilter(compilerFilter) &&
- !Environment.getReferenceProfile(pkg.packageName).exists()) {
- compilerFilter = getNonProfileGuidedCompilerFilter(compilerFilter);
+ String pkgCompilerFilter = compilerFilter;
+ if (useProfileForDexopt) {
+ // Use background dexopt mode to try and use the profile. Note that this does not
+ // guarantee usage of the profile.
+ pkgCompilerFilter =
+ PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
+ PackageManagerService.REASON_BACKGROUND_DEXOPT);
}
// checkProfiles is false to avoid merging profiles during boot which
@@ -9817,7 +9174,7 @@
int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
pkg.packageName,
- compilerFilter,
+ pkgCompilerFilter,
dexoptFlags));
switch (primaryDexOptStaus) {
@@ -10376,7 +9733,8 @@
}
}
- private void addSharedLibraryLPr(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file,
+ private void addSharedLibraryLPr(Set<String> usesLibraryFiles,
+ SharedLibraryEntry file,
PackageParser.Package changingLib) {
if (file.path != null) {
usesLibraryFiles.add(file.path);
@@ -10405,7 +9763,10 @@
if (pkg == null) {
return;
}
- ArraySet<String> usesLibraryFiles = null;
+ // The collection used here must maintain the order of addition (so
+ // that libraries are searched in the correct order) and must have no
+ // duplicates.
+ Set<String> usesLibraryFiles = null;
if (pkg.usesLibraries != null) {
usesLibraryFiles = addSharedLibrariesLPw(pkg.usesLibraries,
null, null, pkg.packageName, changingLib, true,
@@ -10429,10 +9790,10 @@
}
}
- private ArraySet<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
+ private Set<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
@Nullable int[] requiredVersions, @Nullable String[][] requiredCertDigests,
@NonNull String packageName, @Nullable PackageParser.Package changingLib,
- boolean required, int targetSdk, @Nullable ArraySet<String> outUsedLibraries)
+ boolean required, int targetSdk, @Nullable Set<String> outUsedLibraries)
throws PackageManagerException {
final int libCount = requestedLibraries.size();
for (int i = 0; i < libCount; i++) {
@@ -10498,7 +9859,9 @@
}
if (outUsedLibraries == null) {
- outUsedLibraries = new ArraySet<>();
+ // Use LinkedHashSet to preserve the order of files added to
+ // usesLibraryFiles while eliminating duplicates.
+ outUsedLibraries = new LinkedHashSet<>();
}
addSharedLibraryLPr(outUsedLibraries, libEntry, changingLib);
}
@@ -10691,6 +10054,12 @@
assertPackageIsValid(pkg, policyFlags, scanFlags);
+ if (Build.IS_DEBUGGABLE &&
+ pkg.isPrivilegedApp() &&
+ !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+ PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
+ }
+
// Initialize package source and resource directories
final File scanFile = new File(pkg.codePath);
final File destCodeFile = new File(pkg.applicationInfo.getCodePath());
@@ -11854,84 +11223,25 @@
}
}
- ArrayMap<String, BasePermission> permissionMap =
- p.tree ? mSettings.mPermissionTrees
- : mSettings.mPermissions;
- BasePermission bp = permissionMap.get(p.info.name);
-
- // Allow system apps to redefine non-system permissions
- if (bp != null && !Objects.equals(bp.sourcePackage, p.info.packageName)) {
- final boolean currentOwnerIsSystem = (bp.perm != null
- && isSystemApp(bp.perm.owner));
- if (isSystemApp(p.owner)) {
- if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {
- // It's a built-in permission and no owner, take ownership now
- bp.packageSetting = pkgSetting;
- bp.perm = p;
- bp.uid = pkg.applicationInfo.uid;
- bp.sourcePackage = p.info.packageName;
- p.info.flags |= PermissionInfo.FLAG_INSTALLED;
- } else if (!currentOwnerIsSystem) {
- String msg = "New decl " + p.owner + " of permission "
- + p.info.name + " is system; overriding " + bp.sourcePackage;
- reportSettingsProblem(Log.WARN, msg);
- bp = null;
- }
- }
- }
-
- if (bp == null) {
- bp = new BasePermission(p.info.name, p.info.packageName,
- BasePermission.TYPE_NORMAL);
+ // TODO Move to PermissionManager once mPermissionTrees moves there.
+// p.tree ? mSettings.mPermissionTrees
+// : mSettings.mPermissions;
+// final BasePermission bp = BasePermission.createOrUpdate(
+// permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees, chatty);
+// permissionMap.put(p.info.name, bp);
+ if (p.tree) {
+ final ArrayMap<String, BasePermission> permissionMap =
+ mSettings.mPermissionTrees;
+ final BasePermission bp = BasePermission.createOrUpdate(
+ permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees,
+ chatty);
permissionMap.put(p.info.name, bp);
+ } else {
+ final BasePermission bp = BasePermission.createOrUpdate(
+ (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name),
+ p, pkg, mSettings.mPermissionTrees, chatty);
+ mPermissionManager.putPermissionTEMP(p.info.name, bp);
}
-
- if (bp.perm == null) {
- if (bp.sourcePackage == null
- || bp.sourcePackage.equals(p.info.packageName)) {
- BasePermission tree = findPermissionTreeLP(p.info.name);
- if (tree == null
- || tree.sourcePackage.equals(p.info.packageName)) {
- bp.packageSetting = pkgSetting;
- bp.perm = p;
- bp.uid = pkg.applicationInfo.uid;
- bp.sourcePackage = p.info.packageName;
- p.info.flags |= PermissionInfo.FLAG_INSTALLED;
- if (chatty) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
- }
- r.append(p.info.name);
- }
- } else {
- Slog.w(TAG, "Permission " + p.info.name + " from package "
- + p.info.packageName + " ignored: base tree "
- + tree.name + " is from package "
- + tree.sourcePackage);
- }
- } else {
- Slog.w(TAG, "Permission " + p.info.name + " from package "
- + p.info.packageName + " ignored: original from "
- + bp.sourcePackage);
- }
- } else if (chatty) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
- }
- r.append("DUP:");
- r.append(p.info.name);
- }
- if (bp.perm == p) {
- bp.protectionLevel = p.info.protectionLevel;
- }
- }
-
- if (r != null) {
- if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permissions: " + r);
}
N = pkg.instrumentation.size();
@@ -12661,12 +11971,12 @@
r = null;
for (i=0; i<N; i++) {
PackageParser.Permission p = pkg.permissions.get(i);
- BasePermission bp = mSettings.mPermissions.get(p.info.name);
+ BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name);
if (bp == null) {
bp = mSettings.mPermissionTrees.get(p.info.name);
}
- if (bp != null && bp.perm == p) {
- bp.perm = null;
+ if (bp != null && bp.isPermission(p)) {
+ bp.setPermission(null);
if (DEBUG_REMOVE && chatty) {
if (r == null) {
r = new StringBuilder(256);
@@ -12691,8 +12001,7 @@
r = null;
for (i=0; i<N; i++) {
String perm = pkg.requestedPermissions.get(i);
- BasePermission bp = mSettings.mPermissions.get(perm);
- if (bp != null && (bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+ if (mPermissionManager.isPermissionAppOp(perm)) {
ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(perm);
if (appOpPkgs != null) {
appOpPkgs.remove(pkg.packageName);
@@ -12797,23 +12106,32 @@
private void updatePermissionsLPw(String changingPkg,
PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) {
+ // TODO: Most of the methods exposing BasePermission internals [source package name,
+ // etc..] shouldn't be needed. Instead, when we've parsed a permission that doesn't
+ // have package settings, we should make note of it elsewhere [map between
+ // source package name and BasePermission] and cycle through that here. Then we
+ // define a single method on BasePermission that takes a PackageSetting, changing
+ // package name and a package.
+ // NOTE: With this approach, we also don't need to tree trees differently than
+ // normal permissions. Today, we need two separate loops because these BasePermission
+ // objects are stored separately.
// Make sure there are no dangling permission trees.
Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
while (it.hasNext()) {
final BasePermission bp = it.next();
- if (bp.packageSetting == null) {
+ if (bp.getSourcePackageSetting() == null) {
// We may not yet have parsed the package, so just see if
// we still know about its settings.
- bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);
+ bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName()));
}
- if (bp.packageSetting == null) {
- Slog.w(TAG, "Removing dangling permission tree: " + bp.name
- + " from package " + bp.sourcePackage);
+ if (bp.getSourcePackageSetting() == null) {
+ Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
+ + " from package " + bp.getSourcePackageName());
it.remove();
- } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {
- if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
- Slog.i(TAG, "Removing old permission tree: " + bp.name
- + " from package " + bp.sourcePackage);
+ } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) {
+ if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) {
+ Slog.i(TAG, "Removing old permission tree: " + bp.getName()
+ + " from package " + bp.getSourcePackageName());
flags |= UPDATE_PERMISSIONS_ALL;
it.remove();
}
@@ -12822,40 +12140,28 @@
// Make sure all dynamic permissions have been assigned to a package,
// and make sure there are no dangling permissions.
- it = mSettings.mPermissions.values().iterator();
- while (it.hasNext()) {
- final BasePermission bp = it.next();
- if (bp.type == BasePermission.TYPE_DYNAMIC) {
- if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
- + bp.name + " pkg=" + bp.sourcePackage
- + " info=" + bp.pendingInfo);
- if (bp.packageSetting == null && bp.pendingInfo != null) {
- final BasePermission tree = findPermissionTreeLP(bp.name);
- if (tree != null && tree.perm != null) {
- bp.packageSetting = tree.packageSetting;
- bp.perm = new PackageParser.Permission(tree.perm.owner,
- new PermissionInfo(bp.pendingInfo));
- bp.perm.info.packageName = tree.perm.info.packageName;
- bp.perm.info.name = bp.name;
- bp.uid = tree.uid;
- }
- }
+ final Iterator<BasePermission> permissionIter =
+ mPermissionManager.getPermissionIteratorTEMP();
+ while (permissionIter.hasNext()) {
+ final BasePermission bp = permissionIter.next();
+ if (bp.isDynamic()) {
+ bp.updateDynamicPermission(mSettings.mPermissionTrees);
}
- if (bp.packageSetting == null) {
+ if (bp.getSourcePackageSetting() == null) {
// We may not yet have parsed the package, so just see if
// we still know about its settings.
- bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);
+ bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName()));
}
- if (bp.packageSetting == null) {
- Slog.w(TAG, "Removing dangling permission: " + bp.name
- + " from package " + bp.sourcePackage);
- it.remove();
- } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {
- if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
- Slog.i(TAG, "Removing old permission: " + bp.name
- + " from package " + bp.sourcePackage);
+ if (bp.getSourcePackageSetting() == null) {
+ Slog.w(TAG, "Removing dangling permission: " + bp.getName()
+ + " from package " + bp.getSourcePackageName());
+ permissionIter.remove();
+ } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) {
+ if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) {
+ Slog.i(TAG, "Removing old permission: " + bp.getName()
+ + " from package " + bp.getSourcePackageName());
flags |= UPDATE_PERMISSIONS_ALL;
- it.remove();
+ permissionIter.remove();
}
}
}
@@ -12924,8 +12230,9 @@
// the runtime ones are written only if changed. The only cases of
// changed runtime permissions here are promotion of an install to
// runtime and revocation of a runtime from a shared user.
- changedRuntimePermissionUserIds = revokeUnusedSharedUserPermissionsLPw(
- ps.sharedUser, UserManagerService.getInstance().getUserIds());
+ changedRuntimePermissionUserIds =
+ mPermissionManager.revokeUnusedSharedUserPermissions(
+ ps.sharedUser, UserManagerService.getInstance().getUserIds());
if (!ArrayUtils.isEmpty(changedRuntimePermissionUserIds)) {
runtimePermissionsRevoked = true;
}
@@ -12937,7 +12244,7 @@
final int N = pkg.requestedPermissions.size();
for (int i=0; i<N; i++) {
final String name = pkg.requestedPermissions.get(i);
- final BasePermission bp = mSettings.mPermissions.get(name);
+ final BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(name);
final boolean appSupportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
>= Build.VERSION_CODES.M;
@@ -12945,7 +12252,7 @@
Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
}
- if (bp == null || bp.packageSetting == null) {
+ if (bp == null || bp.getSourcePackageSetting() == null) {
if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, "Unknown permission " + name
@@ -12959,7 +12266,7 @@
// Limit ephemeral apps to ephemeral allowed permissions.
if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
if (DEBUG_PERMISSIONS) {
- Log.i(TAG, "Denying non-ephemeral permission " + bp.name + " for package "
+ Log.i(TAG, "Denying non-ephemeral permission " + bp.getName() + " for package "
+ pkg.packageName);
}
continue;
@@ -12967,64 +12274,57 @@
if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
if (DEBUG_PERMISSIONS) {
- Log.i(TAG, "Denying runtime-only permission " + bp.name + " for package "
+ Log.i(TAG, "Denying runtime-only permission " + bp.getName() + " for package "
+ pkg.packageName);
}
continue;
}
- final String perm = bp.name;
+ final String perm = bp.getName();
boolean allowedSig = false;
int grant = GRANT_DENIED;
// Keep track of app op permissions.
- if ((bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
- ArraySet<String> pkgs = mAppOpPermissionPackages.get(bp.name);
+ if (bp.isAppOp()) {
+ ArraySet<String> pkgs = mAppOpPermissionPackages.get(perm);
if (pkgs == null) {
pkgs = new ArraySet<>();
- mAppOpPermissionPackages.put(bp.name, pkgs);
+ mAppOpPermissionPackages.put(perm, pkgs);
}
pkgs.add(pkg.packageName);
}
- final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
- switch (level) {
- case PermissionInfo.PROTECTION_NORMAL: {
- // For all apps normal permissions are install time ones.
+ if (bp.isNormal()) {
+ // For all apps normal permissions are install time ones.
+ grant = GRANT_INSTALL;
+ } else if (bp.isRuntime()) {
+ // If a permission review is required for legacy apps we represent
+ // their permissions as always granted runtime ones since we need
+ // to keep the review required permission flag per user while an
+ // install permission's state is shared across all users.
+ if (!appSupportsRuntimePermissions && !mPermissionReviewRequired) {
+ // For legacy apps dangerous permissions are install time ones.
grant = GRANT_INSTALL;
- } break;
-
- case PermissionInfo.PROTECTION_DANGEROUS: {
- // If a permission review is required for legacy apps we represent
- // their permissions as always granted runtime ones since we need
- // to keep the review required permission flag per user while an
- // install permission's state is shared across all users.
- if (!appSupportsRuntimePermissions && !mPermissionReviewRequired) {
- // For legacy apps dangerous permissions are install time ones.
- grant = GRANT_INSTALL;
- } else if (origPermissions.hasInstallPermission(bp.name)) {
- // For legacy apps that became modern, install becomes runtime.
- grant = GRANT_UPGRADE;
- } else if (mPromoteSystemApps
- && isSystemApp(ps)
- && mExistingSystemPackages.contains(ps.name)) {
- // For legacy system apps, install becomes runtime.
- // We cannot check hasInstallPermission() for system apps since those
- // permissions were granted implicitly and not persisted pre-M.
- grant = GRANT_UPGRADE;
- } else {
- // For modern apps keep runtime permissions unchanged.
- grant = GRANT_RUNTIME;
- }
- } break;
-
- case PermissionInfo.PROTECTION_SIGNATURE: {
- // For all apps signature permissions are install time ones.
- allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
- if (allowedSig) {
- grant = GRANT_INSTALL;
- }
- } break;
+ } else if (origPermissions.hasInstallPermission(bp.getName())) {
+ // For legacy apps that became modern, install becomes runtime.
+ grant = GRANT_UPGRADE;
+ } else if (mPromoteSystemApps
+ && isSystemApp(ps)
+ && mExistingSystemPackages.contains(ps.name)) {
+ // For legacy system apps, install becomes runtime.
+ // We cannot check hasInstallPermission() for system apps since those
+ // permissions were granted implicitly and not persisted pre-M.
+ grant = GRANT_UPGRADE;
+ } else {
+ // For modern apps keep runtime permissions unchanged.
+ grant = GRANT_RUNTIME;
+ }
+ } else if (bp.isSignature()) {
+ // For all apps signature permissions are install time ones.
+ allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
+ if (allowedSig) {
+ grant = GRANT_INSTALL;
+ }
}
if (DEBUG_PERMISSIONS) {
@@ -13053,7 +12353,7 @@
// for legacy apps
for (int userId : UserManagerService.getInstance().getUserIds()) {
if (origPermissions.getRuntimePermissionState(
- bp.name, userId) != null) {
+ perm, userId) != null) {
// Revoke the runtime permission and clear the flags.
origPermissions.revokeRuntimePermission(bp, userId);
origPermissions.updatePermissionFlags(bp, userId,
@@ -13074,10 +12374,10 @@
// Grant previously granted runtime permissions.
for (int userId : UserManagerService.getInstance().getUserIds()) {
PermissionState permissionState = origPermissions
- .getRuntimePermissionState(bp.name, userId);
+ .getRuntimePermissionState(perm, userId);
int flags = permissionState != null
? permissionState.getFlags() : 0;
- if (origPermissions.hasRuntimePermission(bp.name, userId)) {
+ if (origPermissions.hasRuntimePermission(perm, userId)) {
// Don't propagate the permission in a permission review mode if
// the former was revoked, i.e. marked to not propagate on upgrade.
// Note that in a permission review mode install permissions are
@@ -13120,7 +12420,7 @@
// permissions as these are the only ones the platform knows
// how to disable the API to simulate revocation as legacy
// apps don't expect to run with revoked permissions.
- if (PLATFORM_PACKAGE_NAME.equals(bp.sourcePackage)) {
+ if (PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName())) {
if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
// We changed the flags, hence have to write.
@@ -13143,7 +12443,7 @@
case GRANT_UPGRADE: {
// Grant runtime permissions for a previously held install permission.
PermissionState permissionState = origPermissions
- .getInstallPermissionState(bp.name);
+ .getInstallPermissionState(perm);
final int flags = permissionState != null ? permissionState.getFlags() : 0;
if (origPermissions.revokeInstallPermission(bp)
@@ -13191,10 +12491,10 @@
changedInstallPermission = true;
Slog.i(TAG, "Un-granting permission " + perm
+ " from package " + pkg.packageName
- + " (protectionLevel=" + bp.protectionLevel
+ + " (protectionLevel=" + bp.getProtectionLevel()
+ " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+ ")");
- } else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) {
+ } else if (bp.isAppOp()) {
// Don't print warning for app op permissions, since it is fine for them
// not to be granted, there is a UI for the user to decide.
if (DEBUG_PERMISSIONS
@@ -13202,7 +12502,7 @@
|| packageOfInterest.equals(pkg.packageName))) {
Slog.i(TAG, "Not granting permission " + perm
+ " to package " + pkg.packageName
- + " (protectionLevel=" + bp.protectionLevel
+ + " (protectionLevel=" + bp.getProtectionLevel()
+ " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+ ")");
}
@@ -13262,13 +12562,11 @@
private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
BasePermission bp, PermissionsState origPermissions) {
- boolean oemPermission = (bp.protectionLevel
- & PermissionInfo.PROTECTION_FLAG_OEM) != 0;
- boolean privilegedPermission = (bp.protectionLevel
- & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0;
+ boolean oemPermission = bp.isOEM();
+ boolean privilegedPermission = bp.isPrivileged();
boolean privappPermissionsDisable =
RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE;
- boolean platformPermission = PLATFORM_PACKAGE_NAME.equals(bp.sourcePackage);
+ boolean platformPermission = PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName());
boolean platformPackage = PLATFORM_PACKAGE_NAME.equals(pkg.packageName);
if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivilegedApp()
&& !platformPackage && platformPermission) {
@@ -13297,7 +12595,7 @@
}
}
boolean allowed = (compareSignatures(
- bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
+ bp.getSourcePackageSetting().signatures.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH)
|| (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH);
@@ -13374,39 +12672,37 @@
}
}
if (!allowed) {
- if (!allowed && (bp.protectionLevel
- & PermissionInfo.PROTECTION_FLAG_PRE23) != 0
+ if (!allowed
+ && bp.isPre23()
&& pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
// If this was a previously normal/dangerous permission that got moved
// to a system permission as part of the runtime permission redesign, then
// we still want to blindly grant it to old apps.
allowed = true;
}
- if (!allowed && (bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0
+ if (!allowed && bp.isInstaller()
&& pkg.packageName.equals(mRequiredInstallerPackage)) {
// If this permission is to be granted to the system installer and
// this app is an installer, then it gets the permission.
allowed = true;
}
- if (!allowed && (bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0
+ if (!allowed && bp.isVerifier()
&& pkg.packageName.equals(mRequiredVerifierPackage)) {
// If this permission is to be granted to the system verifier and
// this app is a verifier, then it gets the permission.
allowed = true;
}
- if (!allowed && (bp.protectionLevel
- & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0
+ if (!allowed && bp.isPreInstalled()
&& isSystemApp(pkg)) {
// Any pre-installed system app is allowed to get this permission.
allowed = true;
}
- if (!allowed && (bp.protectionLevel
- & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
+ if (!allowed && bp.isDevelopment()) {
// For development permissions, a development permission
// is granted only if it was already granted.
allowed = origPermissions.hasInstallPermission(perm);
}
- if (!allowed && (bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0
+ if (!allowed && bp.isSetup()
&& pkg.packageName.equals(mSetupWizardPackage)) {
// If this permission is to be granted to the system setup wizard and
// this app is a setup wizard, then it gets the permission.
@@ -14707,7 +14003,7 @@
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */, "installPackageAsUser");
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
@@ -14835,7 +14131,7 @@
return installReason;
}
- void installStage(String packageName, File stagedDir, String stagedCid,
+ void installStage(String packageName, File stagedDir,
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
Certificate[][] certificates) {
@@ -14848,12 +14144,7 @@
sessionParams.originatingUri, sessionParams.referrerUri,
sessionParams.originatingUid, installerUid);
- final OriginInfo origin;
- if (stagedDir != null) {
- origin = OriginInfo.fromStagedFile(stagedDir);
- } else {
- origin = OriginInfo.fromStagedContainer(stagedCid);
- }
+ final OriginInfo origin = OriginInfo.fromStagedFile(stagedDir);
final Message msg = mHandler.obtainMessage(INIT_COPY);
final int installReason = fixUpInstallReason(installerPackageName, installerUid,
@@ -14951,7 +14242,7 @@
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
PackageSetting pkgSetting;
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */,
"setApplicationHiddenSetting for user " + userId);
@@ -15053,7 +14344,7 @@
public boolean getApplicationHiddenSettingAsUser(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"getApplicationHidden for user " + userId);
PackageSetting ps;
@@ -15085,7 +14376,7 @@
null);
PackageSetting pkgSetting;
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */,
"installExistingPackage for user " + userId);
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
@@ -15190,7 +14481,7 @@
int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */,
"setPackagesSuspended for user " + userId);
@@ -15251,7 +14542,7 @@
@Override
public boolean isPackageSuspendedForUser(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"isPackageSuspendedForUser for user " + userId);
synchronized (mPackages) {
@@ -15698,7 +14989,7 @@
synchronized (mPackages) {
boolean result = mSettings.setDefaultBrowserPackageNameLPw(packageName, userId);
if (packageName != null) {
- mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowserLPr(
+ mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowser(
packageName, userId);
}
return result;
@@ -16053,7 +15344,6 @@
* file, or a cluster directory. This location may be untrusted.
*/
final File file;
- final String cid;
/**
* Flag indicating that {@link #file} or {@link #cid} has already been
@@ -16072,35 +15362,27 @@
final File resolvedFile;
static OriginInfo fromNothing() {
- return new OriginInfo(null, null, false, false);
+ return new OriginInfo(null, false, false);
}
static OriginInfo fromUntrustedFile(File file) {
- return new OriginInfo(file, null, false, false);
+ return new OriginInfo(file, false, false);
}
static OriginInfo fromExistingFile(File file) {
- return new OriginInfo(file, null, false, true);
+ return new OriginInfo(file, false, true);
}
static OriginInfo fromStagedFile(File file) {
- return new OriginInfo(file, null, true, false);
+ return new OriginInfo(file, true, false);
}
- static OriginInfo fromStagedContainer(String cid) {
- return new OriginInfo(null, cid, true, false);
- }
-
- private OriginInfo(File file, String cid, boolean staged, boolean existing) {
+ private OriginInfo(File file, boolean staged, boolean existing) {
this.file = file;
- this.cid = cid;
this.staged = staged;
this.existing = existing;
- if (cid != null) {
- resolvedPath = PackageHelper.getSdDir(cid);
- resolvedFile = new File(resolvedPath);
- } else if (file != null) {
+ if (file != null) {
resolvedPath = file.getAbsolutePath();
resolvedFile = file;
} else {
@@ -16193,7 +15475,7 @@
@Override
public String toString() {
return "InstallParams{" + Integer.toHexString(System.identityHashCode(this))
- + " file=" + origin.file + " cid=" + origin.cid + "}";
+ + " file=" + origin.file + "}";
}
private int installLocationPolicy(PackageInfoLite pkgLite) {
@@ -16304,9 +15586,6 @@
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &= ~PackageManager.INSTALL_EXTERNAL;
- } else if (origin.cid != null) {
- installFlags |= PackageManager.INSTALL_EXTERNAL;
- installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
@@ -16344,7 +15623,7 @@
Environment.getDataDirectory());
final long sizeBytes = mContainerService.calculateInstalledSize(
- origin.resolvedPath, isForwardLocked(), packageAbiOverride);
+ origin.resolvedPath, packageAbiOverride);
try {
mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
@@ -16581,43 +15860,11 @@
mArgs = createInstallArgs(this);
mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
}
-
- public boolean isForwardLocked() {
- return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
- }
- }
-
- /**
- * Used during creation of InstallArgs
- *
- * @param installFlags package installation flags
- * @return true if should be installed on external storage
- */
- private static boolean installOnExternalAsec(int installFlags) {
- if ((installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
- return false;
- }
- if ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
- return true;
- }
- return false;
- }
-
- /**
- * Used during creation of InstallArgs
- *
- * @param installFlags package installation flags
- * @return true if should be installed as forward locked
- */
- private static boolean installForwardLocked(int installFlags) {
- return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
}
private InstallArgs createInstallArgs(InstallParams params) {
if (params.move != null) {
return new MoveInstallArgs(params);
- } else if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) {
- return new AsecInstallArgs(params);
} else {
return new FileInstallArgs(params);
}
@@ -16629,27 +15876,7 @@
*/
private InstallArgs createInstallArgsForExisting(int installFlags, String codePath,
String resourcePath, String[] instructionSets) {
- final boolean isInAsec;
- if (installOnExternalAsec(installFlags)) {
- /* Apps on SD card are always in ASEC containers. */
- isInAsec = true;
- } else if (installForwardLocked(installFlags)
- && !codePath.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath())) {
- /*
- * Forward-locked apps are only in ASEC containers if they're the
- * new style
- */
- isInAsec = true;
- } else {
- isInAsec = false;
- }
-
- if (isInAsec) {
- return new AsecInstallArgs(codePath, instructionSets,
- installOnExternalAsec(installFlags), installForwardLocked(installFlags));
- } else {
- return new FileInstallArgs(codePath, resourcePath, instructionSets);
- }
+ return new FileInstallArgs(codePath, resourcePath, instructionSets);
}
static abstract class InstallArgs {
@@ -16983,11 +16210,6 @@
}
}
- private boolean isAsecExternal(String cid) {
- final String asecPath = PackageHelper.getSdFilesystem(cid);
- return !asecPath.startsWith(mAsecInternalPath);
- }
-
private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws
PackageManagerException {
if (copyRet < 0) {
@@ -17010,308 +16232,6 @@
}
/**
- * Logic to handle installation of ASEC applications, including copying and
- * renaming logic.
- */
- class AsecInstallArgs extends InstallArgs {
- static final String RES_FILE_NAME = "pkg.apk";
- static final String PUBLIC_RES_FILE_NAME = "res.zip";
-
- String cid;
- String packagePath;
- String resourcePath;
-
- /** New install */
- AsecInstallArgs(InstallParams params) {
- super(params.origin, params.move, params.observer, params.installFlags,
- params.installerPackageName, params.volumeUuid,
- params.getUser(), null /* instruction sets */, params.packageAbiOverride,
- params.grantedRuntimePermissions,
- params.traceMethod, params.traceCookie, params.certificates,
- params.installReason);
- }
-
- /** Existing install */
- AsecInstallArgs(String fullCodePath, String[] instructionSets,
- boolean isExternal, boolean isForwardLocked) {
- super(OriginInfo.fromNothing(), null, null, (isExternal ? INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
- instructionSets, null, null, null, 0, null /*certificates*/,
- PackageManager.INSTALL_REASON_UNKNOWN);
- // Hackily pretend we're still looking at a full code path
- if (!fullCodePath.endsWith(RES_FILE_NAME)) {
- fullCodePath = new File(fullCodePath, RES_FILE_NAME).getAbsolutePath();
- }
-
- // Extract cid from fullCodePath
- int eidx = fullCodePath.lastIndexOf("/");
- String subStr1 = fullCodePath.substring(0, eidx);
- int sidx = subStr1.lastIndexOf("/");
- cid = subStr1.substring(sidx+1, eidx);
- setMountPath(subStr1);
- }
-
- AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) {
- super(OriginInfo.fromNothing(), null, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
- instructionSets, null, null, null, 0, null /*certificates*/,
- PackageManager.INSTALL_REASON_UNKNOWN);
- this.cid = cid;
- setMountPath(PackageHelper.getSdDir(cid));
- }
-
- void createCopyFile() {
- cid = mInstallerService.allocateExternalStageCidLegacy();
- }
-
- int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
- if (origin.staged && origin.cid != null) {
- if (DEBUG_INSTALL) Slog.d(TAG, origin.cid + " already staged; skipping copy");
- cid = origin.cid;
- setMountPath(PackageHelper.getSdDir(cid));
- return PackageManager.INSTALL_SUCCEEDED;
- }
-
- if (temp) {
- createCopyFile();
- } else {
- /*
- * Pre-emptively destroy the container since it's destroyed if
- * copying fails due to it existing anyway.
- */
- PackageHelper.destroySdDir(cid);
- }
-
- final String newMountPath = imcs.copyPackageToContainer(
- origin.file.getAbsolutePath(), cid, getEncryptKey(), isExternalAsec(),
- isFwdLocked(), deriveAbiOverride(abiOverride, null /* settings */));
-
- if (newMountPath != null) {
- setMountPath(newMountPath);
- return PackageManager.INSTALL_SUCCEEDED;
- } else {
- return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- }
- }
-
- @Override
- String getCodePath() {
- return packagePath;
- }
-
- @Override
- String getResourcePath() {
- return resourcePath;
- }
-
- int doPreInstall(int status) {
- if (status != PackageManager.INSTALL_SUCCEEDED) {
- // Destroy container
- PackageHelper.destroySdDir(cid);
- } else {
- boolean mounted = PackageHelper.isContainerMounted(cid);
- if (!mounted) {
- String newMountPath = PackageHelper.mountSdDir(cid, getEncryptKey(),
- Process.SYSTEM_UID);
- if (newMountPath != null) {
- setMountPath(newMountPath);
- } else {
- return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- }
- }
- }
- return status;
- }
-
- boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
- String newCacheId = getNextCodePath(oldCodePath, pkg.packageName, "/" + RES_FILE_NAME);
- String newMountPath = null;
- if (PackageHelper.isContainerMounted(cid)) {
- // Unmount the container
- if (!PackageHelper.unMountSdDir(cid)) {
- Slog.i(TAG, "Failed to unmount " + cid + " before renaming");
- return false;
- }
- }
- if (!PackageHelper.renameSdDir(cid, newCacheId)) {
- Slog.e(TAG, "Failed to rename " + cid + " to " + newCacheId +
- " which might be stale. Will try to clean up.");
- // Clean up the stale container and proceed to recreate.
- if (!PackageHelper.destroySdDir(newCacheId)) {
- Slog.e(TAG, "Very strange. Cannot clean up stale container " + newCacheId);
- return false;
- }
- // Successfully cleaned up stale container. Try to rename again.
- if (!PackageHelper.renameSdDir(cid, newCacheId)) {
- Slog.e(TAG, "Failed to rename " + cid + " to " + newCacheId
- + " inspite of cleaning it up.");
- return false;
- }
- }
- if (!PackageHelper.isContainerMounted(newCacheId)) {
- Slog.w(TAG, "Mounting container " + newCacheId);
- newMountPath = PackageHelper.mountSdDir(newCacheId,
- getEncryptKey(), Process.SYSTEM_UID);
- } else {
- newMountPath = PackageHelper.getSdDir(newCacheId);
- }
- if (newMountPath == null) {
- Slog.w(TAG, "Failed to get cache path for " + newCacheId);
- return false;
- }
- Log.i(TAG, "Succesfully renamed " + cid +
- " to " + newCacheId +
- " at new path: " + newMountPath);
- cid = newCacheId;
-
- final File beforeCodeFile = new File(packagePath);
- setMountPath(newMountPath);
- final File afterCodeFile = new File(packagePath);
-
- // Reflect the rename in scanned details
- pkg.setCodePath(afterCodeFile.getAbsolutePath());
- pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
- afterCodeFile, pkg.baseCodePath));
- pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
- afterCodeFile, pkg.splitCodePaths));
-
- // Reflect the rename in app info
- pkg.setApplicationVolumeUuid(pkg.volumeUuid);
- pkg.setApplicationInfoCodePath(pkg.codePath);
- pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
- pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
- pkg.setApplicationInfoResourcePath(pkg.codePath);
- pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
- pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
-
- return true;
- }
-
- private void setMountPath(String mountPath) {
- final File mountFile = new File(mountPath);
-
- final File monolithicFile = new File(mountFile, RES_FILE_NAME);
- if (monolithicFile.exists()) {
- packagePath = monolithicFile.getAbsolutePath();
- if (isFwdLocked()) {
- resourcePath = new File(mountFile, PUBLIC_RES_FILE_NAME).getAbsolutePath();
- } else {
- resourcePath = packagePath;
- }
- } else {
- packagePath = mountFile.getAbsolutePath();
- resourcePath = packagePath;
- }
- }
-
- int doPostInstall(int status, int uid) {
- if (status != PackageManager.INSTALL_SUCCEEDED) {
- cleanUp();
- } else {
- final int groupOwner;
- final String protectedFile;
- if (isFwdLocked()) {
- groupOwner = UserHandle.getSharedAppGid(uid);
- protectedFile = RES_FILE_NAME;
- } else {
- groupOwner = -1;
- protectedFile = null;
- }
-
- if (uid < Process.FIRST_APPLICATION_UID
- || !PackageHelper.fixSdPermissions(cid, groupOwner, protectedFile)) {
- Slog.e(TAG, "Failed to finalize " + cid);
- PackageHelper.destroySdDir(cid);
- return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- }
-
- boolean mounted = PackageHelper.isContainerMounted(cid);
- if (!mounted) {
- PackageHelper.mountSdDir(cid, getEncryptKey(), Process.myUid());
- }
- }
- return status;
- }
-
- private void cleanUp() {
- if (DEBUG_SD_INSTALL) Slog.i(TAG, "cleanUp");
-
- // Destroy secure container
- PackageHelper.destroySdDir(cid);
- }
-
- private List<String> getAllCodePaths() {
- final File codeFile = new File(getCodePath());
- if (codeFile != null && codeFile.exists()) {
- try {
- final PackageLite pkg = PackageParser.parsePackageLite(codeFile, 0);
- return pkg.getAllCodePaths();
- } catch (PackageParserException e) {
- // Ignored; we tried our best
- }
- }
- return Collections.EMPTY_LIST;
- }
-
- void cleanUpResourcesLI() {
- // Enumerate all code paths before deleting
- cleanUpResourcesLI(getAllCodePaths());
- }
-
- private void cleanUpResourcesLI(List<String> allCodePaths) {
- cleanUp();
- removeDexFiles(allCodePaths, instructionSets);
- }
-
- String getPackageName() {
- return getAsecPackageName(cid);
- }
-
- boolean doPostDeleteLI(boolean delete) {
- if (DEBUG_SD_INSTALL) Slog.i(TAG, "doPostDeleteLI() del=" + delete);
- final List<String> allCodePaths = getAllCodePaths();
- boolean mounted = PackageHelper.isContainerMounted(cid);
- if (mounted) {
- // Unmount first
- if (PackageHelper.unMountSdDir(cid)) {
- mounted = false;
- }
- }
- if (!mounted && delete) {
- cleanUpResourcesLI(allCodePaths);
- }
- return !mounted;
- }
-
- @Override
- int doPreCopy() {
- if (isFwdLocked()) {
- if (!PackageHelper.fixSdPermissions(cid, getPackageUid(DEFAULT_CONTAINER_PACKAGE,
- MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM), RES_FILE_NAME)) {
- return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- }
- }
-
- return PackageManager.INSTALL_SUCCEEDED;
- }
-
- @Override
- int doPostCopy(int uid) {
- if (isFwdLocked()) {
- if (uid < Process.FIRST_APPLICATION_UID
- || !PackageHelper.fixSdPermissions(cid, UserHandle.getSharedAppGid(uid),
- RES_FILE_NAME)) {
- Slog.e(TAG, "Failed to finalize " + cid);
- PackageHelper.destroySdDir(cid);
- return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- }
- }
-
- return PackageManager.INSTALL_SUCCEEDED;
- }
- }
-
- /**
* Logic to handle movement of existing installed applications.
*/
class MoveInstallArgs extends InstallArgs {
@@ -17615,9 +16535,9 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- private boolean shouldCheckUpgradeKeySetLP(PackageSetting oldPs, int scanFlags) {
+ private boolean shouldCheckUpgradeKeySetLP(PackageSettingBase oldPs, int scanFlags) {
// Can't rotate keys during boot or if sharedUser.
- if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.sharedUser != null
+ if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.isSharedUser()
|| !oldPs.keySetData.isUsingUpgradeKeySets()) {
return false;
}
@@ -17637,7 +16557,7 @@
return true;
}
- private boolean checkUpgradeKeySetLP(PackageSetting oldPS, PackageParser.Package newPkg) {
+ private boolean checkUpgradeKeySetLP(PackageSettingBase oldPS, PackageParser.Package newPkg) {
// Upgrade keysets are being used. Determine if new package has a superset of the
// required keys.
long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets();
@@ -18213,66 +17133,6 @@
}
}
- private int[] revokeUnusedSharedUserPermissionsLPw(SharedUserSetting su, int[] allUserIds) {
- // Collect all used permissions in the UID
- ArraySet<String> usedPermissions = new ArraySet<>();
- final int packageCount = su.packages.size();
- for (int i = 0; i < packageCount; i++) {
- PackageSetting ps = su.packages.valueAt(i);
- if (ps.pkg == null) {
- continue;
- }
- final int requestedPermCount = ps.pkg.requestedPermissions.size();
- for (int j = 0; j < requestedPermCount; j++) {
- String permission = ps.pkg.requestedPermissions.get(j);
- BasePermission bp = mSettings.mPermissions.get(permission);
- if (bp != null) {
- usedPermissions.add(permission);
- }
- }
- }
-
- PermissionsState permissionsState = su.getPermissionsState();
- // Prune install permissions
- List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates();
- final int installPermCount = installPermStates.size();
- for (int i = installPermCount - 1; i >= 0; i--) {
- PermissionState permissionState = installPermStates.get(i);
- if (!usedPermissions.contains(permissionState.getName())) {
- BasePermission bp = mSettings.mPermissions.get(permissionState.getName());
- if (bp != null) {
- permissionsState.revokeInstallPermission(bp);
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- PackageManager.MASK_PERMISSION_FLAGS, 0);
- }
- }
- }
-
- int[] runtimePermissionChangedUserIds = EmptyArray.INT;
-
- // Prune runtime permissions
- for (int userId : allUserIds) {
- List<PermissionState> runtimePermStates = permissionsState
- .getRuntimePermissionStates(userId);
- final int runtimePermCount = runtimePermStates.size();
- for (int i = runtimePermCount - 1; i >= 0; i--) {
- PermissionState permissionState = runtimePermStates.get(i);
- if (!usedPermissions.contains(permissionState.getName())) {
- BasePermission bp = mSettings.mPermissions.get(permissionState.getName());
- if (bp != null) {
- permissionsState.revokeRuntimePermission(bp, userId);
- permissionsState.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS, 0);
- runtimePermissionChangedUserIds = ArrayUtils.appendInt(
- runtimePermissionChangedUserIds, userId);
- }
- }
- }
- }
-
- return runtimePermissionChangedUserIds;
- }
-
private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
int[] allUsers, PackageInstalledInfo res, UserHandle user, int installReason) {
// Update the parent package setting
@@ -18673,8 +17533,9 @@
int N = pkg.permissions.size();
for (int i = N-1; i >= 0; i--) {
- PackageParser.Permission perm = pkg.permissions.get(i);
- BasePermission bp = mSettings.mPermissions.get(perm.info.name);
+ final PackageParser.Permission perm = pkg.permissions.get(i);
+ final BasePermission bp =
+ (BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);
// Don't allow anyone but the system to define ephemeral permissions.
if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
@@ -18691,25 +17552,26 @@
// also includes the "updating the same package" case, of course.
// "updating same package" could also involve key-rotation.
final boolean sigsOk;
- if (bp.sourcePackage.equals(pkg.packageName)
- && (bp.packageSetting instanceof PackageSetting)
- && (shouldCheckUpgradeKeySetLP((PackageSetting) bp.packageSetting,
+ final String sourcePackageName = bp.getSourcePackageName();
+ final PackageSettingBase sourcePackageSetting = bp.getSourcePackageSetting();
+ if (sourcePackageName.equals(pkg.packageName)
+ && (shouldCheckUpgradeKeySetLP(sourcePackageSetting,
scanFlags))) {
- sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);
+ sigsOk = checkUpgradeKeySetLP(sourcePackageSetting, pkg);
} else {
- sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,
+ sigsOk = compareSignatures(sourcePackageSetting.signatures.mSignatures,
pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
}
if (!sigsOk) {
// If the owning package is the system itself, we log but allow
// install to proceed; we fail the install on all other permission
// redefinitions.
- if (!bp.sourcePackage.equals("android")) {
+ if (!sourcePackageName.equals("android")) {
res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
+ pkg.packageName + " attempting to redeclare permission "
- + perm.info.name + " already owned by " + bp.sourcePackage);
+ + perm.info.name + " already owned by " + sourcePackageName);
res.origPermission = perm.info.name;
- res.origPackage = bp.sourcePackage;
+ res.origPackage = sourcePackageName;
return;
} else {
Slog.w(TAG, "Package " + pkg.packageName
@@ -18728,7 +17590,7 @@
Slog.w(TAG, "Package " + pkg.packageName + " trying to change a "
+ "non-runtime permission " + perm.info.name
+ " to runtime; keeping old protection level");
- perm.info.protectionLevel = bp.protectionLevel;
+ perm.info.protectionLevel = bp.getProtectionLevel();
}
}
}
@@ -18843,7 +17705,13 @@
// TODO: Layering violation
BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
- startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
+ if (!instantApp) {
+ startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
+ } else {
+ if (DEBUG_DOMAIN_VERIFICATION) {
+ Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
+ }
+ }
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
"installPackageLI")) {
@@ -20428,7 +19296,7 @@
android.Manifest.permission.CLEAR_APP_USER_DATA, null);
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */, "clear application data");
final PackageSetting ps = mSettings.getPackageLPr(packageName);
@@ -20567,9 +19435,9 @@
final int permissionCount = ps.pkg.requestedPermissions.size();
for (int i = 0; i < permissionCount; i++) {
- String permission = ps.pkg.requestedPermissions.get(i);
-
- BasePermission bp = mSettings.mPermissions.get(permission);
+ final String permName = ps.pkg.requestedPermissions.get(i);
+ final BasePermission bp =
+ (BasePermission) mPermissionManager.getPermissionTEMP(permName);
if (bp == null) {
continue;
}
@@ -20581,7 +19449,7 @@
for (int j = 0; j < packageCount; j++) {
PackageSetting pkg = ps.sharedUser.packages.valueAt(j);
if (pkg.pkg != null && !pkg.pkg.packageName.equals(ps.pkg.packageName)
- && pkg.pkg.requestedPermissions.contains(permission)) {
+ && pkg.pkg.requestedPermissions.contains(permName)) {
used = true;
break;
}
@@ -20591,13 +19459,13 @@
}
}
- PermissionsState permissionsState = ps.getPermissionsState();
+ final PermissionsState permissionsState = ps.getPermissionsState();
- final int oldFlags = permissionsState.getPermissionFlags(bp.name, userId);
+ final int oldFlags = permissionsState.getPermissionFlags(permName, userId);
// Always clear the user settable flags.
- final boolean hasInstallState = permissionsState.getInstallPermissionState(
- bp.name) != null;
+ final boolean hasInstallState =
+ permissionsState.getInstallPermissionState(permName) != null;
// If permission review is enabled and this is a legacy app, mark the
// permission as requiring a review as this is the initial state.
int flags = 0;
@@ -20697,7 +19565,7 @@
final int callingUid = Binder.getCallingUid();
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_CACHE_FILES, null);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
/* requireFullPermission= */ true, /* checkShell= */ false,
"delete application cache files");
final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
@@ -20817,7 +19685,7 @@
String opname) {
// writer
int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */, "add preferred activity");
if (filter.countActions() == 0) {
Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
@@ -20882,7 +19750,7 @@
}
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"replace preferred activity");
synchronized (mPackages) {
@@ -21559,8 +20427,11 @@
newFlagSet |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
}
if (DEBUG_BACKUP) {
- Slog.v(TAG, " + Restoring grant: pkg=" + pkgName + " perm=" + permName
- + " granted=" + isGranted + " bits=0x" + Integer.toHexString(newFlagSet));
+ Slog.v(TAG, " + Restoring grant:"
+ + " pkg=" + pkgName
+ + " perm=" + permName
+ + " granted=" + isGranted
+ + " bits=0x" + Integer.toHexString(newFlagSet));
}
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
@@ -21569,13 +20440,15 @@
Slog.v(TAG, " + already installed; applying");
}
PermissionsState perms = ps.getPermissionsState();
- BasePermission bp = mSettings.mPermissions.get(permName);
+ BasePermission bp =
+ (BasePermission) mPermissionManager.getPermissionTEMP(permName);
if (bp != null) {
if (isGranted) {
perms.grantRuntimePermission(bp, userId);
}
if (newFlagSet != 0) {
- perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet);
+ perms.updatePermissionFlags(
+ bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet);
}
}
} else {
@@ -21604,7 +20477,8 @@
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
int callingUid = Binder.getCallingUid();
enforceOwnerRights(ownerPackage, callingUid);
- enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
+ PackageManagerServiceUtils.enforceShellRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
if (intentFilter.countActions() == 0) {
Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions");
return;
@@ -21635,7 +20509,8 @@
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
final int callingUid = Binder.getCallingUid();
enforceOwnerRights(ownerPackage, callingUid);
- enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
+ PackageManagerServiceUtils.enforceShellRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
synchronized (mPackages) {
CrossProfileIntentResolver resolver =
mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
@@ -21865,7 +20740,7 @@
permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
}
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, true /* checkShell */, "set enabled");
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
boolean sendNow = false;
@@ -22154,7 +21029,7 @@
if (!sUserManager.exists(userId)) {
return;
}
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
false /* checkShell */, "flushPackageRestrictions");
synchronized (mPackages) {
mSettings.writePackageRestrictionsLPr(userId);
@@ -22196,7 +21071,7 @@
final int permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */, "stop package");
// writer
synchronized (mPackages) {
@@ -22236,7 +21111,7 @@
public int getApplicationEnabledSetting(String packageName, int userId) {
if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get enabled");
// reader
synchronized (mPackages) {
@@ -22251,7 +21126,7 @@
public int getComponentEnabledSetting(ComponentName component, int userId) {
if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getComponentEnabled");
synchronized (mPackages) {
if (filterAppAccessLPr(mSettings.getPackageLPr(component.getPackageName()), callingUid,
@@ -22349,7 +21224,7 @@
// If we upgraded grant all default permissions before kicking off.
for (int userId : grantPermissionsUserIds) {
- mDefaultPermissionPolicy.grantDefaultPermissions(userId);
+ mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId);
}
if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
@@ -22453,85 +21328,6 @@
return buf.toString();
}
- static class DumpState {
- public static final int DUMP_LIBS = 1 << 0;
- public static final int DUMP_FEATURES = 1 << 1;
- public static final int DUMP_ACTIVITY_RESOLVERS = 1 << 2;
- public static final int DUMP_SERVICE_RESOLVERS = 1 << 3;
- public static final int DUMP_RECEIVER_RESOLVERS = 1 << 4;
- public static final int DUMP_CONTENT_RESOLVERS = 1 << 5;
- public static final int DUMP_PERMISSIONS = 1 << 6;
- public static final int DUMP_PACKAGES = 1 << 7;
- public static final int DUMP_SHARED_USERS = 1 << 8;
- public static final int DUMP_MESSAGES = 1 << 9;
- public static final int DUMP_PROVIDERS = 1 << 10;
- public static final int DUMP_VERIFIERS = 1 << 11;
- public static final int DUMP_PREFERRED = 1 << 12;
- public static final int DUMP_PREFERRED_XML = 1 << 13;
- public static final int DUMP_KEYSETS = 1 << 14;
- public static final int DUMP_VERSION = 1 << 15;
- public static final int DUMP_INSTALLS = 1 << 16;
- public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 17;
- public static final int DUMP_DOMAIN_PREFERRED = 1 << 18;
- public static final int DUMP_FROZEN = 1 << 19;
- public static final int DUMP_DEXOPT = 1 << 20;
- public static final int DUMP_COMPILER_STATS = 1 << 21;
- public static final int DUMP_CHANGES = 1 << 22;
- public static final int DUMP_VOLUMES = 1 << 23;
-
- public static final int OPTION_SHOW_FILTERS = 1 << 0;
-
- private int mTypes;
-
- private int mOptions;
-
- private boolean mTitlePrinted;
-
- private SharedUserSetting mSharedUser;
-
- public boolean isDumping(int type) {
- if (mTypes == 0 && type != DUMP_PREFERRED_XML) {
- return true;
- }
-
- return (mTypes & type) != 0;
- }
-
- public void setDump(int type) {
- mTypes |= type;
- }
-
- public boolean isOptionEnabled(int option) {
- return (mOptions & option) != 0;
- }
-
- public void setOptionEnabled(int option) {
- mOptions |= option;
- }
-
- public boolean onTitlePrinted() {
- final boolean printed = mTitlePrinted;
- mTitlePrinted = true;
- return printed;
- }
-
- public boolean getTitlePrinted() {
- return mTitlePrinted;
- }
-
- public void setTitlePrinted(boolean enabled) {
- mTitlePrinted = enabled;
- }
-
- public SharedUserSetting getSharedUser() {
- return mSharedUser;
- }
-
- public void setSharedUser(SharedUserSetting user) {
- mSharedUser = user;
- }
- }
-
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
@@ -23389,135 +22185,6 @@
}
}
- /*
- * Update media status on PackageManager.
- */
- @Override
- public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
- enforceSystemOrRoot("Media status can only be updated by the system");
- // reader; this apparently protects mMediaMounted, but should probably
- // be a different lock in that case.
- synchronized (mPackages) {
- Log.i(TAG, "Updating external media status from "
- + (mMediaMounted ? "mounted" : "unmounted") + " to "
- + (mediaStatus ? "mounted" : "unmounted"));
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + mediaStatus
- + ", mMediaMounted=" + mMediaMounted);
- if (mediaStatus == mMediaMounted) {
- final Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1
- : 0, -1);
- mHandler.sendMessage(msg);
- return;
- }
- mMediaMounted = mediaStatus;
- }
- // Queue up an async operation since the package installation may take a
- // little while.
- mHandler.post(new Runnable() {
- public void run() {
- updateExternalMediaStatusInner(mediaStatus, reportStatus, true);
- }
- });
- }
-
- /**
- * Called by StorageManagerService when the initial ASECs to scan are available.
- * Should block until all the ASEC containers are finished being scanned.
- */
- public void scanAvailableAsecs() {
- updateExternalMediaStatusInner(true, false, false);
- }
-
- /*
- * Collect information of applications on external media, map them against
- * existing containers and update information based on current mount status.
- * Please note that we always have to report status if reportStatus has been
- * set to true especially when unloading packages.
- */
- private void updateExternalMediaStatusInner(boolean isMounted, boolean reportStatus,
- boolean externalStorage) {
- ArrayMap<AsecInstallArgs, String> processCids = new ArrayMap<>();
- int[] uidArr = EmptyArray.INT;
-
- final String[] list = PackageHelper.getSecureContainerList();
- if (ArrayUtils.isEmpty(list)) {
- Log.i(TAG, "No secure containers found");
- } else {
- // Process list of secure containers and categorize them
- // as active or stale based on their package internal state.
-
- // reader
- synchronized (mPackages) {
- for (String cid : list) {
- // Leave stages untouched for now; installer service owns them
- if (PackageInstallerService.isStageName(cid)) continue;
-
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Processing container " + cid);
- String pkgName = getAsecPackageName(cid);
- if (pkgName == null) {
- Slog.i(TAG, "Found stale container " + cid + " with no package name");
- continue;
- }
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Looking for pkg : " + pkgName);
-
- final PackageSetting ps = mSettings.mPackages.get(pkgName);
- if (ps == null) {
- Slog.i(TAG, "Found stale container " + cid + " with no matching settings");
- continue;
- }
-
- /*
- * Skip packages that are not external if we're unmounting
- * external storage.
- */
- if (externalStorage && !isMounted && !isExternal(ps)) {
- continue;
- }
-
- final AsecInstallArgs args = new AsecInstallArgs(cid,
- getAppDexInstructionSets(ps), ps.isForwardLocked());
- // The package status is changed only if the code path
- // matches between settings and the container id.
- if (ps.codePathString != null
- && ps.codePathString.startsWith(args.getCodePath())) {
- if (DEBUG_SD_INSTALL) {
- Log.i(TAG, "Container : " + cid + " corresponds to pkg : " + pkgName
- + " at code path: " + ps.codePathString);
- }
-
- // We do have a valid package installed on sdcard
- processCids.put(args, ps.codePathString);
- final int uid = ps.appId;
- if (uid != -1) {
- uidArr = ArrayUtils.appendInt(uidArr, uid);
- }
- } else {
- Slog.i(TAG, "Found stale container " + cid + ": expected codePath="
- + ps.codePathString);
- }
- }
- }
-
- Arrays.sort(uidArr);
- }
-
- // Process packages with valid entries.
- if (isMounted) {
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Loading packages");
- loadMediaPackages(processCids, uidArr, externalStorage);
- startCleaningPackages();
- mInstallerService.onSecureContainersAvailable();
- } else {
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Unloading packages");
- unloadMediaPackages(processCids, uidArr, reportStatus);
- }
- }
-
private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
ArrayList<ApplicationInfo> infos, IIntentReceiver finishedReceiver) {
final int size = infos.size();
@@ -23557,193 +22224,6 @@
}
}
- /*
- * Look at potentially valid container ids from processCids If package
- * information doesn't match the one on record or package scanning fails,
- * the cid is added to list of removeCids. We currently don't delete stale
- * containers.
- */
- private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr,
- boolean externalStorage) {
- ArrayList<String> pkgList = new ArrayList<String>();
- Set<AsecInstallArgs> keys = processCids.keySet();
-
- for (AsecInstallArgs args : keys) {
- String codePath = processCids.get(args);
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Loading container : " + args.cid);
- int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- try {
- // Make sure there are no container errors first.
- if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) != PackageManager.INSTALL_SUCCEEDED) {
- Slog.e(TAG, "Failed to mount cid : " + args.cid
- + " when installing from sdcard");
- continue;
- }
- // Check code path here.
- if (codePath == null || !codePath.startsWith(args.getCodePath())) {
- Slog.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath()
- + " does not match one in settings " + codePath);
- continue;
- }
- // Parse package
- int parseFlags = mDefParseFlags;
- if (args.isExternalAsec()) {
- parseFlags |= PackageParser.PARSE_EXTERNAL_STORAGE;
- }
- if (args.isFwdLocked()) {
- parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
- }
-
- synchronized (mInstallLock) {
- PackageParser.Package pkg = null;
- try {
- // Sadly we don't know the package name yet to freeze it
- pkg = scanPackageTracedLI(new File(codePath), parseFlags,
- SCAN_IGNORE_FROZEN, 0, null);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to scan " + codePath + ": " + e.getMessage());
- }
- // Scan the package
- if (pkg != null) {
- /*
- * TODO why is the lock being held? doPostInstall is
- * called in other places without the lock. This needs
- * to be straightened out.
- */
- // writer
- synchronized (mPackages) {
- retCode = PackageManager.INSTALL_SUCCEEDED;
- pkgList.add(pkg.packageName);
- // Post process args
- args.doPostInstall(PackageManager.INSTALL_SUCCEEDED,
- pkg.applicationInfo.uid);
- }
- } else {
- Slog.i(TAG, "Failed to install pkg from " + codePath + " from sdcard");
- }
- }
-
- } finally {
- if (retCode != PackageManager.INSTALL_SUCCEEDED) {
- Log.w(TAG, "Container " + args.cid + " is stale, retCode=" + retCode);
- }
- }
- }
- // writer
- synchronized (mPackages) {
- // If the platform SDK has changed since the last time we booted,
- // we need to re-grant app permission to catch any new ones that
- // appear. This is really a hack, and means that apps can in some
- // cases get permissions that the user didn't initially explicitly
- // allow... it would be nice to have some better way to handle
- // this situation.
- final VersionInfo ver = externalStorage ? mSettings.getExternalVersion()
- : mSettings.getInternalVersion();
- final String volumeUuid = externalStorage ? StorageManager.UUID_PRIMARY_PHYSICAL
- : StorageManager.UUID_PRIVATE_INTERNAL;
-
- int updateFlags = UPDATE_PERMISSIONS_ALL;
- if (ver.sdkVersion != mSdkVersion) {
- logCriticalInfo(Log.INFO, "Platform changed from " + ver.sdkVersion + " to "
- + mSdkVersion + "; regranting permissions for external");
- updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
- }
- updatePermissionsLPw(null, null, volumeUuid, updateFlags);
-
- // Yay, everything is now upgraded
- ver.forceCurrent();
-
- // can downgrade to reader
- // Persist settings
- mSettings.writeLPr();
- }
- // Send a broadcast to let everyone know we are done processing
- if (pkgList.size() > 0) {
- sendResourcesChangedBroadcast(true, false, pkgList, uidArr, null);
- }
- }
-
- /*
- * Utility method to unload a list of specified containers
- */
- private void unloadAllContainers(Set<AsecInstallArgs> cidArgs) {
- // Just unmount all valid containers.
- for (AsecInstallArgs arg : cidArgs) {
- synchronized (mInstallLock) {
- arg.doPostDeleteLI(false);
- }
- }
- }
-
- /*
- * Unload packages mounted on external media. This involves deleting package
- * data from internal structures, sending broadcasts about disabled packages,
- * gc'ing to free up references, unmounting all secure containers
- * corresponding to packages on external media, and posting a
- * UPDATED_MEDIA_STATUS message if status has been requested. Please note
- * that we always have to post this message if status has been requested no
- * matter what.
- */
- private void unloadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int uidArr[],
- final boolean reportStatus) {
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "unloading media packages");
- ArrayList<String> pkgList = new ArrayList<String>();
- ArrayList<AsecInstallArgs> failedList = new ArrayList<AsecInstallArgs>();
- final Set<AsecInstallArgs> keys = processCids.keySet();
- for (AsecInstallArgs args : keys) {
- String pkgName = args.getPackageName();
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Trying to unload pkg : " + pkgName);
- // Delete package internally
- PackageRemovedInfo outInfo = new PackageRemovedInfo(this);
- synchronized (mInstallLock) {
- final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
- final boolean res;
- try (PackageFreezer freezer = freezePackageForDelete(pkgName, deleteFlags,
- "unloadMediaPackages")) {
- res = deletePackageLIF(pkgName, null, false, null, deleteFlags, outInfo, false,
- null);
- }
- if (res) {
- pkgList.add(pkgName);
- } else {
- Slog.e(TAG, "Failed to delete pkg from sdcard : " + pkgName);
- failedList.add(args);
- }
- }
- }
-
- // reader
- synchronized (mPackages) {
- // We didn't update the settings after removing each package;
- // write them now for all packages.
- mSettings.writeLPr();
- }
-
- // We have to absolutely send UPDATED_MEDIA_STATUS only
- // after confirming that all the receivers processed the ordered
- // broadcast when packages get disabled, force a gc to clean things up.
- // and unload all the containers.
- if (pkgList.size() > 0) {
- sendResourcesChangedBroadcast(false, false, pkgList, uidArr,
- new IIntentReceiver.Stub() {
- public void performReceive(Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered, boolean sticky,
- int sendingUser) throws RemoteException {
- Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
- reportStatus ? 1 : 0, 1, keys);
- mHandler.sendMessage(msg);
- }
- });
- } else {
- Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1 : 0, -1,
- keys);
- mHandler.sendMessage(msg);
- }
- }
-
private void loadPrivatePackages(final VolumeInfo vol) {
mHandler.post(new Runnable() {
@Override
@@ -24829,7 +23309,9 @@
}
void onNewUserCreated(final int userId) {
- mDefaultPermissionPolicy.grantDefaultPermissions(userId);
+ synchronized(mPackages) {
+ mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId);
+ }
// If permission review for legacy apps is required, we represent
// dagerous permissions for such apps as always granted runtime
// permissions to keep per user flag state whether review is needed.
@@ -24861,7 +23343,8 @@
synchronized (mPackages) {
if (mSettings.mReadExternalStorageEnforced == null
|| mSettings.mReadExternalStorageEnforced != enforced) {
- mSettings.mReadExternalStorageEnforced = enforced;
+ mSettings.mReadExternalStorageEnforced =
+ enforced ? Boolean.TRUE : Boolean.FALSE;
mSettings.writeLPr();
}
}
@@ -25233,74 +23716,164 @@
}
return results;
}
+
+ // NB: this differentiates between preloads and sideloads
+ @Override
+ public String getInstallerForPackage(String packageName) throws RemoteException {
+ final String installerName = getInstallerPackageName(packageName);
+ if (!TextUtils.isEmpty(installerName)) {
+ return installerName;
+ }
+ // differentiate between preload and sideload
+ int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+ ApplicationInfo appInfo = getApplicationInfo(packageName,
+ /*flags*/ 0,
+ /*userId*/ callingUser);
+ if (appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ return "preload";
+ }
+ return "";
+ }
+
+ @Override
+ public int getVersionCodeForPackage(String packageName) throws RemoteException {
+ try {
+ int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+ PackageInfo pInfo = getPackageInfo(packageName, 0, callingUser);
+ if (pInfo != null) {
+ return pInfo.versionCode;
+ }
+ } catch (Exception e) {
+ }
+ return 0;
+ }
}
private class PackageManagerInternalImpl extends PackageManagerInternal {
@Override
- public void setLocationPackagesProvider(PackagesProvider provider) {
+ public void updatePermissionFlagsTEMP(String permName, String packageName, int flagMask,
+ int flagValues, int userId) {
+ PackageManagerService.this.updatePermissionFlags(
+ permName, packageName, flagMask, flagValues, userId);
+ }
+
+ @Override
+ public int getPermissionFlagsTEMP(String permName, String packageName, int userId) {
+ return PackageManagerService.this.getPermissionFlags(permName, packageName, userId);
+ }
+
+ @Override
+ public Object enforcePermissionTreeTEMP(String permName, int callingUid) {
synchronized (mPackages) {
- mDefaultPermissionPolicy.setLocationPackagesProviderLPw(provider);
+ return BasePermission.enforcePermissionTreeLP(
+ mSettings.mPermissionTrees, permName, callingUid);
}
}
+ @Override
+ public boolean isInstantApp(String packageName, int userId) {
+ return PackageManagerService.this.isInstantApp(packageName, userId);
+ }
+
+ @Override
+ public String getInstantAppPackageName(int uid) {
+ return PackageManagerService.this.getInstantAppPackageName(uid);
+ }
+
+ @Override
+ public boolean filterAppAccess(PackageParser.Package pkg, int callingUid, int userId) {
+ synchronized (mPackages) {
+ return PackageManagerService.this.filterAppAccessLPr(
+ (PackageSetting) pkg.mExtras, callingUid, userId);
+ }
+ }
+
+ @Override
+ public PackageParser.Package getPackage(String packageName) {
+ synchronized (mPackages) {
+ packageName = resolveInternalPackageNameLPr(
+ packageName, PackageManager.VERSION_CODE_HIGHEST);
+ return mPackages.get(packageName);
+ }
+ }
+
+ @Override
+ public PackageParser.Package getDisabledPackage(String packageName) {
+ synchronized (mPackages) {
+ final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
+ return (ps != null) ? ps.pkg : null;
+ }
+ }
+
+ @Override
+ public String getKnownPackageName(int knownPackage, int userId) {
+ switch(knownPackage) {
+ case PackageManagerInternal.PACKAGE_BROWSER:
+ return getDefaultBrowserPackageName(userId);
+ case PackageManagerInternal.PACKAGE_INSTALLER:
+ return mRequiredInstallerPackage;
+ case PackageManagerInternal.PACKAGE_SETUP_WIZARD:
+ return mSetupWizardPackage;
+ case PackageManagerInternal.PACKAGE_SYSTEM:
+ return "android";
+ case PackageManagerInternal.PACKAGE_VERIFIER:
+ return mRequiredVerifierPackage;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isResolveActivityComponent(ComponentInfo component) {
+ return mResolveActivity.packageName.equals(component.packageName)
+ && mResolveActivity.name.equals(component.name);
+ }
+
+ @Override
+ public void setLocationPackagesProvider(PackagesProvider provider) {
+ mDefaultPermissionPolicy.setLocationPackagesProvider(provider);
+ }
@Override
public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
- synchronized (mPackages) {
- mDefaultPermissionPolicy.setVoiceInteractionPackagesProviderLPw(provider);
- }
+ mDefaultPermissionPolicy.setVoiceInteractionPackagesProvider(provider);
}
@Override
public void setSmsAppPackagesProvider(PackagesProvider provider) {
- synchronized (mPackages) {
- mDefaultPermissionPolicy.setSmsAppPackagesProviderLPw(provider);
- }
+ mDefaultPermissionPolicy.setSmsAppPackagesProvider(provider);
}
@Override
public void setDialerAppPackagesProvider(PackagesProvider provider) {
- synchronized (mPackages) {
- mDefaultPermissionPolicy.setDialerAppPackagesProviderLPw(provider);
- }
+ mDefaultPermissionPolicy.setDialerAppPackagesProvider(provider);
}
@Override
public void setSimCallManagerPackagesProvider(PackagesProvider provider) {
- synchronized (mPackages) {
- mDefaultPermissionPolicy.setSimCallManagerPackagesProviderLPw(provider);
- }
+ mDefaultPermissionPolicy.setSimCallManagerPackagesProvider(provider);
}
@Override
public void setSyncAdapterPackagesprovider(SyncAdapterPackagesProvider provider) {
- synchronized (mPackages) {
- mDefaultPermissionPolicy.setSyncAdapterPackagesProviderLPw(provider);
- }
+ mDefaultPermissionPolicy.setSyncAdapterPackagesProvider(provider);
}
@Override
public void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId) {
- synchronized (mPackages) {
- mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSmsAppLPr(
- packageName, userId);
- }
+ mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSmsApp(packageName, userId);
}
@Override
public void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId) {
synchronized (mPackages) {
mSettings.setDefaultDialerPackageNameLPw(packageName, userId);
- mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultDialerAppLPr(
- packageName, userId);
}
+ mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultDialerApp(packageName, userId);
}
@Override
public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
- synchronized (mPackages) {
- mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSimCallManagerLPr(
- packageName, userId);
- }
+ mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSimCallManager(
+ packageName, userId);
}
@Override
@@ -25387,6 +23960,15 @@
}
@Override
+ public List<ResolveInfo> queryIntentServices(
+ Intent intent, int flags, int callingUid, int userId) {
+ final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
+ return PackageManagerService.this
+ .queryIntentServicesInternal(intent, resolvedType, flags, userId, callingUid,
+ false);
+ }
+
+ @Override
public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
int userId) {
return PackageManagerService.this.getHomeActivitiesAsUser(allHomeCandidates, userId);
@@ -25421,17 +24003,19 @@
}
@Override
- public void grantRuntimePermission(String packageName, String name, int userId,
+ public void grantRuntimePermission(String packageName, String permName, int userId,
boolean overridePolicy) {
- PackageManagerService.this.grantRuntimePermission(packageName, name, userId,
- overridePolicy);
+ PackageManagerService.this.mPermissionManager.grantRuntimePermission(
+ permName, packageName, overridePolicy, getCallingUid(), userId,
+ mPermissionCallback);
}
@Override
- public void revokeRuntimePermission(String packageName, String name, int userId,
+ public void revokeRuntimePermission(String packageName, String permName, int userId,
boolean overridePolicy) {
- PackageManagerService.this.revokeRuntimePermission(packageName, name, userId,
- overridePolicy);
+ mPermissionManager.revokeRuntimePermission(
+ permName, packageName, overridePolicy, getCallingUid(), userId,
+ mPermissionCallback);
}
@Override
@@ -25553,9 +24137,9 @@
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
- int flags, int userId) {
+ int flags, int userId, boolean resolveForStart) {
return resolveIntentInternal(
- intent, resolvedType, flags, userId, true /*resolveForStart*/);
+ intent, resolvedType, flags, userId, resolveForStart);
}
@Override
@@ -25565,6 +24149,12 @@
}
@Override
+ public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
+ return PackageManagerService.this.resolveContentProviderInternal(
+ name, flags, userId);
+ }
+
+ @Override
public void addIsolatedUid(int isolatedUid, int ownerUid) {
synchronized (mPackages) {
mIsolatedOwners.put(isolatedUid, ownerUid);
@@ -25611,7 +24201,7 @@
synchronized (mPackages) {
final long identity = Binder.clearCallingIdentity();
try {
- mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledCarrierAppsLPr(
+ mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledCarrierApps(
packageNames, userId);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -25625,7 +24215,7 @@
synchronized (mPackages) {
final long identity = Binder.clearCallingIdentity();
try {
- mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledImsServicesLPr(
+ mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledImsServices(
packageNames, userId);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -25700,7 +24290,7 @@
@Override
public int getInstallReason(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId,
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"get install reason");
synchronized (mPackages) {
@@ -25778,7 +24368,7 @@
public String getInstantAppAndroidId(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_INSTANT_APPS,
"getInstantAppAndroidId");
- enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getInstantAppAndroidId");
// Make sure the target is an Instant App.
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 25fef0a..8f7971e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -22,17 +22,23 @@
import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
import static com.android.server.pm.PackageManagerService.TAG;
+import com.android.internal.util.ArrayUtils;
+
import android.annotation.NonNull;
import android.app.AppGlobals;
import android.content.Intent;
import android.content.pm.PackageParser;
import android.content.pm.ResolveInfo;
import android.os.Build;
+import android.os.Debug;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.system.ErrnoException;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
+import android.util.jar.StrictJarFile;
import dalvik.system.VMRuntime;
import libcore.io.Libcore;
@@ -41,9 +47,11 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Predicate;
+import java.util.zip.ZipEntry;
/**
* Class containing helper methods for the PackageManagerService.
@@ -253,4 +261,73 @@
}
return false;
}
+
+ /**
+ * Checks that the archive located at {@code fileName} has uncompressed dex file and so
+ * files that can be direclty mapped.
+ */
+ public static void logApkHasUncompressedCode(String fileName) {
+ StrictJarFile jarFile = null;
+ try {
+ jarFile = new StrictJarFile(fileName,
+ false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
+ Iterator<ZipEntry> it = jarFile.iterator();
+ while (it.hasNext()) {
+ ZipEntry entry = it.next();
+ if (entry.getName().endsWith(".dex")) {
+ if (entry.getMethod() != ZipEntry.STORED) {
+ Slog.wtf(TAG, "APK " + fileName + " has compressed dex code " +
+ entry.getName());
+ } else if ((entry.getDataOffset() & 0x3) != 0) {
+ Slog.wtf(TAG, "APK " + fileName + " has unaligned dex code " +
+ entry.getName());
+ }
+ } else if (entry.getName().endsWith(".so")) {
+ if (entry.getMethod() != ZipEntry.STORED) {
+ Slog.wtf(TAG, "APK " + fileName + " has compressed native code " +
+ entry.getName());
+ } else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) {
+ Slog.wtf(TAG, "APK " + fileName + " has unaligned native code " +
+ entry.getName());
+ }
+ }
+ }
+ } catch (IOException ignore) {
+ Slog.wtf(TAG, "Error when parsing APK " + fileName);
+ } finally {
+ try {
+ if (jarFile != null) {
+ jarFile.close();
+ }
+ } catch (IOException ignore) {}
+ }
+ return;
+ }
+
+ /**
+ * Checks that the APKs in the given package have uncompressed dex file and so
+ * files that can be direclty mapped.
+ */
+ public static void logPackageHasUncompressedCode(PackageParser.Package pkg) {
+ logApkHasUncompressedCode(pkg.baseCodePath);
+ if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
+ for (int i = 0; i < pkg.splitCodePaths.length; i++) {
+ logApkHasUncompressedCode(pkg.splitCodePaths[i]);
+ }
+ }
+ }
+
+ public static void enforceShellRestriction(String restriction, int callingUid, int userHandle) {
+ if (callingUid == Process.SHELL_UID) {
+ if (userHandle >= 0
+ && PackageManagerService.sUserManager.hasUserRestriction(
+ restriction, userHandle)) {
+ throw new SecurityException("Shell does not have permission to access user "
+ + userHandle);
+ } else if (userHandle < 0) {
+ Slog.e(PackageManagerService.TAG, "Unable to check shell permission for user "
+ + userHandle + "\n\t" + Debug.getCallers(3));
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 1f03e66..1fea003 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -183,7 +183,7 @@
PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
null, null);
params.sessionParams.setSize(PackageHelper.calculateInstalledSize(
- pkgLite, false, params.sessionParams.abiOverride));
+ pkgLite, params.sessionParams.abiOverride));
} catch (PackageParserException | IOException e) {
pw.println("Error: Failed to parse APK file: " + file);
throw new IllegalArgumentException(
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 52bf641..83cb2db 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -23,13 +23,15 @@
import android.service.pm.PackageProto;
import android.util.proto.ProtoOutputStream;
+import com.android.server.pm.permission.PermissionsState;
+
import java.io.File;
import java.util.List;
/**
* Settings data for a particular package we know about.
*/
-final class PackageSetting extends PackageSettingBase {
+public final class PackageSetting extends PackageSettingBase {
int appId;
PackageParser.Package pkg;
/**
@@ -103,12 +105,21 @@
sharedUserId = orig.sharedUserId;
}
+ @Override
public PermissionsState getPermissionsState() {
return (sharedUser != null)
? sharedUser.getPermissionsState()
: super.getPermissionsState();
}
+ public PackageParser.Package getPackage() {
+ return pkg;
+ }
+
+ public int getAppId() {
+ return appId;
+ }
+
public boolean isPrivileged() {
return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
}
@@ -125,6 +136,7 @@
return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
+ @Override
public boolean isSharedUser() {
return sharedUser != null;
}
@@ -136,6 +148,10 @@
return true;
}
+ public boolean hasChildPackages() {
+ return childPackageNames != null && !childPackageNames.isEmpty();
+ }
+
public void writeToProto(ProtoOutputStream proto, long fieldId, List<UserInfo> users) {
final long packageToken = proto.start(fieldId);
proto.write(PackageProto.NAME, (realName != null ? realName : name));
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index d3ca1fd..e19e83f 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -24,14 +24,12 @@
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageUserState;
-import android.os.storage.VolumeInfo;
import android.service.pm.PackageProto;
import android.util.ArraySet;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-import com.google.android.collect.Lists;
import java.io.File;
import java.util.ArrayList;
@@ -42,7 +40,7 @@
/**
* Settings base class for pending and resolved classes.
*/
-abstract class PackageSettingBase extends SettingBase {
+public abstract class PackageSettingBase extends SettingBase {
private static final int[] EMPTY_INT_ARRAY = new int[0];
@@ -230,6 +228,9 @@
return updateAvailable;
}
+ public boolean isSharedUser() {
+ return false;
+ }
/**
* Makes a shallow copy of the given package settings.
*
@@ -412,7 +413,7 @@
modifyUserState(userId).suspended = suspended;
}
- boolean getInstantApp(int userId) {
+ public boolean getInstantApp(int userId) {
return readUserState(userId).instantApp;
}
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index e17cec0..c97f5e5 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -18,6 +18,8 @@
import android.content.pm.ApplicationInfo;
+import com.android.server.pm.permission.PermissionsState;
+
abstract class SettingBase {
int pkgFlags;
int pkgPrivateFlags;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 51d3e10..0084411 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -16,7 +16,6 @@
package com.android.server.pm;
-import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
@@ -64,7 +63,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
import android.service.pm.PackageServiceDumpProto;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -87,10 +85,11 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.XmlUtils;
-import com.android.server.backup.PreferredActivityBackupHelper;
import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.PackageManagerService.DumpState;
-import com.android.server.pm.PermissionsState.PermissionState;
+import com.android.server.pm.permission.BasePermission;
+import com.android.server.pm.permission.PermissionSettings;
+import com.android.server.pm.permission.PermissionsState;
+import com.android.server.pm.permission.PermissionsState.PermissionState;
import libcore.io.IoUtils;
@@ -127,7 +126,7 @@
/**
* Holds information about dynamic settings.
*/
-final class Settings {
+public final class Settings {
private static final String TAG = "PackageSettings";
/**
@@ -176,7 +175,7 @@
private static final String TAG_READ_EXTERNAL_STORAGE = "read-external-storage";
private static final String ATTR_ENFORCEMENT = "enforcement";
- private static final String TAG_ITEM = "item";
+ public static final String TAG_ITEM = "item";
private static final String TAG_DISABLED_COMPONENTS = "disabled-components";
private static final String TAG_ENABLED_COMPONENTS = "enabled-components";
private static final String TAG_PACKAGE_RESTRICTIONS = "package-restrictions";
@@ -201,7 +200,8 @@
private static final String TAG_DEFAULT_DIALER = "default-dialer";
private static final String TAG_VERSION = "version";
- private static final String ATTR_NAME = "name";
+ public static final String ATTR_NAME = "name";
+ public static final String ATTR_PACKAGE = "package";
private static final String ATTR_USER = "user";
private static final String ATTR_CODE = "code";
private static final String ATTR_GRANTED = "granted";
@@ -233,7 +233,6 @@
private static final String ATTR_VOLUME_UUID = "volumeUuid";
private static final String ATTR_SDK_VERSION = "sdkVersion";
private static final String ATTR_DATABASE_VERSION = "databaseVersion";
- private static final String ATTR_DONE = "done";
// Bookkeeping for restored permission grants
private static final String TAG_RESTORED_RUNTIME_PERMISSIONS = "restored-perms";
@@ -379,10 +378,6 @@
private final ArrayMap<Long, Integer> mKeySetRefs =
new ArrayMap<Long, Integer>();
- // Mapping from permission names to info about them.
- final ArrayMap<String, BasePermission> mPermissions =
- new ArrayMap<String, BasePermission>();
-
// Mapping from permission tree names to info about them.
final ArrayMap<String, BasePermission> mPermissionTrees =
new ArrayMap<String, BasePermission>();
@@ -420,14 +415,16 @@
private final File mSystemDir;
public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
+ /** Settings and other information about permissions */
+ private final PermissionSettings mPermissions;
- Settings(Object lock) {
- this(Environment.getDataDirectory(), lock);
+ Settings(PermissionSettings permissions, Object lock) {
+ this(Environment.getDataDirectory(), permissions, lock);
}
- Settings(File dataDir, Object lock) {
+ Settings(File dataDir, PermissionSettings permission, Object lock) {
mLock = lock;
-
+ mPermissions = permission;
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
mSystemDir = new File(dataDir, "system");
@@ -490,7 +487,7 @@
final PermissionsState perms = ps.getPermissionsState();
for (RestoredPermissionGrant grant : grants) {
- BasePermission bp = mPermissions.get(grant.permissionName);
+ BasePermission bp = mPermissions.getPermission(grant.permissionName);
if (bp != null) {
if (grant.granted) {
perms.grantRuntimePermission(bp, userId);
@@ -507,6 +504,10 @@
writeRuntimePermissionsForUserLPr(userId, false);
}
+ public boolean canPropagatePermissionToInstantApp(String permName) {
+ return mPermissions.canPropagatePermissionToInstantApp(permName);
+ }
+
void setInstallerPackageName(String pkgName, String installerPkgName) {
PackageSetting p = mPackages.get(pkgName);
if (p != null) {
@@ -664,29 +665,11 @@
}
}
- // Transfer ownership of permissions from one package to another.
- void transferPermissionsLPw(String origPkg, String newPkg) {
- // Transfer ownership of permissions to the new package.
- for (int i=0; i<2; i++) {
- ArrayMap<String, BasePermission> permissions =
- i == 0 ? mPermissionTrees : mPermissions;
- for (BasePermission bp : permissions.values()) {
- if (origPkg.equals(bp.sourcePackage)) {
- if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG,
- "Moving permission " + bp.name
- + " from pkg " + bp.sourcePackage
- + " to " + newPkg);
- bp.sourcePackage = newPkg;
- bp.packageSetting = null;
- bp.perm = null;
- if (bp.pendingInfo != null) {
- bp.pendingInfo.packageName = newPkg;
- }
- bp.uid = 0;
- bp.setGids(null, false);
- }
- }
- }
+ /**
+ * Transfers ownership of permissions from one package to another.
+ */
+ void transferPermissionsLPw(String origPackageName, String newPackageName) {
+ mPermissions.transferPermissions(origPackageName, newPackageName, mPermissionTrees);
}
/**
@@ -1074,7 +1057,7 @@
// Update permissions
for (String eachPerm : deletedPs.pkg.requestedPermissions) {
- BasePermission bp = mPermissions.get(eachPerm);
+ BasePermission bp = mPermissions.getPermission(eachPerm);
if (bp == null) {
continue;
}
@@ -2022,8 +2005,7 @@
// Specifically for backup/restore
public void processRestoredPermissionGrantLPr(String pkgName, String permission,
- boolean isGranted, int restoredFlagSet, int userId)
- throws IOException, XmlPullParserException {
+ boolean isGranted, int restoredFlagSet, int userId) {
mRuntimePermissionsPersistence.rememberRestoredUserGrantLPr(
pkgName, permission, isGranted, restoredFlagSet, userId);
}
@@ -2225,7 +2207,7 @@
if (tagName.equals(TAG_ITEM)) {
String name = parser.getAttributeValue(null, ATTR_NAME);
- BasePermission bp = mPermissions.get(name);
+ BasePermission bp = mPermissions.getPermission(name);
if (bp == null) {
Slog.w(PackageManagerService.TAG, "Unknown permission: " + name);
XmlUtils.skipCurrentTag(parser);
@@ -2520,9 +2502,7 @@
serializer.endTag(null, "permission-trees");
serializer.startTag(null, "permissions");
- for (BasePermission bp : mPermissions.values()) {
- writePermissionLPr(serializer, bp);
- }
+ mPermissions.writePermissions(serializer);
serializer.endTag(null, "permissions");
for (final PackageSetting pkg : mPackages.values()) {
@@ -2605,9 +2585,6 @@
writeAllRuntimePermissionsLPr();
return;
- } catch(XmlPullParserException e) {
- Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "
- + "current changes will be lost at reboot", e);
} catch(java.io.IOException e) {
Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "
+ "current changes will be lost at reboot", e);
@@ -2951,7 +2928,6 @@
void writeUpgradeKeySetsLPr(XmlSerializer serializer,
PackageKeySetData data) throws IOException {
- long properSigning = data.getProperSigningKeySet();
if (data.isUsingUpgradeKeySets()) {
for (long id : data.getUpgradeKeySets()) {
serializer.startTag(null, "upgrade-keyset");
@@ -2971,32 +2947,8 @@
}
}
- void writePermissionLPr(XmlSerializer serializer, BasePermission bp)
- throws XmlPullParserException, java.io.IOException {
- if (bp.sourcePackage != null) {
- serializer.startTag(null, TAG_ITEM);
- serializer.attribute(null, ATTR_NAME, bp.name);
- serializer.attribute(null, "package", bp.sourcePackage);
- if (bp.protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
- serializer.attribute(null, "protection", Integer.toString(bp.protectionLevel));
- }
- if (PackageManagerService.DEBUG_SETTINGS)
- Log.v(PackageManagerService.TAG, "Writing perm: name=" + bp.name + " type="
- + bp.type);
- if (bp.type == BasePermission.TYPE_DYNAMIC) {
- final PermissionInfo pi = bp.perm != null ? bp.perm.info : bp.pendingInfo;
- if (pi != null) {
- serializer.attribute(null, "type", "dynamic");
- if (pi.icon != 0) {
- serializer.attribute(null, "icon", Integer.toString(pi.icon));
- }
- if (pi.nonLocalizedLabel != null) {
- serializer.attribute(null, "label", pi.nonLocalizedLabel.toString());
- }
- }
- }
- serializer.endTag(null, TAG_ITEM);
- }
+ void writePermissionLPr(XmlSerializer serializer, BasePermission bp) throws IOException {
+ bp.writeLPr(serializer);
}
ArrayList<PackageSetting> getListOfIncompleteInstallPackagesLPr() {
@@ -3088,9 +3040,9 @@
if (tagName.equals("package")) {
readPackageLPw(parser);
} else if (tagName.equals("permissions")) {
- readPermissionsLPw(mPermissions, parser);
+ mPermissions.readPermissions(parser);
} else if (tagName.equals("permission-trees")) {
- readPermissionsLPw(mPermissionTrees, parser);
+ PermissionSettings.readPermissions(mPermissionTrees, parser);
} else if (tagName.equals("shared-user")) {
readSharedUserLPw(parser);
} else if (tagName.equals("preferred-packages")) {
@@ -3169,7 +3121,8 @@
}
} else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) {
final String enforcement = parser.getAttributeValue(null, ATTR_ENFORCEMENT);
- mReadExternalStorageEnforced = "1".equals(enforcement);
+ mReadExternalStorageEnforced =
+ "1".equals(enforcement) ? Boolean.TRUE : Boolean.FALSE;
} else if (tagName.equals("keyset-settings")) {
mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs);
} else if (TAG_VERSION.equals(tagName)) {
@@ -3593,72 +3546,6 @@
}
}
- private int readInt(XmlPullParser parser, String ns, String name, int defValue) {
- String v = parser.getAttributeValue(ns, name);
- try {
- if (v == null) {
- return defValue;
- }
- return Integer.parseInt(v);
- } catch (NumberFormatException e) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: attribute " + name
- + " has bad integer value " + v + " at "
- + parser.getPositionDescription());
- }
- return defValue;
- }
-
- private void readPermissionsLPw(ArrayMap<String, BasePermission> out, XmlPullParser parser)
- throws IOException, XmlPullParserException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- final String tagName = parser.getName();
- if (tagName.equals(TAG_ITEM)) {
- final String name = parser.getAttributeValue(null, ATTR_NAME);
- final String sourcePackage = parser.getAttributeValue(null, "package");
- final String ptype = parser.getAttributeValue(null, "type");
- if (name != null && sourcePackage != null) {
- final boolean dynamic = "dynamic".equals(ptype);
- BasePermission bp = out.get(name);
- // If the permission is builtin, do not clobber it.
- if (bp == null || bp.type != BasePermission.TYPE_BUILTIN) {
- bp = new BasePermission(name.intern(), sourcePackage,
- dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
- }
- bp.protectionLevel = readInt(parser, null, "protection",
- PermissionInfo.PROTECTION_NORMAL);
- bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
- if (dynamic) {
- PermissionInfo pi = new PermissionInfo();
- pi.packageName = sourcePackage.intern();
- pi.name = name.intern();
- pi.icon = readInt(parser, null, "icon", 0);
- pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
- pi.protectionLevel = bp.protectionLevel;
- bp.pendingInfo = pi;
- }
- out.put(bp.name, bp);
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: permissions has" + " no name at "
- + parser.getPositionDescription());
- }
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Unknown element reading permissions: " + parser.getName() + " at "
- + parser.getPositionDescription());
- }
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
private void readDisabledSysPackageLPw(XmlPullParser parser) throws XmlPullParserException,
IOException {
String name = parser.getAttributeValue(null, ATTR_NAME);
@@ -4385,10 +4272,6 @@
return ps;
}
- private String compToString(ArraySet<String> cmp) {
- return cmp != null ? Arrays.toString(cmp.toArray()) : "[]";
- }
-
boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
final PackageSetting ps = mPackages.get(componentInfo.packageName);
if (ps == null) return false;
@@ -5001,45 +4884,8 @@
void dumpPermissionsLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
DumpState dumpState) {
- boolean printedSomething = false;
- for (BasePermission p : mPermissions.values()) {
- if (packageName != null && !packageName.equals(p.sourcePackage)) {
- continue;
- }
- if (permissionNames != null && !permissionNames.contains(p.name)) {
- continue;
- }
- if (!printedSomething) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("Permissions:");
- printedSomething = true;
- }
- pw.print(" Permission ["); pw.print(p.name); pw.print("] (");
- pw.print(Integer.toHexString(System.identityHashCode(p)));
- pw.println("):");
- pw.print(" sourcePackage="); pw.println(p.sourcePackage);
- pw.print(" uid="); pw.print(p.uid);
- pw.print(" gids="); pw.print(Arrays.toString(
- p.computeGids(UserHandle.USER_SYSTEM)));
- pw.print(" type="); pw.print(p.type);
- pw.print(" prot=");
- pw.println(PermissionInfo.protectionToString(p.protectionLevel));
- if (p.perm != null) {
- pw.print(" perm="); pw.println(p.perm);
- if ((p.perm.info.flags & PermissionInfo.FLAG_INSTALLED) == 0
- || (p.perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0) {
- pw.print(" flags=0x"); pw.println(Integer.toHexString(p.perm.info.flags));
- }
- }
- if (p.packageSetting != null) {
- pw.print(" packageSetting="); pw.println(p.packageSetting);
- }
- if (READ_EXTERNAL_STORAGE.equals(p.name)) {
- pw.print(" enforced=");
- pw.println(mReadExternalStorageEnforced);
- }
- }
+ mPermissions.dumpPermissions(pw, packageName, permissionNames,
+ (mReadExternalStorageEnforced == Boolean.TRUE), dumpState);
}
void dumpSharedUsersLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
@@ -5248,7 +5094,7 @@
private final Handler mHandler = new MyHandler();
- private final Object mLock;
+ private final Object mPersistenceLock;
@GuardedBy("mLock")
private final SparseBooleanArray mWriteScheduled = new SparseBooleanArray();
@@ -5265,8 +5111,8 @@
// The mapping keys are user ids.
private final SparseBooleanArray mDefaultPermissionsGranted = new SparseBooleanArray();
- public RuntimePermissionPersistence(Object lock) {
- mLock = lock;
+ public RuntimePermissionPersistence(Object persistenceLock) {
+ mPersistenceLock = persistenceLock;
}
public boolean areDefaultRuntimPermissionsGrantedLPr(int userId) {
@@ -5321,7 +5167,7 @@
ArrayMap<String, List<PermissionState>> permissionsForPackage = new ArrayMap<>();
ArrayMap<String, List<PermissionState>> permissionsForSharedUser = new ArrayMap<>();
- synchronized (mLock) {
+ synchronized (mPersistenceLock) {
mWriteScheduled.delete(userId);
final int packageCount = mPackages.size();
@@ -5470,7 +5316,7 @@
PermissionsState permissionsState = sb.getPermissionsState();
for (PermissionState permissionState
: permissionsState.getRuntimePermissionStates(userId)) {
- BasePermission bp = mPermissions.get(permissionState.getName());
+ BasePermission bp = mPermissions.getPermission(permissionState.getName());
if (bp != null) {
permissionsState.revokeRuntimePermission(bp, userId);
permissionsState.updatePermissionFlags(bp, userId,
@@ -5631,7 +5477,7 @@
switch (parser.getName()) {
case TAG_ITEM: {
String name = parser.getAttributeValue(null, ATTR_NAME);
- BasePermission bp = mPermissions.get(name);
+ BasePermission bp = mPermissions.getPermission(name);
if (bp == null) {
Slog.w(PackageManagerService.TAG, "Unknown permission:" + name);
XmlUtils.skipCurrentTag(parser);
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 06e020a..a0dadae 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -16,12 +16,18 @@
package com.android.server.pm;
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
import android.util.ArraySet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
/**
* Settings data for a particular shared user ID we know about.
*/
-final class SharedUserSetting extends SettingBase {
+public final class SharedUserSetting extends SettingBase {
final String name;
int userId;
@@ -73,4 +79,18 @@
setPrivateFlags(this.pkgPrivateFlags | packageSetting.pkgPrivateFlags);
}
}
+
+ public @Nullable List<PackageParser.Package> getPackages() {
+ if (packages == null || packages.size() == 0) {
+ return null;
+ }
+ final ArrayList<PackageParser.Package> pkgList = new ArrayList<>(packages.size());
+ for (PackageSetting ps : packages) {
+ if (ps == null) {
+ continue;
+ }
+ pkgList.add(ps.pkg);
+ }
+ return pkgList;
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index 3060840..f922ad1 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -25,6 +25,7 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.ShortcutService.DumpFilter;
import com.android.server.pm.ShortcutUser.PackageWithUser;
import org.json.JSONException;
@@ -293,7 +294,7 @@
return ret;
}
- public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) {
pw.println();
pw.print(prefix);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 6f70f4c..6fc1e73 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -33,6 +33,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
+import com.android.server.pm.ShortcutService.DumpFilter;
import com.android.server.pm.ShortcutService.ShortcutOperation;
import com.android.server.pm.ShortcutService.Stats;
@@ -1144,7 +1145,7 @@
return false;
}
- public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) {
pw.println();
pw.print(prefix);
@@ -1186,9 +1187,7 @@
final int size = shortcuts.size();
for (int i = 0; i < size; i++) {
final ShortcutInfo si = shortcuts.valueAt(i);
- pw.print(prefix);
- pw.print(" ");
- pw.println(si.toInsecureString());
+ pw.println(si.toDumpString(prefix + " "));
if (si.getBitmapPath() != null) {
final long len = new File(si.getBitmapPath()).length();
pw.print(prefix);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 0e572d8..27560c5f 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -40,8 +40,8 @@
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
@@ -134,6 +134,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import java.util.regex.Pattern;
/**
* TODO:
@@ -484,12 +485,13 @@
final private IUidObserver mUidObserver = new IUidObserver.Stub() {
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq) {
- handleOnUidStateChanged(uid, procState);
+ injectPostToHandler(() -> handleOnUidStateChanged(uid, procState));
}
@Override
public void onUidGone(int uid, boolean disabled) {
- handleOnUidStateChanged(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
+ injectPostToHandler(() ->
+ handleOnUidStateChanged(uid, ActivityManager.PROCESS_STATE_NONEXISTENT));
}
@Override
@@ -3454,121 +3456,265 @@
@VisibleForTesting
void dumpNoCheck(FileDescriptor fd, PrintWriter pw, String[] args) {
+ final DumpFilter filter = parseDumpArgs(args);
- boolean dumpMain = true;
- boolean checkin = false;
- boolean clear = false;
- boolean dumpUid = false;
- boolean dumpFiles = false;
-
- if (args != null) {
- for (String arg : args) {
- if ("-c".equals(arg)) {
- checkin = true;
-
- } else if ("--checkin".equals(arg)) {
- checkin = true;
- clear = true;
-
- } else if ("-a".equals(arg) || "--all".equals(arg)) {
- dumpUid = true;
- dumpFiles = true;
-
- } else if ("-u".equals(arg) || "--uid".equals(arg)) {
- dumpUid = true;
-
- } else if ("-f".equals(arg) || "--files".equals(arg)) {
- dumpFiles = true;
-
- } else if ("-n".equals(arg) || "--no-main".equals(arg)) {
- dumpMain = false;
- }
- }
- }
-
- if (checkin) {
+ if (filter.shouldDumpCheckIn()) {
// Other flags are not supported for checkin.
- dumpCheckin(pw, clear);
+ dumpCheckin(pw, filter.shouldCheckInClear());
} else {
- if (dumpMain) {
- dumpInner(pw);
+ if (filter.shouldDumpMain()) {
+ dumpInner(pw, filter);
pw.println();
}
- if (dumpUid) {
+ if (filter.shouldDumpUid()) {
dumpUid(pw);
pw.println();
}
- if (dumpFiles) {
+ if (filter.shouldDumpFiles()) {
dumpDumpFiles(pw);
pw.println();
}
}
}
- private void dumpInner(PrintWriter pw) {
- synchronized (mLock) {
- final long now = injectCurrentTimeMillis();
- pw.print("Now: [");
- pw.print(now);
- pw.print("] ");
- pw.print(formatTime(now));
+ private static DumpFilter parseDumpArgs(String[] args) {
+ final DumpFilter filter = new DumpFilter();
+ if (args == null) {
+ return filter;
+ }
- pw.print(" Raw last reset: [");
- pw.print(mRawLastResetTime);
- pw.print("] ");
- pw.print(formatTime(mRawLastResetTime));
+ int argIndex = 0;
+ while (argIndex < args.length) {
+ final String arg = args[argIndex++];
- final long last = getLastResetTimeLocked();
- pw.print(" Last reset: [");
- pw.print(last);
- pw.print("] ");
- pw.print(formatTime(last));
+ if ("-c".equals(arg)) {
+ filter.setDumpCheckIn(true);
+ continue;
+ }
+ if ("--checkin".equals(arg)) {
+ filter.setDumpCheckIn(true);
+ filter.setCheckInClear(true);
+ continue;
+ }
+ if ("-a".equals(arg) || "--all".equals(arg)) {
+ filter.setDumpUid(true);
+ filter.setDumpFiles(true);
+ continue;
+ }
+ if ("-u".equals(arg) || "--uid".equals(arg)) {
+ filter.setDumpUid(true);
+ continue;
+ }
+ if ("-f".equals(arg) || "--files".equals(arg)) {
+ filter.setDumpFiles(true);
+ continue;
+ }
+ if ("-n".equals(arg) || "--no-main".equals(arg)) {
+ filter.setDumpMain(false);
+ continue;
+ }
+ if ("--user".equals(arg)) {
+ if (argIndex >= args.length) {
+ throw new IllegalArgumentException("Missing user ID for --user");
+ }
+ try {
+ filter.addUser(Integer.parseInt(args[argIndex++]));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid user ID", e);
+ }
+ continue;
+ }
+ if ("-p".equals(arg) || "--package".equals(arg)) {
+ if (argIndex >= args.length) {
+ throw new IllegalArgumentException("Missing package name for --package");
+ }
+ filter.addPackageRegex(args[argIndex++]);
+ filter.setDumpDetails(false);
+ continue;
+ }
+ if (arg.startsWith("-")) {
+ throw new IllegalArgumentException("Unknown option " + arg);
+ }
+ break;
+ }
+ while (argIndex < args.length) {
+ filter.addPackage(args[argIndex++]);
+ }
+ return filter;
+ }
- final long next = getNextResetTimeLocked();
- pw.print(" Next reset: [");
- pw.print(next);
- pw.print("] ");
- pw.print(formatTime(next));
+ static class DumpFilter {
+ private boolean mDumpCheckIn = false;
+ private boolean mCheckInClear = false;
- pw.print(" Config:");
- pw.print(" Max icon dim: ");
- pw.println(mMaxIconDimension);
- pw.print(" Icon format: ");
- pw.println(mIconPersistFormat);
- pw.print(" Icon quality: ");
- pw.println(mIconPersistQuality);
- pw.print(" saveDelayMillis: ");
- pw.println(mSaveDelayMillis);
- pw.print(" resetInterval: ");
- pw.println(mResetInterval);
- pw.print(" maxUpdatesPerInterval: ");
- pw.println(mMaxUpdatesPerInterval);
- pw.print(" maxShortcutsPerActivity: ");
- pw.println(mMaxShortcuts);
- pw.println();
+ private boolean mDumpMain = true;
+ private boolean mDumpUid = false;
+ private boolean mDumpFiles = false;
- pw.println(" Stats:");
- synchronized (mStatLock) {
- for (int i = 0; i < Stats.COUNT; i++) {
- dumpStatLS(pw, " ", i);
+ private boolean mDumpDetails = true;
+ private List<Pattern> mPackagePatterns = new ArrayList<>();
+ private List<Integer> mUsers = new ArrayList<>();
+
+ void addPackageRegex(String regex) {
+ mPackagePatterns.add(Pattern.compile(regex));
+ }
+
+ public void addPackage(String packageName) {
+ addPackageRegex(Pattern.quote(packageName));
+ }
+
+ void addUser(int userId) {
+ mUsers.add(userId);
+ }
+
+ boolean isPackageMatch(String packageName) {
+ if (mPackagePatterns.size() == 0) {
+ return true;
+ }
+ for (int i = 0; i < mPackagePatterns.size(); i++) {
+ if (mPackagePatterns.get(i).matcher(packageName).find()) {
+ return true;
}
}
+ return false;
+ }
- pw.println();
- pw.print(" #Failures: ");
- pw.println(mWtfCount);
+ boolean isUserMatch(int userId) {
+ if (mUsers.size() == 0) {
+ return true;
+ }
+ for (int i = 0; i < mUsers.size(); i++) {
+ if (mUsers.get(i) == userId) {
+ return true;
+ }
+ }
+ return false;
+ }
- if (mLastWtfStacktrace != null) {
- pw.print(" Last failure stack trace: ");
- pw.println(Log.getStackTraceString(mLastWtfStacktrace));
+ public boolean shouldDumpCheckIn() {
+ return mDumpCheckIn;
+ }
+
+ public void setDumpCheckIn(boolean dumpCheckIn) {
+ mDumpCheckIn = dumpCheckIn;
+ }
+
+ public boolean shouldCheckInClear() {
+ return mCheckInClear;
+ }
+
+ public void setCheckInClear(boolean checkInClear) {
+ mCheckInClear = checkInClear;
+ }
+
+ public boolean shouldDumpMain() {
+ return mDumpMain;
+ }
+
+ public void setDumpMain(boolean dumpMain) {
+ mDumpMain = dumpMain;
+ }
+
+ public boolean shouldDumpUid() {
+ return mDumpUid;
+ }
+
+ public void setDumpUid(boolean dumpUid) {
+ mDumpUid = dumpUid;
+ }
+
+ public boolean shouldDumpFiles() {
+ return mDumpFiles;
+ }
+
+ public void setDumpFiles(boolean dumpFiles) {
+ mDumpFiles = dumpFiles;
+ }
+
+ public boolean shouldDumpDetails() {
+ return mDumpDetails;
+ }
+
+ public void setDumpDetails(boolean dumpDetails) {
+ mDumpDetails = dumpDetails;
+ }
+ }
+
+ private void dumpInner(PrintWriter pw) {
+ dumpInner(pw, new DumpFilter());
+ }
+
+ private void dumpInner(PrintWriter pw, DumpFilter filter) {
+ synchronized (mLock) {
+ if (filter.shouldDumpDetails()) {
+ final long now = injectCurrentTimeMillis();
+ pw.print("Now: [");
+ pw.print(now);
+ pw.print("] ");
+ pw.print(formatTime(now));
+
+ pw.print(" Raw last reset: [");
+ pw.print(mRawLastResetTime);
+ pw.print("] ");
+ pw.print(formatTime(mRawLastResetTime));
+
+ final long last = getLastResetTimeLocked();
+ pw.print(" Last reset: [");
+ pw.print(last);
+ pw.print("] ");
+ pw.print(formatTime(last));
+
+ final long next = getNextResetTimeLocked();
+ pw.print(" Next reset: [");
+ pw.print(next);
+ pw.print("] ");
+ pw.print(formatTime(next));
+
+ pw.print(" Config:");
+ pw.print(" Max icon dim: ");
+ pw.println(mMaxIconDimension);
+ pw.print(" Icon format: ");
+ pw.println(mIconPersistFormat);
+ pw.print(" Icon quality: ");
+ pw.println(mIconPersistQuality);
+ pw.print(" saveDelayMillis: ");
+ pw.println(mSaveDelayMillis);
+ pw.print(" resetInterval: ");
+ pw.println(mResetInterval);
+ pw.print(" maxUpdatesPerInterval: ");
+ pw.println(mMaxUpdatesPerInterval);
+ pw.print(" maxShortcutsPerActivity: ");
+ pw.println(mMaxShortcuts);
+ pw.println();
+
+ pw.println(" Stats:");
+ synchronized (mStatLock) {
+ for (int i = 0; i < Stats.COUNT; i++) {
+ dumpStatLS(pw, " ", i);
+ }
+ }
+
+ pw.println();
+ pw.print(" #Failures: ");
+ pw.println(mWtfCount);
+
+ if (mLastWtfStacktrace != null) {
+ pw.print(" Last failure stack trace: ");
+ pw.println(Log.getStackTraceString(mLastWtfStacktrace));
+ }
+
+ pw.println();
+ mShortcutBitmapSaver.dumpLocked(pw, " ");
+
+ pw.println();
}
- pw.println();
- mShortcutBitmapSaver.dumpLocked(pw, " ");
-
for (int i = 0; i < mUsers.size(); i++) {
- pw.println();
- mUsers.valueAt(i).dump(pw, " ");
+ final ShortcutUser user = mUsers.valueAt(i);
+ if (filter.isUserMatch(user.getUserId())) {
+ user.dump(pw, " ", filter);
+ pw.println();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 2c388c4..55e6d28 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -28,6 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.server.pm.ShortcutService.DumpFilter;
import com.android.server.pm.ShortcutService.InvalidFileFormatException;
import libcore.util.Objects;
@@ -531,44 +532,54 @@
+ " S=" + restoredShortcuts[0]);
}
- public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
- pw.print(prefix);
- pw.print("User: ");
- pw.print(mUserId);
- pw.print(" Known locales: ");
- pw.print(mKnownLocales);
- pw.print(" Last app scan: [");
- pw.print(mLastAppScanTime);
- pw.print("] ");
- pw.print(ShortcutService.formatTime(mLastAppScanTime));
- pw.print(" Last app scan FP: ");
- pw.print(mLastAppScanOsFingerprint);
- pw.println();
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) {
+ if (filter.shouldDumpDetails()) {
+ pw.print(prefix);
+ pw.print("User: ");
+ pw.print(mUserId);
+ pw.print(" Known locales: ");
+ pw.print(mKnownLocales);
+ pw.print(" Last app scan: [");
+ pw.print(mLastAppScanTime);
+ pw.print("] ");
+ pw.print(ShortcutService.formatTime(mLastAppScanTime));
+ pw.print(" Last app scan FP: ");
+ pw.print(mLastAppScanOsFingerprint);
+ pw.println();
- prefix += prefix + " ";
+ prefix += prefix + " ";
- pw.print(prefix);
- pw.print("Cached launcher: ");
- pw.print(mCachedLauncher);
- pw.println();
+ pw.print(prefix);
+ pw.print("Cached launcher: ");
+ pw.print(mCachedLauncher);
+ pw.println();
- pw.print(prefix);
- pw.print("Last known launcher: ");
- pw.print(mLastKnownLauncher);
- pw.println();
+ pw.print(prefix);
+ pw.print("Last known launcher: ");
+ pw.print(mLastKnownLauncher);
+ pw.println();
+ }
for (int i = 0; i < mLaunchers.size(); i++) {
- mLaunchers.valueAt(i).dump(pw, prefix);
+ ShortcutLauncher launcher = mLaunchers.valueAt(i);
+ if (filter.isPackageMatch(launcher.getPackageName())) {
+ launcher.dump(pw, prefix, filter);
+ }
}
for (int i = 0; i < mPackages.size(); i++) {
- mPackages.valueAt(i).dump(pw, prefix);
+ ShortcutPackage pkg = mPackages.valueAt(i);
+ if (filter.isPackageMatch(pkg.getPackageName())) {
+ pkg.dump(pw, prefix, filter);
+ }
}
- pw.println();
- pw.print(prefix);
- pw.println("Bitmap directories: ");
- dumpDirectorySize(pw, prefix + " ", mService.getUserBitmapFilePath(mUserId));
+ if (filter.shouldDumpDetails()) {
+ pw.println();
+ pw.print(prefix);
+ pw.println("Bitmap directories: ");
+ dumpDirectorySize(pw, prefix + " ", mService.getUserBitmapFilePath(mUserId));
+ }
}
private void dumpDirectorySize(@NonNull PrintWriter pw,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f2d527b..1e5245c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1055,7 +1055,7 @@
/** Called by PackageManagerService */
public boolean exists(int userId) {
- return getUserInfoNoChecks(userId) != null;
+ return mLocalService.exists(userId);
}
@Override
@@ -3502,8 +3502,8 @@
* @param userId
* @return whether the user has been initialized yet
*/
- boolean isInitialized(int userId) {
- return (getUserInfo(userId).flags & UserInfo.FLAG_INITIALIZED) != 0;
+ boolean isUserInitialized(int userId) {
+ return mLocalService.isUserInitialized(userId);
}
private class LocalService extends UserManagerInternal {
@@ -3715,6 +3715,16 @@
}
return state == UserState.STATE_RUNNING_UNLOCKED;
}
+
+ @Override
+ public boolean isUserInitialized(int userId) {
+ return (getUserInfo(userId).flags & UserInfo.FLAG_INITIALIZED) != 0;
+ }
+
+ @Override
+ public boolean exists(int userId) {
+ return getUserInfoNoChecks(userId) != null;
+ }
}
/* Remove all the users except of the system one. */
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
new file mode 100644
index 0000000..09a6e9c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2006 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.pm.permission;
+
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
+import static android.content.pm.PermissionInfo.PROTECTION_NORMAL;
+import static android.content.pm.PermissionInfo.PROTECTION_SIGNATURE;
+import static android.content.pm.PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM;
+
+import static com.android.server.pm.Settings.ATTR_NAME;
+import static com.android.server.pm.Settings.ATTR_PACKAGE;
+import static com.android.server.pm.Settings.TAG_ITEM;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.Permission;
+import android.content.pm.PermissionInfo;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.server.pm.DumpState;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.PackageSettingBase;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+public final class BasePermission {
+ static final String TAG = "PackageManager";
+
+ public static final int TYPE_NORMAL = 0;
+ public static final int TYPE_BUILTIN = 1;
+ public static final int TYPE_DYNAMIC = 2;
+ @IntDef(value = {
+ TYPE_NORMAL,
+ TYPE_BUILTIN,
+ TYPE_DYNAMIC,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PermissionType {}
+
+ @IntDef(value = {
+ PROTECTION_DANGEROUS,
+ PROTECTION_NORMAL,
+ PROTECTION_SIGNATURE,
+ PROTECTION_SIGNATURE_OR_SYSTEM,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProtectionLevel {}
+
+ final String name;
+
+ @PermissionType final int type;
+
+ String sourcePackageName;
+
+ // TODO: Can we get rid of this? Seems we only use some signature info from the setting
+ PackageSettingBase sourcePackageSetting;
+
+ int protectionLevel;
+
+ PackageParser.Permission perm;
+
+ PermissionInfo pendingPermissionInfo;
+
+ /** UID that owns the definition of this permission */
+ int uid;
+
+ /** Additional GIDs given to apps granted this permission */
+ private int[] gids;
+
+ /**
+ * Flag indicating that {@link #gids} should be adjusted based on the
+ * {@link UserHandle} the granted app is running as.
+ */
+ private boolean perUser;
+
+ public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) {
+ name = _name;
+ sourcePackageName = _sourcePackageName;
+ type = _type;
+ // Default to most conservative protection level.
+ protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
+ }
+
+ @Override
+ public String toString() {
+ return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name
+ + "}";
+ }
+
+ public String getName() {
+ return name;
+ }
+ public int getProtectionLevel() {
+ return protectionLevel;
+ }
+ public String getSourcePackageName() {
+ return sourcePackageName;
+ }
+ public PackageSettingBase getSourcePackageSetting() {
+ return sourcePackageSetting;
+ }
+ public int getType() {
+ return type;
+ }
+ public int getUid() {
+ return uid;
+ }
+ public void setGids(int[] gids, boolean perUser) {
+ this.gids = gids;
+ this.perUser = perUser;
+ }
+ public void setPermission(@Nullable Permission perm) {
+ this.perm = perm;
+ }
+ public void setSourcePackageSetting(PackageSettingBase sourcePackageSetting) {
+ this.sourcePackageSetting = sourcePackageSetting;
+ }
+
+ public int[] computeGids(int userId) {
+ if (perUser) {
+ final int[] userGids = new int[gids.length];
+ for (int i = 0; i < gids.length; i++) {
+ userGids[i] = UserHandle.getUid(userId, gids[i]);
+ }
+ return userGids;
+ } else {
+ return gids;
+ }
+ }
+
+ public int calculateFootprint(BasePermission perm) {
+ if (uid == perm.uid) {
+ return perm.name.length() + perm.perm.info.calculateFootprint();
+ }
+ return 0;
+ }
+
+ public boolean isPermission(Permission perm) {
+ return this.perm == perm;
+ }
+
+ public boolean isDynamic() {
+ return type == TYPE_DYNAMIC;
+ }
+
+
+ public boolean isNormal() {
+ return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_NORMAL;
+ }
+ public boolean isRuntime() {
+ return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_DANGEROUS;
+ }
+ public boolean isSignature() {
+ return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) ==
+ PermissionInfo.PROTECTION_SIGNATURE;
+ }
+
+ public boolean isAppOp() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+ }
+ public boolean isDevelopment() {
+ return isSignature()
+ && (protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
+ }
+ public boolean isInstaller() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0;
+ }
+ public boolean isInstant() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
+ }
+ public boolean isOEM() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_OEM) != 0;
+ }
+ public boolean isPre23() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0;
+ }
+ public boolean isPreInstalled() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0;
+ }
+ public boolean isPrivileged() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0;
+ }
+ public boolean isRuntimeOnly() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
+ }
+ public boolean isSetup() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0;
+ }
+ public boolean isVerifier() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0;
+ }
+
+ public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
+ if (!origPackageName.equals(sourcePackageName)) {
+ return;
+ }
+ sourcePackageName = newPackageName;
+ sourcePackageSetting = null;
+ perm = null;
+ if (pendingPermissionInfo != null) {
+ pendingPermissionInfo.packageName = newPackageName;
+ }
+ uid = 0;
+ setGids(null, false);
+ }
+
+ public boolean addToTree(@ProtectionLevel int protectionLevel,
+ @NonNull PermissionInfo info, @NonNull BasePermission tree) {
+ final boolean changed =
+ (this.protectionLevel != protectionLevel
+ || perm == null
+ || uid != tree.uid
+ || !perm.owner.equals(tree.perm.owner)
+ || !comparePermissionInfos(perm.info, info));
+ this.protectionLevel = protectionLevel;
+ info = new PermissionInfo(info);
+ info.protectionLevel = protectionLevel;
+ perm = new PackageParser.Permission(tree.perm.owner, info);
+ perm.info.packageName = tree.perm.info.packageName;
+ uid = tree.uid;
+ return changed;
+ }
+
+ public void updateDynamicPermission(Map<String, BasePermission> permissionTrees) {
+ if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
+ + getName() + " pkg=" + getSourcePackageName()
+ + " info=" + pendingPermissionInfo);
+ if (sourcePackageSetting == null && pendingPermissionInfo != null) {
+ final BasePermission tree = findPermissionTreeLP(permissionTrees, name);
+ if (tree != null && tree.perm != null) {
+ sourcePackageSetting = tree.sourcePackageSetting;
+ perm = new PackageParser.Permission(tree.perm.owner,
+ new PermissionInfo(pendingPermissionInfo));
+ perm.info.packageName = tree.perm.info.packageName;
+ perm.info.name = name;
+ uid = tree.uid;
+ }
+ }
+ }
+
+ public static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p,
+ @NonNull PackageParser.Package pkg, Map<String, BasePermission> permissionTrees,
+ boolean chatty) {
+ final PackageSettingBase pkgSetting = (PackageSettingBase) pkg.mExtras;
+ // Allow system apps to redefine non-system permissions
+ if (bp != null && !Objects.equals(bp.sourcePackageName, p.info.packageName)) {
+ final boolean currentOwnerIsSystem = (bp.perm != null
+ && bp.perm.owner.isSystemApp());
+ if (p.owner.isSystemApp()) {
+ if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {
+ // It's a built-in permission and no owner, take ownership now
+ bp.sourcePackageSetting = pkgSetting;
+ bp.perm = p;
+ bp.uid = pkg.applicationInfo.uid;
+ bp.sourcePackageName = p.info.packageName;
+ p.info.flags |= PermissionInfo.FLAG_INSTALLED;
+ } else if (!currentOwnerIsSystem) {
+ String msg = "New decl " + p.owner + " of permission "
+ + p.info.name + " is system; overriding " + bp.sourcePackageName;
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ bp = null;
+ }
+ }
+ }
+ if (bp == null) {
+ bp = new BasePermission(p.info.name, p.info.packageName, TYPE_NORMAL);
+ }
+ StringBuilder r = null;
+ if (bp.perm == null) {
+ if (bp.sourcePackageName == null
+ || bp.sourcePackageName.equals(p.info.packageName)) {
+ final BasePermission tree = findPermissionTreeLP(permissionTrees, p.info.name);
+ if (tree == null
+ || tree.sourcePackageName.equals(p.info.packageName)) {
+ bp.sourcePackageSetting = pkgSetting;
+ bp.perm = p;
+ bp.uid = pkg.applicationInfo.uid;
+ bp.sourcePackageName = p.info.packageName;
+ p.info.flags |= PermissionInfo.FLAG_INSTALLED;
+ if (chatty) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
+ }
+ r.append(p.info.name);
+ }
+ } else {
+ Slog.w(TAG, "Permission " + p.info.name + " from package "
+ + p.info.packageName + " ignored: base tree "
+ + tree.name + " is from package "
+ + tree.sourcePackageName);
+ }
+ } else {
+ Slog.w(TAG, "Permission " + p.info.name + " from package "
+ + p.info.packageName + " ignored: original from "
+ + bp.sourcePackageName);
+ }
+ } else if (chatty) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
+ }
+ r.append("DUP:");
+ r.append(p.info.name);
+ }
+ if (bp.perm == p) {
+ bp.protectionLevel = p.info.protectionLevel;
+ }
+ if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
+ Log.d(TAG, " Permissions: " + r);
+ }
+ return bp;
+ }
+
+ public static BasePermission enforcePermissionTreeLP(
+ Map<String, BasePermission> permissionTrees, String permName, int callingUid) {
+ if (permName != null) {
+ BasePermission bp = findPermissionTreeLP(permissionTrees, permName);
+ if (bp != null) {
+ if (bp.uid == UserHandle.getAppId(callingUid)) {//UserHandle.getAppId(Binder.getCallingUid())) {
+ return bp;
+ }
+ throw new SecurityException("Calling uid " + callingUid
+ + " is not allowed to add to permission tree "
+ + bp.name + " owned by uid " + bp.uid);
+ }
+ }
+ throw new SecurityException("No permission tree found for " + permName);
+ }
+
+ public void enforceDeclaredUsedAndRuntimeOrDevelopment(PackageParser.Package pkg) {
+ int index = pkg.requestedPermissions.indexOf(name);
+ if (index == -1) {
+ throw new SecurityException("Package " + pkg.packageName
+ + " has not requested permission " + name);
+ }
+ if (!isRuntime() && !isDevelopment()) {
+ throw new SecurityException("Permission " + name
+ + " is not a changeable permission type");
+ }
+ }
+
+ private static BasePermission findPermissionTreeLP(
+ Map<String, BasePermission> permissionTrees, String permName) {
+ for (BasePermission bp : permissionTrees.values()) {
+ if (permName.startsWith(bp.name) &&
+ permName.length() > bp.name.length() &&
+ permName.charAt(bp.name.length()) == '.') {
+ return bp;
+ }
+ }
+ return null;
+ }
+
+ public @Nullable PermissionInfo generatePermissionInfo(@NonNull String groupName, int flags) {
+ if (groupName == null) {
+ if (perm == null || perm.info.group == null) {
+ return generatePermissionInfo(protectionLevel, flags);
+ }
+ } else {
+ if (perm != null && groupName.equals(perm.info.group)) {
+ return PackageParser.generatePermissionInfo(perm, flags);
+ }
+ }
+ return null;
+ }
+
+ public @NonNull PermissionInfo generatePermissionInfo(int adjustedProtectionLevel, int flags) {
+ final boolean protectionLevelChanged = protectionLevel != adjustedProtectionLevel;
+ // if we return different protection level, don't use the cached info
+ if (perm != null && !protectionLevelChanged) {
+ return PackageParser.generatePermissionInfo(perm, flags);
+ }
+ final PermissionInfo pi = new PermissionInfo();
+ pi.name = name;
+ pi.packageName = sourcePackageName;
+ pi.nonLocalizedLabel = name;
+ pi.protectionLevel = protectionLevelChanged ? adjustedProtectionLevel : protectionLevel;
+ return pi;
+ }
+
+ public static boolean readLPw(@NonNull Map<String, BasePermission> out,
+ @NonNull XmlPullParser parser) {
+ final String tagName = parser.getName();
+ if (!tagName.equals(TAG_ITEM)) {
+ return false;
+ }
+ final String name = parser.getAttributeValue(null, ATTR_NAME);
+ final String sourcePackage = parser.getAttributeValue(null, ATTR_PACKAGE);
+ final String ptype = parser.getAttributeValue(null, "type");
+ if (name == null || sourcePackage == null) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: permissions has" + " no name at "
+ + parser.getPositionDescription());
+ return false;
+ }
+ final boolean dynamic = "dynamic".equals(ptype);
+ BasePermission bp = out.get(name);
+ // If the permission is builtin, do not clobber it.
+ if (bp == null || bp.type != TYPE_BUILTIN) {
+ bp = new BasePermission(name.intern(), sourcePackage,
+ dynamic ? TYPE_DYNAMIC : TYPE_NORMAL);
+ }
+ bp.protectionLevel = readInt(parser, null, "protection",
+ PermissionInfo.PROTECTION_NORMAL);
+ bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
+ if (dynamic) {
+ final PermissionInfo pi = new PermissionInfo();
+ pi.packageName = sourcePackage.intern();
+ pi.name = name.intern();
+ pi.icon = readInt(parser, null, "icon", 0);
+ pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
+ pi.protectionLevel = bp.protectionLevel;
+ bp.pendingPermissionInfo = pi;
+ }
+ out.put(bp.name, bp);
+ return true;
+ }
+
+ private static int readInt(XmlPullParser parser, String ns, String name, int defValue) {
+ String v = parser.getAttributeValue(ns, name);
+ try {
+ if (v == null) {
+ return defValue;
+ }
+ return Integer.parseInt(v);
+ } catch (NumberFormatException e) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: attribute " + name
+ + " has bad integer value " + v + " at "
+ + parser.getPositionDescription());
+ }
+ return defValue;
+ }
+
+ public void writeLPr(@NonNull XmlSerializer serializer) throws IOException {
+ if (sourcePackageName == null) {
+ return;
+ }
+ serializer.startTag(null, TAG_ITEM);
+ serializer.attribute(null, ATTR_NAME, name);
+ serializer.attribute(null, ATTR_PACKAGE, sourcePackageName);
+ if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
+ serializer.attribute(null, "protection", Integer.toString(protectionLevel));
+ }
+ if (type == BasePermission.TYPE_DYNAMIC) {
+ final PermissionInfo pi = perm != null ? perm.info : pendingPermissionInfo;
+ if (pi != null) {
+ serializer.attribute(null, "type", "dynamic");
+ if (pi.icon != 0) {
+ serializer.attribute(null, "icon", Integer.toString(pi.icon));
+ }
+ if (pi.nonLocalizedLabel != null) {
+ serializer.attribute(null, "label", pi.nonLocalizedLabel.toString());
+ }
+ }
+ }
+ serializer.endTag(null, TAG_ITEM);
+ }
+
+ private static boolean compareStrings(CharSequence s1, CharSequence s2) {
+ if (s1 == null) {
+ return s2 == null;
+ }
+ if (s2 == null) {
+ return false;
+ }
+ if (s1.getClass() != s2.getClass()) {
+ return false;
+ }
+ return s1.equals(s2);
+ }
+
+ private static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
+ if (pi1.icon != pi2.icon) return false;
+ if (pi1.logo != pi2.logo) return false;
+ if (pi1.protectionLevel != pi2.protectionLevel) return false;
+ if (!compareStrings(pi1.name, pi2.name)) return false;
+ if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
+ // We'll take care of setting this one.
+ if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
+ // These are not currently stored in settings.
+ //if (!compareStrings(pi1.group, pi2.group)) return false;
+ //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
+ //if (pi1.labelRes != pi2.labelRes) return false;
+ //if (pi1.descriptionRes != pi2.descriptionRes) return false;
+ return true;
+ }
+
+ public boolean dumpPermissionsLPr(@NonNull PrintWriter pw, @NonNull String packageName,
+ @NonNull Set<String> permissionNames, boolean readEnforced,
+ boolean printedSomething, @NonNull DumpState dumpState) {
+ if (packageName != null && !packageName.equals(sourcePackageName)) {
+ return false;
+ }
+ if (permissionNames != null && !permissionNames.contains(name)) {
+ return false;
+ }
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("Permissions:");
+ printedSomething = true;
+ }
+ pw.print(" Permission ["); pw.print(name); pw.print("] (");
+ pw.print(Integer.toHexString(System.identityHashCode(this)));
+ pw.println("):");
+ pw.print(" sourcePackage="); pw.println(sourcePackageName);
+ pw.print(" uid="); pw.print(uid);
+ pw.print(" gids="); pw.print(Arrays.toString(
+ computeGids(UserHandle.USER_SYSTEM)));
+ pw.print(" type="); pw.print(type);
+ pw.print(" prot=");
+ pw.println(PermissionInfo.protectionToString(protectionLevel));
+ if (perm != null) {
+ pw.print(" perm="); pw.println(perm);
+ if ((perm.info.flags & PermissionInfo.FLAG_INSTALLED) == 0
+ || (perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0) {
+ pw.print(" flags=0x"); pw.println(Integer.toHexString(perm.info.flags));
+ }
+ }
+ if (sourcePackageSetting != null) {
+ pw.print(" packageSetting="); pw.println(sourcePackageSetting);
+ }
+ if (READ_EXTERNAL_STORAGE.equals(name)) {
+ pw.print(" enforced=");
+ pw.println(readEnforced);
+ }
+ return true;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
new file mode 100644
index 0000000..161efd3
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -0,0 +1,1296 @@
+/*
+ * 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.pm.permission;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.DownloadManager;
+import android.app.admin.DevicePolicyManager;
+import android.companion.CompanionDeviceManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManagerInternal.PackagesProvider;
+import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.print.PrintManager;
+import android.provider.CalendarContract;
+import android.provider.ContactsContract;
+import android.provider.MediaStore;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.TelephonyManager;
+import android.security.Credentials;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.Xml;
+import com.android.internal.util.XmlUtils;
+import com.android.server.LocalServices;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.PackageSetting;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static android.os.Process.FIRST_APPLICATION_UID;
+
+/**
+ * This class is the policy for granting runtime permissions to
+ * platform components and default handlers in the system such
+ * that the device is usable out-of-the-box. For example, the
+ * shell UID is a part of the system and the Phone app should
+ * have phone related permission by default.
+ * <p>
+ * NOTE: This class is at the wrong abstraction level. It is a part of the package manager
+ * service but knows about lots of higher level subsystems. The correct way to do this is
+ * to have an interface defined in the package manager but have the impl next to other
+ * policy stuff like PhoneWindowManager
+ */
+public final class DefaultPermissionGrantPolicy {
+ private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars
+ private static final boolean DEBUG = false;
+
+ private static final int DEFAULT_FLAGS =
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES;
+
+ private static final String AUDIO_MIME_TYPE = "audio/mpeg";
+
+ private static final String TAG_EXCEPTIONS = "exceptions";
+ private static final String TAG_EXCEPTION = "exception";
+ private static final String TAG_PERMISSION = "permission";
+ private static final String ATTR_PACKAGE = "package";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_FIXED = "fixed";
+
+ private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>();
+ static {
+ PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE);
+ PHONE_PERMISSIONS.add(Manifest.permission.CALL_PHONE);
+ PHONE_PERMISSIONS.add(Manifest.permission.READ_CALL_LOG);
+ PHONE_PERMISSIONS.add(Manifest.permission.WRITE_CALL_LOG);
+ PHONE_PERMISSIONS.add(Manifest.permission.ADD_VOICEMAIL);
+ PHONE_PERMISSIONS.add(Manifest.permission.USE_SIP);
+ PHONE_PERMISSIONS.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
+ }
+
+ private static final Set<String> CONTACTS_PERMISSIONS = new ArraySet<>();
+ static {
+ CONTACTS_PERMISSIONS.add(Manifest.permission.READ_CONTACTS);
+ CONTACTS_PERMISSIONS.add(Manifest.permission.WRITE_CONTACTS);
+ CONTACTS_PERMISSIONS.add(Manifest.permission.GET_ACCOUNTS);
+ }
+
+ private static final Set<String> LOCATION_PERMISSIONS = new ArraySet<>();
+ static {
+ LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
+ LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
+ }
+
+ private static final Set<String> CALENDAR_PERMISSIONS = new ArraySet<>();
+ static {
+ CALENDAR_PERMISSIONS.add(Manifest.permission.READ_CALENDAR);
+ CALENDAR_PERMISSIONS.add(Manifest.permission.WRITE_CALENDAR);
+ }
+
+ private static final Set<String> SMS_PERMISSIONS = new ArraySet<>();
+ static {
+ SMS_PERMISSIONS.add(Manifest.permission.SEND_SMS);
+ SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_SMS);
+ SMS_PERMISSIONS.add(Manifest.permission.READ_SMS);
+ SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_WAP_PUSH);
+ SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_MMS);
+ SMS_PERMISSIONS.add(Manifest.permission.READ_CELL_BROADCASTS);
+ }
+
+ private static final Set<String> MICROPHONE_PERMISSIONS = new ArraySet<>();
+ static {
+ MICROPHONE_PERMISSIONS.add(Manifest.permission.RECORD_AUDIO);
+ }
+
+ private static final Set<String> CAMERA_PERMISSIONS = new ArraySet<>();
+ static {
+ CAMERA_PERMISSIONS.add(Manifest.permission.CAMERA);
+ }
+
+ private static final Set<String> SENSORS_PERMISSIONS = new ArraySet<>();
+ static {
+ SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
+ }
+
+ private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
+ static {
+ STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+ STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ }
+
+ private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
+
+ private static final String ACTION_TRACK = "com.android.fitness.TRACK";
+
+ private final Handler mHandler;
+
+ private PackagesProvider mLocationPackagesProvider;
+ private PackagesProvider mVoiceInteractionPackagesProvider;
+ private PackagesProvider mSmsAppPackagesProvider;
+ private PackagesProvider mDialerAppPackagesProvider;
+ private PackagesProvider mSimCallManagerPackagesProvider;
+ private SyncAdapterPackagesProvider mSyncAdapterPackagesProvider;
+
+ private ArrayMap<String, List<DefaultPermissionGrant>> mGrantExceptions;
+ private final Context mContext;
+ private final Object mLock = new Object();
+ private final PackageManagerInternal mServiceInternal;
+ private final PermissionManagerService mPermissionManager;
+ private final DefaultPermissionGrantedCallback mPermissionGrantedCallback;
+ public interface DefaultPermissionGrantedCallback {
+ /** Callback when permissions have been granted */
+ public void onDefaultRuntimePermissionsGranted(int userId);
+ }
+
+ public DefaultPermissionGrantPolicy(Context context, Looper looper,
+ @Nullable DefaultPermissionGrantedCallback callback,
+ @NonNull PermissionManagerService permissionManager) {
+ mContext = context;
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS) {
+ synchronized (mLock) {
+ if (mGrantExceptions == null) {
+ mGrantExceptions = readDefaultPermissionExceptionsLocked();
+ }
+ }
+ }
+ }
+ };
+ mPermissionGrantedCallback = callback;
+ mPermissionManager = permissionManager;
+ mServiceInternal = LocalServices.getService(PackageManagerInternal.class);
+ }
+
+ public void setLocationPackagesProvider(PackagesProvider provider) {
+ synchronized (mLock) {
+ mLocationPackagesProvider = provider;
+ }
+ }
+
+ public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
+ synchronized (mLock) {
+ mVoiceInteractionPackagesProvider = provider;
+ }
+ }
+
+ public void setSmsAppPackagesProvider(PackagesProvider provider) {
+ synchronized (mLock) {
+ mSmsAppPackagesProvider = provider;
+ }
+ }
+
+ public void setDialerAppPackagesProvider(PackagesProvider provider) {
+ synchronized (mLock) {
+ mDialerAppPackagesProvider = provider;
+ }
+ }
+
+ public void setSimCallManagerPackagesProvider(PackagesProvider provider) {
+ synchronized (mLock) {
+ mSimCallManagerPackagesProvider = provider;
+ }
+ }
+
+ public void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider) {
+ synchronized (mLock) {
+ mSyncAdapterPackagesProvider = provider;
+ }
+ }
+
+ public void grantDefaultPermissions(Collection<PackageParser.Package> packages, int userId) {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
+ grantAllRuntimePermissions(packages, userId);
+ } else {
+ grantPermissionsToSysComponentsAndPrivApps(packages, userId);
+ grantDefaultSystemHandlerPermissions(userId);
+ grantDefaultPermissionExceptions(userId);
+ }
+ }
+
+ private void grantRuntimePermissionsForPackage(int userId, PackageParser.Package pkg) {
+ Set<String> permissions = new ArraySet<>();
+ for (String permission : pkg.requestedPermissions) {
+ final BasePermission bp = mPermissionManager.getPermission(permission);
+ if (bp == null) {
+ continue;
+ }
+ if (bp.isRuntime()) {
+ permissions.add(permission);
+ }
+ }
+ if (!permissions.isEmpty()) {
+ grantRuntimePermissions(pkg, permissions, true, userId);
+ }
+ }
+
+ private void grantAllRuntimePermissions(
+ Collection<PackageParser.Package> packages, int userId) {
+ Log.i(TAG, "Granting all runtime permissions for user " + userId);
+ for (PackageParser.Package pkg : packages) {
+ grantRuntimePermissionsForPackage(userId, pkg);
+ }
+ }
+
+ public void scheduleReadDefaultPermissionExceptions() {
+ mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
+ }
+
+ private void grantPermissionsToSysComponentsAndPrivApps(
+ Collection<PackageParser.Package> packages, int userId) {
+ Log.i(TAG, "Granting permissions to platform components for user " + userId);
+ for (PackageParser.Package pkg : packages) {
+ if (!isSysComponentOrPersistentPlatformSignedPrivApp(pkg)
+ || !doesPackageSupportRuntimePermissions(pkg)
+ || pkg.requestedPermissions.isEmpty()) {
+ continue;
+ }
+ grantRuntimePermissionsForPackage(userId, pkg);
+ }
+ }
+
+ private void grantDefaultSystemHandlerPermissions(int userId) {
+ Log.i(TAG, "Granting permissions to default platform handlers for user " + userId);
+
+ final PackagesProvider locationPackagesProvider;
+ final PackagesProvider voiceInteractionPackagesProvider;
+ final PackagesProvider smsAppPackagesProvider;
+ final PackagesProvider dialerAppPackagesProvider;
+ final PackagesProvider simCallManagerPackagesProvider;
+ final SyncAdapterPackagesProvider syncAdapterPackagesProvider;
+
+ synchronized (mLock) {
+ locationPackagesProvider = mLocationPackagesProvider;
+ voiceInteractionPackagesProvider = mVoiceInteractionPackagesProvider;
+ smsAppPackagesProvider = mSmsAppPackagesProvider;
+ dialerAppPackagesProvider = mDialerAppPackagesProvider;
+ simCallManagerPackagesProvider = mSimCallManagerPackagesProvider;
+ syncAdapterPackagesProvider = mSyncAdapterPackagesProvider;
+ }
+
+ String[] voiceInteractPackageNames = (voiceInteractionPackagesProvider != null)
+ ? voiceInteractionPackagesProvider.getPackages(userId) : null;
+ String[] locationPackageNames = (locationPackagesProvider != null)
+ ? locationPackagesProvider.getPackages(userId) : null;
+ String[] smsAppPackageNames = (smsAppPackagesProvider != null)
+ ? smsAppPackagesProvider.getPackages(userId) : null;
+ String[] dialerAppPackageNames = (dialerAppPackagesProvider != null)
+ ? dialerAppPackagesProvider.getPackages(userId) : null;
+ String[] simCallManagerPackageNames = (simCallManagerPackagesProvider != null)
+ ? simCallManagerPackagesProvider.getPackages(userId) : null;
+ String[] contactsSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
+ syncAdapterPackagesProvider.getPackages(ContactsContract.AUTHORITY, userId) : null;
+ String[] calendarSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
+ syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY, userId) : null;
+
+ // Installer
+ final String installerPackageName = mServiceInternal.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_INSTALLER, userId);
+ PackageParser.Package installerPackage = getSystemPackage(installerPackageName);
+ if (installerPackage != null
+ && doesPackageSupportRuntimePermissions(installerPackage)) {
+ grantRuntimePermissions(installerPackage, STORAGE_PERMISSIONS, true, userId);
+ }
+
+ // Verifier
+ final String verifierPackageName = mServiceInternal.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_VERIFIER, userId);
+ PackageParser.Package verifierPackage = getSystemPackage(verifierPackageName);
+ if (verifierPackage != null
+ && doesPackageSupportRuntimePermissions(verifierPackage)) {
+ grantRuntimePermissions(verifierPackage, STORAGE_PERMISSIONS, true, userId);
+ grantRuntimePermissions(verifierPackage, PHONE_PERMISSIONS, false, userId);
+ grantRuntimePermissions(verifierPackage, SMS_PERMISSIONS, false, userId);
+ }
+
+ // SetupWizard
+ final String setupWizardPackageName = mServiceInternal.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId);
+ PackageParser.Package setupPackage = getSystemPackage(setupWizardPackageName);
+ if (setupPackage != null
+ && doesPackageSupportRuntimePermissions(setupPackage)) {
+ grantRuntimePermissions(setupPackage, PHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(setupPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(setupPackage, LOCATION_PERMISSIONS, userId);
+ grantRuntimePermissions(setupPackage, CAMERA_PERMISSIONS, userId);
+ }
+
+ // Camera
+ Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+ PackageParser.Package cameraPackage = getDefaultSystemHandlerActivityPackage(
+ cameraIntent, userId);
+ if (cameraPackage != null
+ && doesPackageSupportRuntimePermissions(cameraPackage)) {
+ grantRuntimePermissions(cameraPackage, CAMERA_PERMISSIONS, userId);
+ grantRuntimePermissions(cameraPackage, MICROPHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(cameraPackage, STORAGE_PERMISSIONS, userId);
+ }
+
+ // Media provider
+ PackageParser.Package mediaStorePackage = getDefaultProviderAuthorityPackage(
+ MediaStore.AUTHORITY, userId);
+ if (mediaStorePackage != null) {
+ grantRuntimePermissions(mediaStorePackage, STORAGE_PERMISSIONS, true, userId);
+ }
+
+ // Downloads provider
+ PackageParser.Package downloadsPackage = getDefaultProviderAuthorityPackage(
+ "downloads", userId);
+ if (downloadsPackage != null) {
+ grantRuntimePermissions(downloadsPackage, STORAGE_PERMISSIONS, true, userId);
+ }
+
+ // Downloads UI
+ Intent downloadsUiIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
+ PackageParser.Package downloadsUiPackage = getDefaultSystemHandlerActivityPackage(
+ downloadsUiIntent, userId);
+ if (downloadsUiPackage != null
+ && doesPackageSupportRuntimePermissions(downloadsUiPackage)) {
+ grantRuntimePermissions(downloadsUiPackage, STORAGE_PERMISSIONS, true, userId);
+ }
+
+ // Storage provider
+ PackageParser.Package storagePackage = getDefaultProviderAuthorityPackage(
+ "com.android.externalstorage.documents", userId);
+ if (storagePackage != null) {
+ grantRuntimePermissions(storagePackage, STORAGE_PERMISSIONS, true, userId);
+ }
+
+ // CertInstaller
+ Intent certInstallerIntent = new Intent(Credentials.INSTALL_ACTION);
+ PackageParser.Package certInstallerPackage = getDefaultSystemHandlerActivityPackage(
+ certInstallerIntent, userId);
+ if (certInstallerPackage != null
+ && doesPackageSupportRuntimePermissions(certInstallerPackage)) {
+ grantRuntimePermissions(certInstallerPackage, STORAGE_PERMISSIONS, true, userId);
+ }
+
+ // Dialer
+ if (dialerAppPackageNames == null) {
+ Intent dialerIntent = new Intent(Intent.ACTION_DIAL);
+ PackageParser.Package dialerPackage = getDefaultSystemHandlerActivityPackage(
+ dialerIntent, userId);
+ if (dialerPackage != null) {
+ grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId);
+ }
+ } else {
+ for (String dialerAppPackageName : dialerAppPackageNames) {
+ PackageParser.Package dialerPackage = getSystemPackage(dialerAppPackageName);
+ if (dialerPackage != null) {
+ grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId);
+ }
+ }
+ }
+
+ // Sim call manager
+ if (simCallManagerPackageNames != null) {
+ for (String simCallManagerPackageName : simCallManagerPackageNames) {
+ PackageParser.Package simCallManagerPackage =
+ getSystemPackage(simCallManagerPackageName);
+ if (simCallManagerPackage != null) {
+ grantDefaultPermissionsToDefaultSimCallManager(simCallManagerPackage,
+ userId);
+ }
+ }
+ }
+
+ // SMS
+ if (smsAppPackageNames == null) {
+ Intent smsIntent = new Intent(Intent.ACTION_MAIN);
+ smsIntent.addCategory(Intent.CATEGORY_APP_MESSAGING);
+ PackageParser.Package smsPackage = getDefaultSystemHandlerActivityPackage(
+ smsIntent, userId);
+ if (smsPackage != null) {
+ grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
+ }
+ } else {
+ for (String smsPackageName : smsAppPackageNames) {
+ PackageParser.Package smsPackage = getSystemPackage(smsPackageName);
+ if (smsPackage != null) {
+ grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
+ }
+ }
+ }
+
+ // Cell Broadcast Receiver
+ Intent cbrIntent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
+ PackageParser.Package cbrPackage =
+ getDefaultSystemHandlerActivityPackage(cbrIntent, userId);
+ if (cbrPackage != null && doesPackageSupportRuntimePermissions(cbrPackage)) {
+ grantRuntimePermissions(cbrPackage, SMS_PERMISSIONS, userId);
+ }
+
+ // Carrier Provisioning Service
+ Intent carrierProvIntent = new Intent(Intents.SMS_CARRIER_PROVISION_ACTION);
+ PackageParser.Package carrierProvPackage =
+ getDefaultSystemHandlerServicePackage(carrierProvIntent, userId);
+ if (carrierProvPackage != null
+ && doesPackageSupportRuntimePermissions(carrierProvPackage)) {
+ grantRuntimePermissions(carrierProvPackage, SMS_PERMISSIONS, false, userId);
+ }
+
+ // Calendar
+ Intent calendarIntent = new Intent(Intent.ACTION_MAIN);
+ calendarIntent.addCategory(Intent.CATEGORY_APP_CALENDAR);
+ PackageParser.Package calendarPackage = getDefaultSystemHandlerActivityPackage(
+ calendarIntent, userId);
+ if (calendarPackage != null
+ && doesPackageSupportRuntimePermissions(calendarPackage)) {
+ grantRuntimePermissions(calendarPackage, CALENDAR_PERMISSIONS, userId);
+ grantRuntimePermissions(calendarPackage, CONTACTS_PERMISSIONS, userId);
+ }
+
+ // Calendar provider
+ PackageParser.Package calendarProviderPackage = getDefaultProviderAuthorityPackage(
+ CalendarContract.AUTHORITY, userId);
+ if (calendarProviderPackage != null) {
+ grantRuntimePermissions(calendarProviderPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(calendarProviderPackage, CALENDAR_PERMISSIONS,
+ true, userId);
+ grantRuntimePermissions(calendarProviderPackage, STORAGE_PERMISSIONS, userId);
+ }
+
+ // Calendar provider sync adapters
+ List<PackageParser.Package> calendarSyncAdapters = getHeadlessSyncAdapterPackages(
+ calendarSyncAdapterPackages, userId);
+ final int calendarSyncAdapterCount = calendarSyncAdapters.size();
+ for (int i = 0; i < calendarSyncAdapterCount; i++) {
+ PackageParser.Package calendarSyncAdapter = calendarSyncAdapters.get(i);
+ if (doesPackageSupportRuntimePermissions(calendarSyncAdapter)) {
+ grantRuntimePermissions(calendarSyncAdapter, CALENDAR_PERMISSIONS, userId);
+ }
+ }
+
+ // Contacts
+ Intent contactsIntent = new Intent(Intent.ACTION_MAIN);
+ contactsIntent.addCategory(Intent.CATEGORY_APP_CONTACTS);
+ PackageParser.Package contactsPackage = getDefaultSystemHandlerActivityPackage(
+ contactsIntent, userId);
+ if (contactsPackage != null
+ && doesPackageSupportRuntimePermissions(contactsPackage)) {
+ grantRuntimePermissions(contactsPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(contactsPackage, PHONE_PERMISSIONS, userId);
+ }
+
+ // Contacts provider sync adapters
+ List<PackageParser.Package> contactsSyncAdapters = getHeadlessSyncAdapterPackages(
+ contactsSyncAdapterPackages, userId);
+ final int contactsSyncAdapterCount = contactsSyncAdapters.size();
+ for (int i = 0; i < contactsSyncAdapterCount; i++) {
+ PackageParser.Package contactsSyncAdapter = contactsSyncAdapters.get(i);
+ if (doesPackageSupportRuntimePermissions(contactsSyncAdapter)) {
+ grantRuntimePermissions(contactsSyncAdapter, CONTACTS_PERMISSIONS, userId);
+ }
+ }
+
+ // Contacts provider
+ PackageParser.Package contactsProviderPackage = getDefaultProviderAuthorityPackage(
+ ContactsContract.AUTHORITY, userId);
+ if (contactsProviderPackage != null) {
+ grantRuntimePermissions(contactsProviderPackage, CONTACTS_PERMISSIONS,
+ true, userId);
+ grantRuntimePermissions(contactsProviderPackage, PHONE_PERMISSIONS,
+ true, userId);
+ grantRuntimePermissions(contactsProviderPackage, STORAGE_PERMISSIONS, userId);
+ }
+
+ // Device provisioning
+ Intent deviceProvisionIntent = new Intent(
+ DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE);
+ PackageParser.Package deviceProvisionPackage =
+ getDefaultSystemHandlerActivityPackage(deviceProvisionIntent, userId);
+ if (deviceProvisionPackage != null
+ && doesPackageSupportRuntimePermissions(deviceProvisionPackage)) {
+ grantRuntimePermissions(deviceProvisionPackage, CONTACTS_PERMISSIONS, userId);
+ }
+
+ // Maps
+ Intent mapsIntent = new Intent(Intent.ACTION_MAIN);
+ mapsIntent.addCategory(Intent.CATEGORY_APP_MAPS);
+ PackageParser.Package mapsPackage = getDefaultSystemHandlerActivityPackage(
+ mapsIntent, userId);
+ if (mapsPackage != null
+ && doesPackageSupportRuntimePermissions(mapsPackage)) {
+ grantRuntimePermissions(mapsPackage, LOCATION_PERMISSIONS, userId);
+ }
+
+ // Gallery
+ Intent galleryIntent = new Intent(Intent.ACTION_MAIN);
+ galleryIntent.addCategory(Intent.CATEGORY_APP_GALLERY);
+ PackageParser.Package galleryPackage = getDefaultSystemHandlerActivityPackage(
+ galleryIntent, userId);
+ if (galleryPackage != null
+ && doesPackageSupportRuntimePermissions(galleryPackage)) {
+ grantRuntimePermissions(galleryPackage, STORAGE_PERMISSIONS, userId);
+ }
+
+ // Email
+ Intent emailIntent = new Intent(Intent.ACTION_MAIN);
+ emailIntent.addCategory(Intent.CATEGORY_APP_EMAIL);
+ PackageParser.Package emailPackage = getDefaultSystemHandlerActivityPackage(
+ emailIntent, userId);
+ if (emailPackage != null
+ && doesPackageSupportRuntimePermissions(emailPackage)) {
+ grantRuntimePermissions(emailPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(emailPackage, CALENDAR_PERMISSIONS, userId);
+ }
+
+ // Browser
+ PackageParser.Package browserPackage = null;
+ String defaultBrowserPackage = mServiceInternal.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_BROWSER, userId);
+ if (defaultBrowserPackage != null) {
+ browserPackage = getPackage(defaultBrowserPackage);
+ }
+ if (browserPackage == null) {
+ Intent browserIntent = new Intent(Intent.ACTION_MAIN);
+ browserIntent.addCategory(Intent.CATEGORY_APP_BROWSER);
+ browserPackage = getDefaultSystemHandlerActivityPackage(
+ browserIntent, userId);
+ }
+ if (browserPackage != null
+ && doesPackageSupportRuntimePermissions(browserPackage)) {
+ grantRuntimePermissions(browserPackage, LOCATION_PERMISSIONS, userId);
+ }
+
+ // Voice interaction
+ if (voiceInteractPackageNames != null) {
+ for (String voiceInteractPackageName : voiceInteractPackageNames) {
+ PackageParser.Package voiceInteractPackage = getSystemPackage(
+ voiceInteractPackageName);
+ if (voiceInteractPackage != null
+ && doesPackageSupportRuntimePermissions(voiceInteractPackage)) {
+ grantRuntimePermissions(voiceInteractPackage,
+ CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(voiceInteractPackage,
+ CALENDAR_PERMISSIONS, userId);
+ grantRuntimePermissions(voiceInteractPackage,
+ MICROPHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(voiceInteractPackage,
+ PHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(voiceInteractPackage,
+ SMS_PERMISSIONS, userId);
+ grantRuntimePermissions(voiceInteractPackage,
+ LOCATION_PERMISSIONS, userId);
+ }
+ }
+ }
+
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ // Allow voice search on low-ram devices
+ Intent globalSearchIntent = new Intent("android.search.action.GLOBAL_SEARCH");
+ PackageParser.Package globalSearchPickerPackage =
+ getDefaultSystemHandlerActivityPackage(globalSearchIntent, userId);
+
+ if (globalSearchPickerPackage != null
+ && doesPackageSupportRuntimePermissions(globalSearchPickerPackage)) {
+ grantRuntimePermissions(globalSearchPickerPackage,
+ MICROPHONE_PERMISSIONS, true, userId);
+ grantRuntimePermissions(globalSearchPickerPackage,
+ LOCATION_PERMISSIONS, true, userId);
+ }
+ }
+
+ // Voice recognition
+ Intent voiceRecoIntent = new Intent("android.speech.RecognitionService");
+ voiceRecoIntent.addCategory(Intent.CATEGORY_DEFAULT);
+ PackageParser.Package voiceRecoPackage = getDefaultSystemHandlerServicePackage(
+ voiceRecoIntent, userId);
+ if (voiceRecoPackage != null
+ && doesPackageSupportRuntimePermissions(voiceRecoPackage)) {
+ grantRuntimePermissions(voiceRecoPackage, MICROPHONE_PERMISSIONS, userId);
+ }
+
+ // Location
+ if (locationPackageNames != null) {
+ for (String packageName : locationPackageNames) {
+ PackageParser.Package locationPackage = getSystemPackage(packageName);
+ if (locationPackage != null
+ && doesPackageSupportRuntimePermissions(locationPackage)) {
+ grantRuntimePermissions(locationPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(locationPackage, CALENDAR_PERMISSIONS, userId);
+ grantRuntimePermissions(locationPackage, MICROPHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(locationPackage, PHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(locationPackage, SMS_PERMISSIONS, userId);
+ grantRuntimePermissions(locationPackage, LOCATION_PERMISSIONS,
+ true, userId);
+ grantRuntimePermissions(locationPackage, CAMERA_PERMISSIONS, userId);
+ grantRuntimePermissions(locationPackage, SENSORS_PERMISSIONS, userId);
+ grantRuntimePermissions(locationPackage, STORAGE_PERMISSIONS, userId);
+ }
+ }
+ }
+
+ // Music
+ Intent musicIntent = new Intent(Intent.ACTION_VIEW);
+ musicIntent.addCategory(Intent.CATEGORY_DEFAULT);
+ musicIntent.setDataAndType(Uri.fromFile(new File("foo.mp3")),
+ AUDIO_MIME_TYPE);
+ PackageParser.Package musicPackage = getDefaultSystemHandlerActivityPackage(
+ musicIntent, userId);
+ if (musicPackage != null
+ && doesPackageSupportRuntimePermissions(musicPackage)) {
+ grantRuntimePermissions(musicPackage, STORAGE_PERMISSIONS, userId);
+ }
+
+ // Home
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_HOME);
+ homeIntent.addCategory(Intent.CATEGORY_LAUNCHER_APP);
+ PackageParser.Package homePackage = getDefaultSystemHandlerActivityPackage(
+ homeIntent, userId);
+ if (homePackage != null
+ && doesPackageSupportRuntimePermissions(homePackage)) {
+ grantRuntimePermissions(homePackage, LOCATION_PERMISSIONS, false, userId);
+ }
+
+ // Watches
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
+ // Home application on watches
+ Intent wearHomeIntent = new Intent(Intent.ACTION_MAIN);
+ wearHomeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
+
+ PackageParser.Package wearHomePackage = getDefaultSystemHandlerActivityPackage(
+ wearHomeIntent, userId);
+
+ if (wearHomePackage != null
+ && doesPackageSupportRuntimePermissions(wearHomePackage)) {
+ grantRuntimePermissions(wearHomePackage, CONTACTS_PERMISSIONS, false,
+ userId);
+ grantRuntimePermissions(wearHomePackage, PHONE_PERMISSIONS, true, userId);
+ grantRuntimePermissions(wearHomePackage, MICROPHONE_PERMISSIONS, false,
+ userId);
+ grantRuntimePermissions(wearHomePackage, LOCATION_PERMISSIONS, false,
+ userId);
+ }
+
+ // Fitness tracking on watches
+ Intent trackIntent = new Intent(ACTION_TRACK);
+ PackageParser.Package trackPackage = getDefaultSystemHandlerActivityPackage(
+ trackIntent, userId);
+ if (trackPackage != null
+ && doesPackageSupportRuntimePermissions(trackPackage)) {
+ grantRuntimePermissions(trackPackage, SENSORS_PERMISSIONS, false, userId);
+ grantRuntimePermissions(trackPackage, LOCATION_PERMISSIONS, false, userId);
+ }
+ }
+
+ // Print Spooler
+ PackageParser.Package printSpoolerPackage = getSystemPackage(
+ PrintManager.PRINT_SPOOLER_PACKAGE_NAME);
+ if (printSpoolerPackage != null
+ && doesPackageSupportRuntimePermissions(printSpoolerPackage)) {
+ grantRuntimePermissions(printSpoolerPackage, LOCATION_PERMISSIONS, true, userId);
+ }
+
+ // EmergencyInfo
+ Intent emergencyInfoIntent = new Intent(TelephonyManager.ACTION_EMERGENCY_ASSISTANCE);
+ PackageParser.Package emergencyInfoPckg = getDefaultSystemHandlerActivityPackage(
+ emergencyInfoIntent, userId);
+ if (emergencyInfoPckg != null
+ && doesPackageSupportRuntimePermissions(emergencyInfoPckg)) {
+ grantRuntimePermissions(emergencyInfoPckg, CONTACTS_PERMISSIONS, true, userId);
+ grantRuntimePermissions(emergencyInfoPckg, PHONE_PERMISSIONS, true, userId);
+ }
+
+ // NFC Tag viewer
+ Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW);
+ nfcTagIntent.setType("vnd.android.cursor.item/ndef_msg");
+ PackageParser.Package nfcTagPkg = getDefaultSystemHandlerActivityPackage(
+ nfcTagIntent, userId);
+ if (nfcTagPkg != null
+ && doesPackageSupportRuntimePermissions(nfcTagPkg)) {
+ grantRuntimePermissions(nfcTagPkg, CONTACTS_PERMISSIONS, false, userId);
+ grantRuntimePermissions(nfcTagPkg, PHONE_PERMISSIONS, false, userId);
+ }
+
+ // Storage Manager
+ Intent storageManagerIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
+ PackageParser.Package storageManagerPckg = getDefaultSystemHandlerActivityPackage(
+ storageManagerIntent, userId);
+ if (storageManagerPckg != null
+ && doesPackageSupportRuntimePermissions(storageManagerPckg)) {
+ grantRuntimePermissions(storageManagerPckg, STORAGE_PERMISSIONS, true, userId);
+ }
+
+ // Companion devices
+ PackageParser.Package companionDeviceDiscoveryPackage = getSystemPackage(
+ CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME);
+ if (companionDeviceDiscoveryPackage != null
+ && doesPackageSupportRuntimePermissions(companionDeviceDiscoveryPackage)) {
+ grantRuntimePermissions(companionDeviceDiscoveryPackage,
+ LOCATION_PERMISSIONS, true, userId);
+ }
+
+ // Ringtone Picker
+ Intent ringtonePickerIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+ PackageParser.Package ringtonePickerPackage =
+ getDefaultSystemHandlerActivityPackage(ringtonePickerIntent, userId);
+ if (ringtonePickerPackage != null
+ && doesPackageSupportRuntimePermissions(ringtonePickerPackage)) {
+ grantRuntimePermissions(ringtonePickerPackage,
+ STORAGE_PERMISSIONS, true, userId);
+ }
+
+ if (mPermissionGrantedCallback != null) {
+ mPermissionGrantedCallback.onDefaultRuntimePermissionsGranted(userId);
+ }
+ }
+
+ private void grantDefaultPermissionsToDefaultSystemDialerApp(
+ PackageParser.Package dialerPackage, int userId) {
+ if (doesPackageSupportRuntimePermissions(dialerPackage)) {
+ boolean isPhonePermFixed =
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
+ grantRuntimePermissions(
+ dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId);
+ grantRuntimePermissions(dialerPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(dialerPackage, SMS_PERMISSIONS, userId);
+ grantRuntimePermissions(dialerPackage, MICROPHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(dialerPackage, CAMERA_PERMISSIONS, userId);
+ }
+ }
+
+ private void grantDefaultPermissionsToDefaultSystemSmsApp(
+ PackageParser.Package smsPackage, int userId) {
+ if (doesPackageSupportRuntimePermissions(smsPackage)) {
+ grantRuntimePermissions(smsPackage, PHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(smsPackage, CONTACTS_PERMISSIONS, userId);
+ grantRuntimePermissions(smsPackage, SMS_PERMISSIONS, userId);
+ grantRuntimePermissions(smsPackage, STORAGE_PERMISSIONS, userId);
+ grantRuntimePermissions(smsPackage, MICROPHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(smsPackage, CAMERA_PERMISSIONS, userId);
+ }
+ }
+
+ public void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId) {
+ Log.i(TAG, "Granting permissions to default sms app for user:" + userId);
+ if (packageName == null) {
+ return;
+ }
+ PackageParser.Package smsPackage = getPackage(packageName);
+ if (smsPackage != null && doesPackageSupportRuntimePermissions(smsPackage)) {
+ grantRuntimePermissions(smsPackage, PHONE_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(smsPackage, CONTACTS_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(smsPackage, SMS_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(smsPackage, STORAGE_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(smsPackage, MICROPHONE_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(smsPackage, CAMERA_PERMISSIONS, false, true, userId);
+ }
+ }
+
+ public void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId) {
+ Log.i(TAG, "Granting permissions to default dialer app for user:" + userId);
+ if (packageName == null) {
+ return;
+ }
+ PackageParser.Package dialerPackage = getPackage(packageName);
+ if (dialerPackage != null
+ && doesPackageSupportRuntimePermissions(dialerPackage)) {
+ grantRuntimePermissions(dialerPackage, PHONE_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(dialerPackage, CONTACTS_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(dialerPackage, SMS_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(dialerPackage, MICROPHONE_PERMISSIONS, false, true, userId);
+ grantRuntimePermissions(dialerPackage, CAMERA_PERMISSIONS, false, true, userId);
+ }
+ }
+
+ private void grantDefaultPermissionsToDefaultSimCallManager(
+ PackageParser.Package simCallManagerPackage, int userId) {
+ Log.i(TAG, "Granting permissions to sim call manager for user:" + userId);
+ if (doesPackageSupportRuntimePermissions(simCallManagerPackage)) {
+ grantRuntimePermissions(simCallManagerPackage, PHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(simCallManagerPackage, MICROPHONE_PERMISSIONS, userId);
+ }
+ }
+
+ public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
+ if (packageName == null) {
+ return;
+ }
+ PackageParser.Package simCallManagerPackage = getPackage(packageName);
+ if (simCallManagerPackage != null) {
+ grantDefaultPermissionsToDefaultSimCallManager(simCallManagerPackage, userId);
+ }
+ }
+
+ public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) {
+ Log.i(TAG, "Granting permissions to enabled carrier apps for user:" + userId);
+ if (packageNames == null) {
+ return;
+ }
+ for (String packageName : packageNames) {
+ PackageParser.Package carrierPackage = getSystemPackage(packageName);
+ if (carrierPackage != null
+ && doesPackageSupportRuntimePermissions(carrierPackage)) {
+ grantRuntimePermissions(carrierPackage, PHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(carrierPackage, LOCATION_PERMISSIONS, userId);
+ grantRuntimePermissions(carrierPackage, SMS_PERMISSIONS, userId);
+ }
+ }
+ }
+
+ public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) {
+ Log.i(TAG, "Granting permissions to enabled ImsServices for user:" + userId);
+ if (packageNames == null) {
+ return;
+ }
+ for (String packageName : packageNames) {
+ PackageParser.Package imsServicePackage = getSystemPackage(packageName);
+ if (imsServicePackage != null
+ && doesPackageSupportRuntimePermissions(imsServicePackage)) {
+ grantRuntimePermissions(imsServicePackage, PHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(imsServicePackage, MICROPHONE_PERMISSIONS, userId);
+ grantRuntimePermissions(imsServicePackage, LOCATION_PERMISSIONS, userId);
+ grantRuntimePermissions(imsServicePackage, CAMERA_PERMISSIONS, userId);
+ }
+ }
+ }
+
+ public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) {
+ Log.i(TAG, "Granting permissions to default browser for user:" + userId);
+ if (packageName == null) {
+ return;
+ }
+ PackageParser.Package browserPackage = getSystemPackage(packageName);
+ if (browserPackage != null
+ && doesPackageSupportRuntimePermissions(browserPackage)) {
+ grantRuntimePermissions(browserPackage, LOCATION_PERMISSIONS, false, false, userId);
+ }
+ }
+
+ private PackageParser.Package getDefaultSystemHandlerActivityPackage(
+ Intent intent, int userId) {
+ ResolveInfo handler = mServiceInternal.resolveIntent(intent,
+ intent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS, userId, false);
+ if (handler == null || handler.activityInfo == null) {
+ return null;
+ }
+ if (mServiceInternal.isResolveActivityComponent(handler.activityInfo)) {
+ return null;
+ }
+ return getSystemPackage(handler.activityInfo.packageName);
+ }
+
+ private PackageParser.Package getDefaultSystemHandlerServicePackage(
+ Intent intent, int userId) {
+ List<ResolveInfo> handlers = mServiceInternal.queryIntentServices(
+ intent, DEFAULT_FLAGS, Binder.getCallingUid(), userId);
+ if (handlers == null) {
+ return null;
+ }
+ final int handlerCount = handlers.size();
+ for (int i = 0; i < handlerCount; i++) {
+ ResolveInfo handler = handlers.get(i);
+ PackageParser.Package handlerPackage = getSystemPackage(
+ handler.serviceInfo.packageName);
+ if (handlerPackage != null) {
+ return handlerPackage;
+ }
+ }
+ return null;
+ }
+
+ private List<PackageParser.Package> getHeadlessSyncAdapterPackages(
+ String[] syncAdapterPackageNames, int userId) {
+ List<PackageParser.Package> syncAdapterPackages = new ArrayList<>();
+
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+ for (String syncAdapterPackageName : syncAdapterPackageNames) {
+ homeIntent.setPackage(syncAdapterPackageName);
+
+ ResolveInfo homeActivity = mServiceInternal.resolveIntent(homeIntent,
+ homeIntent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS,
+ userId, false);
+ if (homeActivity != null) {
+ continue;
+ }
+
+ PackageParser.Package syncAdapterPackage = getSystemPackage(syncAdapterPackageName);
+ if (syncAdapterPackage != null) {
+ syncAdapterPackages.add(syncAdapterPackage);
+ }
+ }
+
+ return syncAdapterPackages;
+ }
+
+ private PackageParser.Package getDefaultProviderAuthorityPackage(
+ String authority, int userId) {
+ ProviderInfo provider =
+ mServiceInternal.resolveContentProvider(authority, DEFAULT_FLAGS, userId);
+ if (provider != null) {
+ return getSystemPackage(provider.packageName);
+ }
+ return null;
+ }
+
+ private PackageParser.Package getPackage(String packageName) {
+ return mServiceInternal.getPackage(packageName);
+ }
+
+ private PackageParser.Package getSystemPackage(String packageName) {
+ PackageParser.Package pkg = getPackage(packageName);
+ if (pkg != null && pkg.isSystemApp()) {
+ return !isSysComponentOrPersistentPlatformSignedPrivApp(pkg) ? pkg : null;
+ }
+ return null;
+ }
+
+ private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
+ int userId) {
+ grantRuntimePermissions(pkg, permissions, false, false, userId);
+ }
+
+ private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
+ boolean systemFixed, int userId) {
+ grantRuntimePermissions(pkg, permissions, systemFixed, false, userId);
+ }
+
+ private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
+ boolean systemFixed, boolean isDefaultPhoneOrSms, int userId) {
+ if (pkg.requestedPermissions.isEmpty()) {
+ return;
+ }
+
+ List<String> requestedPermissions = pkg.requestedPermissions;
+ Set<String> grantablePermissions = null;
+
+ // If this is the default Phone or SMS app we grant permissions regardless
+ // whether the version on the system image declares the permission as used since
+ // selecting the app as the default Phone or SMS the user makes a deliberate
+ // choice to grant this app the permissions needed to function. For all other
+ // apps, (default grants on first boot and user creation) we don't grant default
+ // permissions if the version on the system image does not declare them.
+ if (!isDefaultPhoneOrSms && pkg.isUpdatedSystemApp()) {
+ final PackageParser.Package disabledPkg =
+ mServiceInternal.getDisabledPackage(pkg.packageName);
+ if (disabledPkg != null) {
+ if (disabledPkg.requestedPermissions.isEmpty()) {
+ return;
+ }
+ if (!requestedPermissions.equals(disabledPkg.requestedPermissions)) {
+ grantablePermissions = new ArraySet<>(requestedPermissions);
+ requestedPermissions = disabledPkg.requestedPermissions;
+ }
+ }
+ }
+
+ final int grantablePermissionCount = requestedPermissions.size();
+ for (int i = 0; i < grantablePermissionCount; i++) {
+ String permission = requestedPermissions.get(i);
+
+ // If there is a disabled system app it may request a permission the updated
+ // version ot the data partition doesn't, In this case skip the permission.
+ if (grantablePermissions != null && !grantablePermissions.contains(permission)) {
+ continue;
+ }
+
+ if (permissions.contains(permission)) {
+ final int flags = mServiceInternal.getPermissionFlagsTEMP(
+ permission, pkg.packageName, userId);
+
+ // If any flags are set to the permission, then it is either set in
+ // its current state by the system or device/profile owner or the user.
+ // In all these cases we do not want to clobber the current state.
+ // Unless the caller wants to override user choices. The override is
+ // to make sure we can grant the needed permission to the default
+ // sms and phone apps after the user chooses this in the UI.
+ if (flags == 0 || isDefaultPhoneOrSms) {
+ // Never clobber policy or system.
+ final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
+ | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+ if ((flags & fixedFlags) != 0) {
+ continue;
+ }
+
+ mServiceInternal.grantRuntimePermission(
+ pkg.packageName, permission, userId, false);
+ if (DEBUG) {
+ Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ")
+ + permission + " to default handler " + pkg.packageName);
+ }
+
+ int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+ if (systemFixed) {
+ newFlags |= PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+ }
+
+ mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName,
+ newFlags, newFlags, userId);
+ }
+
+ // If a component gets a permission for being the default handler A
+ // and also default handler B, we grant the weaker grant form.
+ if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0
+ && (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
+ && !systemFixed) {
+ if (DEBUG) {
+ Log.i(TAG, "Granted not fixed " + permission + " to default handler "
+ + pkg.packageName);
+ }
+ mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName,
+ PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, userId);
+ }
+ }
+ }
+ }
+
+ private boolean isSysComponentOrPersistentPlatformSignedPrivApp(PackageParser.Package pkg) {
+ if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
+ return true;
+ }
+ if (!pkg.isPrivilegedApp()) {
+ return false;
+ }
+ final PackageParser.Package disabledPkg =
+ mServiceInternal.getDisabledPackage(pkg.packageName);
+ if (disabledPkg != null) {
+ if ((disabledPkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
+ return false;
+ }
+ } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
+ return false;
+ }
+ final String systemPackageName = mServiceInternal.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
+ final PackageParser.Package systemPackage = getPackage(systemPackageName);
+ return PackageManagerService.compareSignatures(systemPackage.mSignatures,
+ pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
+ }
+
+ private void grantDefaultPermissionExceptions(int userId) {
+ mHandler.removeMessages(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
+
+ synchronized (mLock) {
+ // mGrantExceptions is null only before the first read and then
+ // it serves as a cache of the default grants that should be
+ // performed for every user. If there is an entry then the app
+ // is on the system image and supports runtime permissions.
+ if (mGrantExceptions == null) {
+ mGrantExceptions = readDefaultPermissionExceptionsLocked();
+ }
+ }
+
+ Set<String> permissions = null;
+ final int exceptionCount = mGrantExceptions.size();
+ for (int i = 0; i < exceptionCount; i++) {
+ String packageName = mGrantExceptions.keyAt(i);
+ PackageParser.Package pkg = getSystemPackage(packageName);
+ List<DefaultPermissionGrant> permissionGrants = mGrantExceptions.valueAt(i);
+ final int permissionGrantCount = permissionGrants.size();
+ for (int j = 0; j < permissionGrantCount; j++) {
+ DefaultPermissionGrant permissionGrant = permissionGrants.get(j);
+ if (permissions == null) {
+ permissions = new ArraySet<>();
+ } else {
+ permissions.clear();
+ }
+ permissions.add(permissionGrant.name);
+ grantRuntimePermissions(pkg, permissions,
+ permissionGrant.fixed, userId);
+ }
+ }
+ }
+
+ private File[] getDefaultPermissionFiles() {
+ ArrayList<File> ret = new ArrayList<File>();
+ File dir = new File(Environment.getRootDirectory(), "etc/default-permissions");
+ if (dir.isDirectory() && dir.canRead()) {
+ Collections.addAll(ret, dir.listFiles());
+ }
+ dir = new File(Environment.getVendorDirectory(), "etc/default-permissions");
+ if (dir.isDirectory() && dir.canRead()) {
+ Collections.addAll(ret, dir.listFiles());
+ }
+ return ret.isEmpty() ? null : ret.toArray(new File[0]);
+ }
+
+ private @NonNull ArrayMap<String, List<DefaultPermissionGrant>>
+ readDefaultPermissionExceptionsLocked() {
+ File[] files = getDefaultPermissionFiles();
+ if (files == null) {
+ return new ArrayMap<>(0);
+ }
+
+ ArrayMap<String, List<DefaultPermissionGrant>> grantExceptions = new ArrayMap<>();
+
+ // Iterate over the files in the directory and scan .xml files
+ for (File file : files) {
+ if (!file.getPath().endsWith(".xml")) {
+ Slog.i(TAG, "Non-xml file " + file
+ + " in " + file.getParent() + " directory, ignoring");
+ continue;
+ }
+ if (!file.canRead()) {
+ Slog.w(TAG, "Default permissions file " + file + " cannot be read");
+ continue;
+ }
+ try (
+ InputStream str = new BufferedInputStream(new FileInputStream(file))
+ ) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(str, null);
+ parse(parser, grantExceptions);
+ } catch (XmlPullParserException | IOException e) {
+ Slog.w(TAG, "Error reading default permissions file " + file, e);
+ }
+ }
+
+ return grantExceptions;
+ }
+
+ private void parse(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
+ outGrantExceptions) throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ if (TAG_EXCEPTIONS.equals(parser.getName())) {
+ parseExceptions(parser, outGrantExceptions);
+ } else {
+ Log.e(TAG, "Unknown tag " + parser.getName());
+ }
+ }
+ }
+
+ private void parseExceptions(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
+ outGrantExceptions) throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ if (TAG_EXCEPTION.equals(parser.getName())) {
+ String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+
+ List<DefaultPermissionGrant> packageExceptions =
+ outGrantExceptions.get(packageName);
+ if (packageExceptions == null) {
+ // The package must be on the system image
+ PackageParser.Package pkg = getSystemPackage(packageName);
+ if (pkg == null) {
+ Log.w(TAG, "Unknown package:" + packageName);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+
+ // The package must support runtime permissions
+ if (!doesPackageSupportRuntimePermissions(pkg)) {
+ Log.w(TAG, "Skipping non supporting runtime permissions package:"
+ + packageName);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ packageExceptions = new ArrayList<>();
+ outGrantExceptions.put(packageName, packageExceptions);
+ }
+
+ parsePermission(parser, packageExceptions);
+ } else {
+ Log.e(TAG, "Unknown tag " + parser.getName() + "under <exceptions>");
+ }
+ }
+ }
+
+ private void parsePermission(XmlPullParser parser, List<DefaultPermissionGrant>
+ outPackageExceptions) throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ if (TAG_PERMISSION.contains(parser.getName())) {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ if (name == null) {
+ Log.w(TAG, "Mandatory name attribute missing for permission tag");
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+
+ final boolean fixed = XmlUtils.readBooleanAttribute(parser, ATTR_FIXED);
+
+ DefaultPermissionGrant exception = new DefaultPermissionGrant(name, fixed);
+ outPackageExceptions.add(exception);
+ } else {
+ Log.e(TAG, "Unknown tag " + parser.getName() + "under <exception>");
+ }
+ }
+ }
+
+ private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) {
+ return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
+ }
+
+ private static final class DefaultPermissionGrant {
+ final String name;
+ final boolean fixed;
+
+ public DefaultPermissionGrant(String name, boolean fixed) {
+ this.name = name;
+ this.fixed = fixed;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
new file mode 100644
index 0000000..3b20b42
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017 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.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
+import android.content.pm.PermissionInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManager.PermissionInfoFlags;
+import android.content.pm.PackageParser.Permission;
+
+import com.android.server.pm.SharedUserSetting;
+import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Internal interfaces to be used by other components within the system server.
+ */
+public abstract class PermissionManagerInternal {
+ /**
+ * Callbacks invoked when interesting actions have been taken on a permission.
+ * <p>
+ * NOTE: The current arguments are merely to support the existing use cases. This
+ * needs to be properly thought out with appropriate arguments for each of the
+ * callback methods.
+ */
+ public static class PermissionCallback {
+ public void onGidsChanged(int appId, int userId) {
+ }
+ public void onPermissionChanged() {
+ }
+ public void onPermissionGranted(int uid, int userId) {
+ }
+ public void onInstallPermissionGranted() {
+ }
+ public void onPermissionRevoked(int uid, int userId) {
+ }
+ public void onInstallPermissionRevoked() {
+ }
+ public void onPermissionUpdated(int userId) {
+ }
+ public void onPermissionRemoved() {
+ }
+ public void onInstallPermissionUpdated() {
+ }
+ }
+
+ public abstract void grantRuntimePermission(
+ @NonNull String permName, @NonNull String packageName, boolean overridePolicy,
+ int callingUid, int userId, @Nullable PermissionCallback callback);
+ public abstract void grantRuntimePermissionsGrantedToDisabledPackage(
+ @NonNull PackageParser.Package pkg, int callingUid,
+ @Nullable PermissionCallback callback);
+ public abstract void grantRequestedRuntimePermissions(
+ @NonNull PackageParser.Package pkg, @NonNull int[] userIds,
+ @NonNull String[] grantedPermissions, int callingUid,
+ @Nullable PermissionCallback callback);
+ public abstract void revokeRuntimePermission(@NonNull String permName,
+ @NonNull String packageName, boolean overridePolicy, int callingUid, int userId,
+ @Nullable PermissionCallback callback);
+ public abstract int[] revokeUnusedSharedUserPermissions(@NonNull SharedUserSetting suSetting,
+ @NonNull int[] allUserIds);
+
+
+ public abstract boolean addPermission(@NonNull PermissionInfo info, boolean async,
+ int callingUid, @Nullable PermissionCallback callback);
+ public abstract void removePermission(@NonNull String permName, int callingUid,
+ @Nullable PermissionCallback callback);
+
+ public abstract int getPermissionFlags(@NonNull String permName,
+ @NonNull String packageName, int callingUid, int userId);
+ /**
+ * Retrieve all of the information we know about a particular permission.
+ */
+ public abstract @Nullable PermissionInfo getPermissionInfo(@NonNull String permName,
+ @NonNull String packageName, @PermissionInfoFlags int flags, int callingUid);
+ /**
+ * Retrieve all of the permissions associated with a particular group.
+ */
+ public abstract @Nullable List<PermissionInfo> getPermissionInfoByGroup(@NonNull String group,
+ @PermissionInfoFlags int flags, int callingUid);
+ public abstract boolean isPermissionAppOp(@NonNull String permName);
+ public abstract boolean isPermissionInstant(@NonNull String permName);
+
+ /**
+ * Updates the flags associated with a permission by replacing the flags in
+ * the specified mask with the provided flag values.
+ */
+ public abstract void updatePermissionFlags(@NonNull String permName,
+ @NonNull String packageName, int flagMask, int flagValues, int callingUid, int userId,
+ @Nullable PermissionCallback callback);
+ /**
+ * Updates the flags for all applications by replacing the flags in the specified mask
+ * with the provided flag values.
+ */
+ public abstract boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues,
+ int callingUid, int userId, @NonNull Collection<PackageParser.Package> packages,
+ @Nullable PermissionCallback callback);
+
+ public abstract int checkPermission(@NonNull String permName, @NonNull String packageName,
+ int callingUid, int userId);
+
+ /**
+ * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
+ * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userid} is not for the caller.
+ * @param checkShell whether to prevent shell from access if there's a debugging restriction
+ * @param message the message to log on security exception
+ */
+ public abstract void enforceCrossUserPermission(int callingUid, int userId,
+ boolean requireFullPermission, boolean checkShell, @NonNull String message);
+ public abstract void enforceGrantRevokeRuntimePermissionPermissions(@NonNull String message);
+
+ public abstract @NonNull PermissionSettings getPermissionSettings();
+ public abstract @NonNull DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy();
+
+ /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
+ public abstract Iterator<BasePermission> getPermissionIteratorTEMP();
+ public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName);
+ public abstract void putPermissionTEMP(@NonNull String permName,
+ @NonNull BasePermission permission);
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
new file mode 100644
index 0000000..6c031a6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (C) 2017 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.pm.permission;
+
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionInfo;
+import android.content.pm.PackageParser.Package;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.UserManagerInternal;
+import android.os.storage.StorageManagerInternal;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
+import com.android.server.SystemConfig;
+import com.android.server.Watchdog;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.PackageManagerServiceUtils;
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.ProcessLoggingHandler;
+import com.android.server.pm.SharedUserSetting;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
+import com.android.server.pm.permission.PermissionsState.PermissionState;
+
+import libcore.util.EmptyArray;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Manages all permissions and handles permissions related tasks.
+ */
+public class PermissionManagerService {
+ private static final String TAG = "PackageManager";
+
+ /** All dangerous permission names in the same order as the events in MetricsEvent */
+ private static final List<String> ALL_DANGEROUS_PERMISSIONS = Arrays.asList(
+ Manifest.permission.READ_CALENDAR,
+ Manifest.permission.WRITE_CALENDAR,
+ Manifest.permission.CAMERA,
+ Manifest.permission.READ_CONTACTS,
+ Manifest.permission.WRITE_CONTACTS,
+ Manifest.permission.GET_ACCOUNTS,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.RECORD_AUDIO,
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.CALL_PHONE,
+ Manifest.permission.READ_CALL_LOG,
+ Manifest.permission.WRITE_CALL_LOG,
+ Manifest.permission.ADD_VOICEMAIL,
+ Manifest.permission.USE_SIP,
+ Manifest.permission.PROCESS_OUTGOING_CALLS,
+ Manifest.permission.READ_CELL_BROADCASTS,
+ Manifest.permission.BODY_SENSORS,
+ Manifest.permission.SEND_SMS,
+ Manifest.permission.RECEIVE_SMS,
+ Manifest.permission.READ_SMS,
+ Manifest.permission.RECEIVE_WAP_PUSH,
+ Manifest.permission.RECEIVE_MMS,
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.READ_PHONE_NUMBERS,
+ Manifest.permission.ANSWER_PHONE_CALLS);
+
+ /** Cap the size of permission trees that 3rd party apps can define */
+ private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768; // characters of text
+
+ /** Lock to protect internal data access */
+ private final Object mLock;
+
+ /** Internal connection to the package manager */
+ private final PackageManagerInternal mPackageManagerInt;
+
+ /** Internal connection to the user manager */
+ private final UserManagerInternal mUserManagerInt;
+
+ /** Default permission policy to provide proper behaviour out-of-the-box */
+ private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
+
+ /** Internal storage for permissions and related settings */
+ private final PermissionSettings mSettings;
+
+ private final HandlerThread mHandlerThread;
+ private final Handler mHandler;
+ private final Context mContext;
+
+ PermissionManagerService(Context context,
+ @Nullable DefaultPermissionGrantedCallback defaultGrantCallback,
+ @NonNull Object externalLock) {
+ mContext = context;
+ mLock = externalLock;
+ mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
+ mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
+ mSettings = new PermissionSettings(context, mLock);
+
+ mHandlerThread = new ServiceThread(TAG,
+ Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ Watchdog.getInstance().addThread(mHandler);
+
+ mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy(
+ context, mHandlerThread.getLooper(), defaultGrantCallback, this);
+
+ // propagate permission configuration
+ final ArrayMap<String, SystemConfig.PermissionEntry> permConfig =
+ SystemConfig.getInstance().getPermissions();
+ synchronized (mLock) {
+ for (int i=0; i<permConfig.size(); i++) {
+ final SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
+ BasePermission bp = mSettings.getPermissionLocked(perm.name);
+ if (bp == null) {
+ bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
+ mSettings.putPermissionLocked(perm.name, bp);
+ }
+ if (perm.gids != null) {
+ bp.setGids(perm.gids, perm.perUser);
+ }
+ }
+ }
+
+ LocalServices.addService(
+ PermissionManagerInternal.class, new PermissionManagerInternalImpl());
+ }
+
+ /**
+ * Creates and returns an initialized, internal service for use by other components.
+ * <p>
+ * The object returned is identical to the one returned by the LocalServices class using:
+ * {@code LocalServices.getService(PermissionManagerInternal.class);}
+ * <p>
+ * NOTE: The external lock is temporary and should be removed. This needs to be a
+ * lock created by the permission manager itself.
+ */
+ public static PermissionManagerInternal create(Context context,
+ @Nullable DefaultPermissionGrantedCallback defaultGrantCallback,
+ @NonNull Object externalLock) {
+ final PermissionManagerInternal permMgrInt =
+ LocalServices.getService(PermissionManagerInternal.class);
+ if (permMgrInt != null) {
+ return permMgrInt;
+ }
+ new PermissionManagerService(context, defaultGrantCallback, externalLock);
+ return LocalServices.getService(PermissionManagerInternal.class);
+ }
+
+ @Nullable BasePermission getPermission(String permName) {
+ synchronized (mLock) {
+ return mSettings.getPermissionLocked(permName);
+ }
+ }
+
+ private int checkPermission(String permName, String pkgName, int callingUid, int userId) {
+ if (!mUserManagerInt.exists(userId)) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(pkgName);
+ if (pkg != null && pkg.mExtras != null) {
+ if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ final boolean instantApp = ps.getInstantApp(userId);
+ final PermissionsState permissionsState = ps.getPermissionsState();
+ if (permissionsState.hasPermission(permName, userId)) {
+ if (instantApp) {
+ synchronized (mLock) {
+ BasePermission bp = mSettings.getPermissionLocked(permName);
+ if (bp != null && bp.isInstant()) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ }
+ } else {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ }
+ // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
+ if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
+ .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ }
+
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ private PermissionInfo getPermissionInfo(String name, String packageName, int flags,
+ int callingUid) {
+ if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+ return null;
+ }
+ // reader
+ synchronized (mLock) {
+ final BasePermission bp = mSettings.getPermissionLocked(name);
+ if (bp == null) {
+ return null;
+ }
+ final int adjustedProtectionLevel = adjustPermissionProtectionFlagsLocked(
+ bp.getProtectionLevel(), packageName, callingUid);
+ return bp.generatePermissionInfo(adjustedProtectionLevel, flags);
+ }
+ }
+
+ private List<PermissionInfo> getPermissionInfoByGroup(
+ String groupName, int flags, int callingUid) {
+ if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+ return null;
+ }
+ // reader
+ synchronized (mLock) {
+ // TODO Uncomment when mPermissionGroups moves to this class
+// if (groupName != null && !mPermissionGroups.containsKey(groupName)) {
+// // This is thrown as NameNotFoundException
+// return null;
+// }
+
+ final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
+ for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
+ final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags);
+ if (pi != null) {
+ out.add(pi);
+ }
+ }
+ return out;
+ }
+ }
+
+ private int adjustPermissionProtectionFlagsLocked(
+ int protectionLevel, String packageName, int uid) {
+ // Signature permission flags area always reported
+ final int protectionLevelMasked = protectionLevel
+ & (PermissionInfo.PROTECTION_NORMAL
+ | PermissionInfo.PROTECTION_DANGEROUS
+ | PermissionInfo.PROTECTION_SIGNATURE);
+ if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) {
+ return protectionLevel;
+ }
+ // System sees all flags.
+ final int appId = UserHandle.getAppId(uid);
+ if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID
+ || appId == Process.SHELL_UID) {
+ return protectionLevel;
+ }
+ // Normalize package name to handle renamed packages and static libs
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+ if (pkg == null) {
+ return protectionLevel;
+ }
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+ return protectionLevelMasked;
+ }
+ // Apps that target O see flags for all protection levels.
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null) {
+ return protectionLevel;
+ }
+ if (ps.getAppId() != appId) {
+ return protectionLevel;
+ }
+ return protectionLevel;
+ }
+
+ private boolean addPermission(
+ PermissionInfo info, int callingUid, PermissionCallback callback) {
+ if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+ throw new SecurityException("Instant apps can't add permissions");
+ }
+ if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
+ throw new SecurityException("Label must be specified in permission");
+ }
+ final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP(
+ info.name, callingUid);
+ final boolean added;
+ final boolean changed;
+ synchronized (mLock) {
+ BasePermission bp = mSettings.getPermissionLocked(info.name);
+ added = bp == null;
+ int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
+ if (added) {
+ enforcePermissionCapLocked(info, tree);
+ bp = new BasePermission(info.name, tree.getSourcePackageName(),
+ BasePermission.TYPE_DYNAMIC);
+ } else if (bp.isDynamic()) {
+ throw new SecurityException(
+ "Not allowed to modify non-dynamic permission "
+ + info.name);
+ }
+ changed = bp.addToTree(fixedLevel, info, tree);
+ if (added) {
+ mSettings.putPermissionLocked(info.name, bp);
+ }
+ }
+ if (changed && callback != null) {
+ callback.onPermissionChanged();
+ }
+ return added;
+ }
+
+ private void removePermission(
+ String permName, int callingUid, PermissionCallback callback) {
+ if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+ throw new SecurityException("Instant applications don't have access to this method");
+ }
+ final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP(
+ permName, callingUid);
+ synchronized (mLock) {
+ final BasePermission bp = mSettings.getPermissionLocked(permName);
+ if (bp == null) {
+ return;
+ }
+ if (bp.isDynamic()) {
+ throw new SecurityException(
+ "Not allowed to modify non-dynamic permission "
+ + permName);
+ }
+ mSettings.removePermissionLocked(permName);
+ if (callback != null) {
+ callback.onPermissionRemoved();
+ }
+ }
+ }
+
+ private void grantRuntimePermissionsGrantedToDisabledPackageLocked(
+ PackageParser.Package pkg, int callingUid, PermissionCallback callback) {
+ if (pkg.parentPackage == null) {
+ return;
+ }
+ if (pkg.requestedPermissions == null) {
+ return;
+ }
+ final PackageParser.Package disabledPkg =
+ mPackageManagerInt.getDisabledPackage(pkg.parentPackage.packageName);
+ if (disabledPkg == null || disabledPkg.mExtras == null) {
+ return;
+ }
+ final PackageSetting disabledPs = (PackageSetting) disabledPkg.mExtras;
+ if (!disabledPs.isPrivileged() || disabledPs.hasChildPackages()) {
+ return;
+ }
+ final int permCount = pkg.requestedPermissions.size();
+ for (int i = 0; i < permCount; i++) {
+ String permission = pkg.requestedPermissions.get(i);
+ BasePermission bp = mSettings.getPermissionLocked(permission);
+ if (bp == null || !(bp.isRuntime() || bp.isDevelopment())) {
+ continue;
+ }
+ for (int userId : mUserManagerInt.getUserIds()) {
+ if (disabledPs.getPermissionsState().hasRuntimePermission(permission, userId)) {
+ grantRuntimePermission(
+ permission, pkg.packageName, false, callingUid, userId, callback);
+ }
+ }
+ }
+ }
+
+ private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
+ String[] grantedPermissions, int callingUid, PermissionCallback callback) {
+ for (int userId : userIds) {
+ grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions, callingUid,
+ callback);
+ }
+ }
+
+ private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId,
+ String[] grantedPermissions, int callingUid, PermissionCallback callback) {
+ PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null) {
+ return;
+ }
+
+ PermissionsState permissionsState = ps.getPermissionsState();
+
+ final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
+ | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+
+ final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
+ >= Build.VERSION_CODES.M;
+
+ final boolean instantApp = mPackageManagerInt.isInstantApp(pkg.packageName, userId);
+
+ for (String permission : pkg.requestedPermissions) {
+ final BasePermission bp;
+ synchronized (mLock) {
+ bp = mSettings.getPermissionLocked(permission);
+ }
+ if (bp != null && (bp.isRuntime() || bp.isDevelopment())
+ && (!instantApp || bp.isInstant())
+ && (supportsRuntimePermissions || !bp.isRuntimeOnly())
+ && (grantedPermissions == null
+ || ArrayUtils.contains(grantedPermissions, permission))) {
+ final int flags = permissionsState.getPermissionFlags(permission, userId);
+ if (supportsRuntimePermissions) {
+ // Installer cannot change immutable permissions.
+ if ((flags & immutableFlags) == 0) {
+ grantRuntimePermission(permission, pkg.packageName, false, callingUid,
+ userId, callback);
+ }
+ } else if (mSettings.mPermissionReviewRequired) {
+ // In permission review mode we clear the review flag when we
+ // are asked to install the app with all permissions granted.
+ if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ updatePermissionFlags(permission, pkg.packageName,
+ PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, callingUid,
+ userId, callback);
+ }
+ }
+ }
+ }
+ }
+
+ private void grantRuntimePermission(String permName, String packageName, boolean overridePolicy,
+ int callingUid, final int userId, PermissionCallback callback) {
+ if (!mUserManagerInt.exists(userId)) {
+ Log.e(TAG, "No such user:" + userId);
+ return;
+ }
+
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ "grantRuntimePermission");
+
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, true /* checkShell */,
+ "grantRuntimePermission");
+
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+ if (pkg == null || pkg.mExtras == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ final BasePermission bp;
+ synchronized(mLock) {
+ bp = mSettings.getPermissionLocked(permName);
+ }
+ if (bp == null) {
+ throw new IllegalArgumentException("Unknown permission: " + permName);
+ }
+ if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+
+ bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);
+
+ // If a permission review is required for legacy apps we represent
+ // their permissions as always granted runtime ones since we need
+ // to keep the review required permission flag per user while an
+ // install permission's state is shared across all users.
+ if (mSettings.mPermissionReviewRequired
+ && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+ && bp.isRuntime()) {
+ return;
+ }
+
+ final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
+
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ final PermissionsState permissionsState = ps.getPermissionsState();
+
+ final int flags = permissionsState.getPermissionFlags(permName, userId);
+ if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
+ throw new SecurityException("Cannot grant system fixed permission "
+ + permName + " for package " + packageName);
+ }
+ if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+ throw new SecurityException("Cannot grant policy fixed permission "
+ + permName + " for package " + packageName);
+ }
+
+ if (bp.isDevelopment()) {
+ // Development permissions must be handled specially, since they are not
+ // normal runtime permissions. For now they apply to all users.
+ if (permissionsState.grantInstallPermission(bp) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ if (callback != null) {
+ callback.onInstallPermissionGranted();
+ }
+ }
+ return;
+ }
+
+ if (ps.getInstantApp(userId) && !bp.isInstant()) {
+ throw new SecurityException("Cannot grant non-ephemeral permission"
+ + permName + " for package " + packageName);
+ }
+
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+ Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
+ return;
+ }
+
+ final int result = permissionsState.grantRuntimePermission(bp, userId);
+ switch (result) {
+ case PermissionsState.PERMISSION_OPERATION_FAILURE: {
+ return;
+ }
+
+ case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
+ if (callback != null) {
+ callback.onGidsChanged(UserHandle.getAppId(pkg.applicationInfo.uid), userId);
+ }
+ }
+ break;
+ }
+
+ if (bp.isRuntime()) {
+ logPermissionGranted(mContext, permName, packageName);
+ }
+
+ if (callback != null) {
+ callback.onPermissionGranted(uid, userId);
+ }
+
+ // Only need to do this if user is initialized. Otherwise it's a new user
+ // and there are no processes running as the user yet and there's no need
+ // to make an expensive call to remount processes for the changed permissions.
+ if (READ_EXTERNAL_STORAGE.equals(permName)
+ || WRITE_EXTERNAL_STORAGE.equals(permName)) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (mUserManagerInt.isUserInitialized(userId)) {
+ StorageManagerInternal storageManagerInternal = LocalServices.getService(
+ StorageManagerInternal.class);
+ storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ }
+
+ private void revokeRuntimePermission(String permName, String packageName,
+ boolean overridePolicy, int callingUid, int userId, PermissionCallback callback) {
+ if (!mUserManagerInt.exists(userId)) {
+ Log.e(TAG, "No such user:" + userId);
+ return;
+ }
+
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+ "revokeRuntimePermission");
+
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ true /* requireFullPermission */, true /* checkShell */,
+ "revokeRuntimePermission");
+
+ final int appId;
+
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+ if (pkg == null || pkg.mExtras == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ if (mPackageManagerInt.filterAppAccess(pkg, Binder.getCallingUid(), userId)) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ final BasePermission bp = mSettings.getPermissionLocked(permName);
+ if (bp == null) {
+ throw new IllegalArgumentException("Unknown permission: " + permName);
+ }
+
+ bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);
+
+ // If a permission review is required for legacy apps we represent
+ // their permissions as always granted runtime ones since we need
+ // to keep the review required permission flag per user while an
+ // install permission's state is shared across all users.
+ if (mSettings.mPermissionReviewRequired
+ && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+ && bp.isRuntime()) {
+ return;
+ }
+
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ final PermissionsState permissionsState = ps.getPermissionsState();
+
+ final int flags = permissionsState.getPermissionFlags(permName, userId);
+ if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
+ throw new SecurityException("Cannot revoke system fixed permission "
+ + permName + " for package " + packageName);
+ }
+ if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+ throw new SecurityException("Cannot revoke policy fixed permission "
+ + permName + " for package " + packageName);
+ }
+
+ if (bp.isDevelopment()) {
+ // Development permissions must be handled specially, since they are not
+ // normal runtime permissions. For now they apply to all users.
+ if (permissionsState.revokeInstallPermission(bp) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ if (callback != null) {
+ callback.onInstallPermissionRevoked();
+ }
+ }
+ return;
+ }
+
+ if (permissionsState.revokeRuntimePermission(bp, userId) ==
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ return;
+ }
+
+ if (bp.isRuntime()) {
+ logPermissionRevoked(mContext, permName, packageName);
+ }
+
+ if (callback != null) {
+ final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
+ callback.onPermissionRevoked(pkg.applicationInfo.uid, userId);
+ }
+ }
+
+ private int[] revokeUnusedSharedUserPermissions(SharedUserSetting suSetting, int[] allUserIds) {
+ // Collect all used permissions in the UID
+ final ArraySet<String> usedPermissions = new ArraySet<>();
+ final List<PackageParser.Package> pkgList = suSetting.getPackages();
+ if (pkgList == null || pkgList.size() == 0) {
+ return EmptyArray.INT;
+ }
+ for (PackageParser.Package pkg : pkgList) {
+ final int requestedPermCount = pkg.requestedPermissions.size();
+ for (int j = 0; j < requestedPermCount; j++) {
+ String permission = pkg.requestedPermissions.get(j);
+ BasePermission bp = mSettings.getPermissionLocked(permission);
+ if (bp != null) {
+ usedPermissions.add(permission);
+ }
+ }
+ }
+
+ PermissionsState permissionsState = suSetting.getPermissionsState();
+ // Prune install permissions
+ List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates();
+ final int installPermCount = installPermStates.size();
+ for (int i = installPermCount - 1; i >= 0; i--) {
+ PermissionState permissionState = installPermStates.get(i);
+ if (!usedPermissions.contains(permissionState.getName())) {
+ BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
+ if (bp != null) {
+ permissionsState.revokeInstallPermission(bp);
+ permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
+ PackageManager.MASK_PERMISSION_FLAGS, 0);
+ }
+ }
+ }
+
+ int[] runtimePermissionChangedUserIds = EmptyArray.INT;
+
+ // Prune runtime permissions
+ for (int userId : allUserIds) {
+ List<PermissionState> runtimePermStates = permissionsState
+ .getRuntimePermissionStates(userId);
+ final int runtimePermCount = runtimePermStates.size();
+ for (int i = runtimePermCount - 1; i >= 0; i--) {
+ PermissionState permissionState = runtimePermStates.get(i);
+ if (!usedPermissions.contains(permissionState.getName())) {
+ BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
+ if (bp != null) {
+ permissionsState.revokeRuntimePermission(bp, userId);
+ permissionsState.updatePermissionFlags(bp, userId,
+ PackageManager.MASK_PERMISSION_FLAGS, 0);
+ runtimePermissionChangedUserIds = ArrayUtils.appendInt(
+ runtimePermissionChangedUserIds, userId);
+ }
+ }
+ }
+ }
+
+ return runtimePermissionChangedUserIds;
+ }
+
+ private int getPermissionFlags(String permName, String packageName, int callingUid, int userId) {
+ if (!mUserManagerInt.exists(userId)) {
+ return 0;
+ }
+
+ enforceGrantRevokeRuntimePermissionPermissions("getPermissionFlags");
+
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, false /* checkShell */,
+ "getPermissionFlags");
+
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+ if (pkg == null || pkg.mExtras == null) {
+ return 0;
+ }
+ synchronized (mLock) {
+ if (mSettings.getPermissionLocked(permName) == null) {
+ return 0;
+ }
+ }
+ if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+ return 0;
+ }
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ PermissionsState permissionsState = ps.getPermissionsState();
+ return permissionsState.getPermissionFlags(permName, userId);
+ }
+
+ private void updatePermissionFlags(String permName, String packageName, int flagMask,
+ int flagValues, int callingUid, int userId, PermissionCallback callback) {
+ if (!mUserManagerInt.exists(userId)) {
+ return;
+ }
+
+ enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlags");
+
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, true /* checkShell */,
+ "updatePermissionFlags");
+
+ // Only the system can change these flags and nothing else.
+ if (callingUid != Process.SYSTEM_UID) {
+ flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+ flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+ flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+ flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+ flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+ }
+
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+ if (pkg == null || pkg.mExtras == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+
+ final BasePermission bp;
+ synchronized (mLock) {
+ bp = mSettings.getPermissionLocked(permName);
+ }
+ if (bp == null) {
+ throw new IllegalArgumentException("Unknown permission: " + permName);
+ }
+
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ final PermissionsState permissionsState = ps.getPermissionsState();
+ final boolean hadState =
+ permissionsState.getRuntimePermissionState(permName, userId) != null;
+ final boolean permissionUpdated =
+ permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues);
+ if (permissionUpdated && callback != null) {
+ // Install and runtime permissions are stored in different places,
+ // so figure out what permission changed and persist the change.
+ if (permissionsState.getInstallPermissionState(permName) != null) {
+ callback.onInstallPermissionUpdated();
+ } else if (permissionsState.getRuntimePermissionState(permName, userId) != null
+ || hadState) {
+ callback.onPermissionUpdated(userId);
+ }
+ }
+ }
+
+ private boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, int callingUid,
+ int userId, Collection<Package> packages, PermissionCallback callback) {
+ if (!mUserManagerInt.exists(userId)) {
+ return false;
+ }
+
+ enforceGrantRevokeRuntimePermissionPermissions(
+ "updatePermissionFlagsForAllApps");
+ enforceCrossUserPermission(callingUid, userId,
+ true /* requireFullPermission */, true /* checkShell */,
+ "updatePermissionFlagsForAllApps");
+
+ // Only the system can change system fixed flags.
+ if (callingUid != Process.SYSTEM_UID) {
+ flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+ flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+ }
+
+ boolean changed = false;
+ for (PackageParser.Package pkg : packages) {
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null) {
+ continue;
+ }
+ PermissionsState permissionsState = ps.getPermissionsState();
+ changed |= permissionsState.updatePermissionFlagsForAllPermissions(
+ userId, flagMask, flagValues);
+ }
+ return changed;
+ }
+
+ private void enforceGrantRevokeRuntimePermissionPermissions(String message) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
+ != PackageManager.PERMISSION_GRANTED
+ && mContext.checkCallingOrSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(message + " requires "
+ + Manifest.permission.GRANT_RUNTIME_PERMISSIONS + " or "
+ + Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
+ }
+ }
+
+ /**
+ * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS
+ * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller.
+ * @param checkShell whether to prevent shell from access if there's a debugging restriction
+ * @param message the message to log on security exception
+ */
+ private void enforceCrossUserPermission(int callingUid, int userId,
+ boolean requireFullPermission, boolean checkShell, String message) {
+ if (userId < 0) {
+ throw new IllegalArgumentException("Invalid userId " + userId);
+ }
+ if (checkShell) {
+ PackageManagerServiceUtils.enforceShellRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+ }
+ if (userId == UserHandle.getUserId(callingUid)) return;
+ if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
+ if (requireFullPermission) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+ } else {
+ try {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+ } catch (SecurityException se) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS, message);
+ }
+ }
+ }
+ }
+
+ private int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
+ int size = 0;
+ for (BasePermission perm : mSettings.getAllPermissionsLocked()) {
+ size += tree.calculateFootprint(perm);
+ }
+ return size;
+ }
+
+ private void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) {
+ // We calculate the max size of permissions defined by this uid and throw
+ // if that plus the size of 'info' would exceed our stated maximum.
+ if (tree.getUid() != Process.SYSTEM_UID) {
+ final int curTreeSize = calculateCurrentPermissionFootprintLocked(tree);
+ if (curTreeSize + info.calculateFootprint() > MAX_PERMISSION_TREE_FOOTPRINT) {
+ throw new SecurityException("Permission tree size cap exceeded");
+ }
+ }
+ }
+
+ /**
+ * Get the first event id for the permission.
+ *
+ * <p>There are four events for each permission: <ul>
+ * <li>Request permission: first id + 0</li>
+ * <li>Grant permission: first id + 1</li>
+ * <li>Request for permission denied: first id + 2</li>
+ * <li>Revoke permission: first id + 3</li>
+ * </ul></p>
+ *
+ * @param name name of the permission
+ *
+ * @return The first event id for the permission
+ */
+ private static int getBaseEventId(@NonNull String name) {
+ int eventIdIndex = ALL_DANGEROUS_PERMISSIONS.indexOf(name);
+
+ if (eventIdIndex == -1) {
+ if (AppOpsManager.permissionToOpCode(name) == AppOpsManager.OP_NONE
+ || Build.IS_USER) {
+ Log.i(TAG, "Unknown permission " + name);
+
+ return MetricsEvent.ACTION_PERMISSION_REQUEST_UNKNOWN;
+ } else {
+ // Most likely #ALL_DANGEROUS_PERMISSIONS needs to be updated.
+ //
+ // Also update
+ // - EventLogger#ALL_DANGEROUS_PERMISSIONS
+ // - metrics_constants.proto
+ throw new IllegalStateException("Unknown permission " + name);
+ }
+ }
+
+ return MetricsEvent.ACTION_PERMISSION_REQUEST_READ_CALENDAR + eventIdIndex * 4;
+ }
+
+ /**
+ * Log that a permission was revoked.
+ *
+ * @param context Context of the caller
+ * @param name name of the permission
+ * @param packageName package permission if for
+ */
+ private static void logPermissionRevoked(@NonNull Context context, @NonNull String name,
+ @NonNull String packageName) {
+ MetricsLogger.action(context, getBaseEventId(name) + 3, packageName);
+ }
+
+ /**
+ * Log that a permission request was granted.
+ *
+ * @param context Context of the caller
+ * @param name name of the permission
+ * @param packageName package permission if for
+ */
+ private static void logPermissionGranted(@NonNull Context context, @NonNull String name,
+ @NonNull String packageName) {
+ MetricsLogger.action(context, getBaseEventId(name) + 1, packageName);
+ }
+
+ private class PermissionManagerInternalImpl extends PermissionManagerInternal {
+ @Override
+ public boolean addPermission(PermissionInfo info, boolean async, int callingUid,
+ PermissionCallback callback) {
+ return PermissionManagerService.this.addPermission(info, callingUid, callback);
+ }
+ @Override
+ public void removePermission(String permName, int callingUid,
+ PermissionCallback callback) {
+ PermissionManagerService.this.removePermission(permName, callingUid, callback);
+ }
+ @Override
+ public void grantRuntimePermission(String permName, String packageName,
+ boolean overridePolicy, int callingUid, int userId,
+ PermissionCallback callback) {
+ PermissionManagerService.this.grantRuntimePermission(
+ permName, packageName, overridePolicy, callingUid, userId, callback);
+ }
+ @Override
+ public void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
+ String[] grantedPermissions, int callingUid, PermissionCallback callback) {
+ PermissionManagerService.this.grantRequestedRuntimePermissions(
+ pkg, userIds, grantedPermissions, callingUid, callback);
+ }
+ @Override
+ public void grantRuntimePermissionsGrantedToDisabledPackage(PackageParser.Package pkg,
+ int callingUid, PermissionCallback callback) {
+ PermissionManagerService.this.grantRuntimePermissionsGrantedToDisabledPackageLocked(
+ pkg, callingUid, callback);
+ }
+ @Override
+ public void revokeRuntimePermission(String permName, String packageName,
+ boolean overridePolicy, int callingUid, int userId,
+ PermissionCallback callback) {
+ PermissionManagerService.this.revokeRuntimePermission(permName, packageName,
+ overridePolicy, callingUid, userId, callback);
+ }
+ @Override
+ public int[] revokeUnusedSharedUserPermissions(SharedUserSetting suSetting,
+ int[] allUserIds) {
+ return PermissionManagerService.this.revokeUnusedSharedUserPermissions(
+ (SharedUserSetting) suSetting, allUserIds);
+ }
+ @Override
+ public int getPermissionFlags(String permName, String packageName, int callingUid,
+ int userId) {
+ return PermissionManagerService.this.getPermissionFlags(permName, packageName,
+ callingUid, userId);
+ }
+ @Override
+ public void updatePermissionFlags(String permName, String packageName, int flagMask,
+ int flagValues, int callingUid, int userId, PermissionCallback callback) {
+ PermissionManagerService.this.updatePermissionFlags(
+ permName, packageName, flagMask, flagValues, callingUid, userId, callback);
+ }
+ @Override
+ public boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, int callingUid,
+ int userId, Collection<Package> packages, PermissionCallback callback) {
+ return PermissionManagerService.this.updatePermissionFlagsForAllApps(
+ flagMask, flagValues, callingUid, userId, packages, callback);
+ }
+ @Override
+ public void enforceCrossUserPermission(int callingUid, int userId,
+ boolean requireFullPermission, boolean checkShell, String message) {
+ PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId,
+ requireFullPermission, checkShell, message);
+ }
+ @Override
+ public void enforceGrantRevokeRuntimePermissionPermissions(String message) {
+ PermissionManagerService.this.enforceGrantRevokeRuntimePermissionPermissions(message);
+ }
+ @Override
+ public int checkPermission(String permName, String packageName, int callingUid,
+ int userId) {
+ return PermissionManagerService.this.checkPermission(
+ permName, packageName, callingUid, userId);
+ }
+ @Override
+ public PermissionInfo getPermissionInfo(String permName, String packageName, int flags,
+ int callingUid) {
+ return PermissionManagerService.this.getPermissionInfo(
+ permName, packageName, flags, callingUid);
+ }
+ @Override
+ public List<PermissionInfo> getPermissionInfoByGroup(String group, int flags,
+ int callingUid) {
+ return PermissionManagerService.this.getPermissionInfoByGroup(group, flags, callingUid);
+ }
+ @Override
+ public boolean isPermissionInstant(String permName) {
+ synchronized (PermissionManagerService.this.mLock) {
+ final BasePermission bp = mSettings.getPermissionLocked(permName);
+ return (bp != null && bp.isInstant());
+ }
+ }
+ @Override
+ public boolean isPermissionAppOp(String permName) {
+ synchronized (PermissionManagerService.this.mLock) {
+ final BasePermission bp = mSettings.getPermissionLocked(permName);
+ return (bp != null && bp.isAppOp());
+ }
+ }
+ @Override
+ public PermissionSettings getPermissionSettings() {
+ return mSettings;
+ }
+ @Override
+ public DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() {
+ return mDefaultPermissionGrantPolicy;
+ }
+ @Override
+ public BasePermission getPermissionTEMP(String permName) {
+ synchronized (PermissionManagerService.this.mLock) {
+ return mSettings.getPermissionLocked(permName);
+ }
+ }
+ @Override
+ public void putPermissionTEMP(String permName, BasePermission permission) {
+ synchronized (PermissionManagerService.this.mLock) {
+ mSettings.putPermissionLocked(permName, (BasePermission) permission);
+ }
+ }
+ @Override
+ public Iterator<BasePermission> getPermissionIteratorTEMP() {
+ synchronized (PermissionManagerService.this.mLock) {
+ return mSettings.getAllPermissionsLocked().iterator();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
new file mode 100644
index 0000000..7a2e5ecc
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2017 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.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.util.XmlUtils;
+import com.android.server.pm.DumpState;
+import com.android.server.pm.PackageManagerService;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Collection;
+
+/**
+ * Permissions and other related data. This class is not meant for
+ * direct access outside of the permission package with the sole exception
+ * of package settings. Instead, it should be reference either from the
+ * permission manager or package settings.
+ */
+public class PermissionSettings {
+
+ final boolean mPermissionReviewRequired;
+ /**
+ * All of the permissions known to the system. The mapping is from permission
+ * name to permission object.
+ */
+ private final ArrayMap<String, BasePermission> mPermissions =
+ new ArrayMap<String, BasePermission>();
+ private final Object mLock;
+
+ PermissionSettings(@NonNull Context context, @NonNull Object lock) {
+ mPermissionReviewRequired =
+ context.getResources().getBoolean(R.bool.config_permissionReviewRequired);
+ mLock = lock;
+ }
+
+ public @Nullable BasePermission getPermission(@NonNull String permName) {
+ synchronized (mLock) {
+ return getPermissionLocked(permName);
+ }
+ }
+
+ /**
+ * Transfers ownership of permissions from one package to another.
+ */
+ public void transferPermissions(String origPackageName, String newPackageName,
+ ArrayMap<String, BasePermission> permissionTrees) {
+ synchronized (mLock) {
+ for (int i=0; i<2; i++) {
+ ArrayMap<String, BasePermission> permissions =
+ i == 0 ? permissionTrees : mPermissions;
+ for (BasePermission bp : permissions.values()) {
+ bp.transfer(origPackageName, newPackageName);
+ }
+ }
+ }
+ }
+
+ public boolean canPropagatePermissionToInstantApp(String permName) {
+ synchronized (mLock) {
+ final BasePermission bp = mPermissions.get(permName);
+ return (bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant());
+ }
+ }
+
+ public void readPermissions(XmlPullParser parser) throws IOException, XmlPullParserException {
+ synchronized (mLock) {
+ readPermissions(mPermissions, parser);
+ }
+ }
+
+ public void writePermissions(XmlSerializer serializer) throws IOException {
+ for (BasePermission bp : mPermissions.values()) {
+ bp.writeLPr(serializer);
+ }
+ }
+
+ public static void readPermissions(ArrayMap<String, BasePermission> out, XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ if (!BasePermission.readLPw(out, parser)) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element reading permissions: " + parser.getName() + " at "
+ + parser.getPositionDescription());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ public void dumpPermissions(PrintWriter pw, String packageName,
+ ArraySet<String> permissionNames, boolean externalStorageEnforced,
+ DumpState dumpState) {
+ synchronized (mLock) {
+ boolean printedSomething = false;
+ for (BasePermission bp : mPermissions.values()) {
+ printedSomething = bp.dumpPermissionsLPr(pw, packageName, permissionNames,
+ externalStorageEnforced, printedSomething, dumpState);
+ }
+ }
+ }
+
+ @Nullable BasePermission getPermissionLocked(@NonNull String permName) {
+ return mPermissions.get(permName);
+ }
+
+ void putPermissionLocked(@NonNull String permName, @NonNull BasePermission permission) {
+ mPermissions.put(permName, permission);
+ }
+
+ void removePermissionLocked(@NonNull String permName) {
+ mPermissions.remove(permName);
+ }
+
+ Collection<BasePermission> getAllPermissionsLocked() {
+ return mPermissions.values();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java
similarity index 97%
rename from services/core/java/com/android/server/pm/PermissionsState.java
rename to services/core/java/com/android/server/pm/permission/PermissionsState.java
index f4d2ad2..11df380 100644
--- a/services/core/java/com/android/server/pm/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm;
+package com.android.server.pm.permission;
import android.content.pm.PackageManager;
import android.os.UserHandle;
@@ -406,7 +406,7 @@
ensurePermissionData(permission);
}
- PermissionData permissionData = mPermissions.get(permission.name);
+ PermissionData permissionData = mPermissions.get(permission.getName());
if (permissionData == null) {
if (!mayChangeFlags) {
return false;
@@ -557,7 +557,7 @@
}
private int grantPermission(BasePermission permission, int userId) {
- if (hasPermission(permission.name, userId)) {
+ if (hasPermission(permission.getName(), userId)) {
return PERMISSION_OPERATION_FAILURE;
}
@@ -581,21 +581,22 @@
}
private int revokePermission(BasePermission permission, int userId) {
- if (!hasPermission(permission.name, userId)) {
+ final String permName = permission.getName();
+ if (!hasPermission(permName, userId)) {
return PERMISSION_OPERATION_FAILURE;
}
final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
- PermissionData permissionData = mPermissions.get(permission.name);
+ PermissionData permissionData = mPermissions.get(permName);
if (!permissionData.revoke(userId)) {
return PERMISSION_OPERATION_FAILURE;
}
if (permissionData.isDefault()) {
- ensureNoPermissionData(permission.name);
+ ensureNoPermissionData(permName);
}
if (hasGids) {
@@ -625,13 +626,14 @@
}
private PermissionData ensurePermissionData(BasePermission permission) {
+ final String permName = permission.getName();
if (mPermissions == null) {
mPermissions = new ArrayMap<>();
}
- PermissionData permissionData = mPermissions.get(permission.name);
+ PermissionData permissionData = mPermissions.get(permName);
if (permissionData == null) {
permissionData = new PermissionData(permission);
- mPermissions.put(permission.name, permissionData);
+ mPermissions.put(permName, permissionData);
}
return permissionData;
}
@@ -692,7 +694,7 @@
PermissionState userState = mUserStates.get(userId);
if (userState == null) {
- userState = new PermissionState(mPerm.name);
+ userState = new PermissionState(mPerm.getName());
mUserStates.put(userId, userState);
}
@@ -760,7 +762,7 @@
}
return userState.mFlags != oldFlags;
} else if (newFlags != 0) {
- userState = new PermissionState(mPerm.name);
+ userState = new PermissionState(mPerm.getName());
userState.mFlags = newFlags;
mUserStates.put(userId, userState);
return true;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d19bf445..db7817e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -20,9 +20,12 @@
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.AppOpsManager.OP_TOAST_WINDOW;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Context.CONTEXT_RESTRICTED;
import static android.content.Context.DISPLAY_SERVICE;
import static android.content.Context.WINDOW_SERVICE;
@@ -2627,18 +2630,15 @@
attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
}
- if (ActivityManager.isHighEndGfx()) {
- if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
- attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- }
- final boolean forceWindowDrawsStatusBarBackground =
- (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND)
- != 0;
- if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
- || forceWindowDrawsStatusBarBackground
- && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) {
- attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- }
+ if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
+ attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+ }
+ final boolean forceWindowDrawsStatusBarBackground =
+ (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
+ if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
+ || forceWindowDrawsStatusBarBackground
+ && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) {
+ attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
}
}
@@ -7920,8 +7920,10 @@
mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
- mWindowManagerFuncs.getStackBounds(HOME_STACK_ID, mNonDockedStackBounds);
- mWindowManagerFuncs.getStackBounds(DOCKED_STACK_ID, mDockedStackBounds);
+ mWindowManagerFuncs.getStackBounds(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);
+ mWindowManagerFuncs.getStackBounds(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
final int diff = visibility ^ mLastSystemUiFlags;
final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 12ca89a..b917dae 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -120,7 +120,7 @@
implements Watchdog.Monitor {
private static final String TAG = "PowerManagerService";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static final boolean DEBUG_SPEW = DEBUG && true;
// Message: Sent when a user activity timeout occurs to update the power state.
@@ -206,6 +206,8 @@
private static final String REASON_REBOOT = "reboot";
private static final String REASON_USERREQUESTED = "shutdown,userrequested";
private static final String REASON_THERMAL_SHUTDOWN = "shutdown,thermal";
+ private static final String REASON_LOW_BATTERY = "shutdown,battery";
+ private static final String REASON_BATTERY_THERMAL_STATE = "shutdown,thermal,battery";
private static final String TRACE_SCREEN_ON = "Screen turning on";
@@ -4651,6 +4653,10 @@
return PowerManager.SHUTDOWN_REASON_USER_REQUESTED;
case REASON_THERMAL_SHUTDOWN:
return PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN;
+ case REASON_LOW_BATTERY:
+ return PowerManager.SHUTDOWN_REASON_LOW_BATTERY;
+ case REASON_BATTERY_THERMAL_STATE:
+ return PowerManager.SHUTDOWN_REASON_BATTERY_THERMAL;
default:
return PowerManager.SHUTDOWN_REASON_UNKNOWN;
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 11ae212..05fd248 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -30,11 +30,16 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.KernelWakelockReader;
+import com.android.internal.os.KernelWakelockStats;
import com.android.server.SystemService;
+import java.util.Map;
+
/**
* Helper service for statsd (the native stats management service in cmds/statsd/).
* Used for registering and receiving alarms on behalf of statsd.
+ * @hide
*/
public class StatsCompanionService extends IStatsCompanionService.Stub {
static final String TAG = "StatsCompanionService";
@@ -156,7 +161,44 @@
}
}
- @Override
+ // These values must be kept in sync with cmd/statsd/StatsPuller.h.
+ // TODO: pull the constant from stats_events.proto instead
+ private static final int PULL_CODE_KERNEL_WAKELOCKS = 20;
+
+ private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
+ private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
+
+ @Override // Binder call
+ public String pullData(int pullCode) {
+ enforceCallingPermission();
+ if (DEBUG) Slog.d(TAG, "Fetching " + pullCode);
+
+ StringBuilder s = new StringBuilder(); // TODO: use and return a Parcel instead of a string
+ switch (pullCode) {
+ case PULL_CODE_KERNEL_WAKELOCKS:
+ final KernelWakelockStats wakelockStats =
+ mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
+
+ for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
+ String name = ent.getKey();
+ KernelWakelockStats.Entry kws = ent.getValue();
+ s.append("Wakelock ")
+ .append(name)
+ .append(", time=")
+ .append(kws.mTotalTime)
+ .append(", count=")
+ .append(kws.mCount)
+ .append('\n');
+ }
+ break;
+ default:
+ Slog.w(TAG, "No such pollable data as " + pullCode);
+ return null;
+ }
+ return s.toString();
+ }
+
+ @Override // Binder call
public void statsdReady() {
enforceCallingPermission();
if (DEBUG) Slog.d(TAG, "learned that statsdReady");
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index f10bf1a..83fd549 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -529,7 +529,7 @@
*/
@Override
public void disable2ForUser(int what, IBinder token, String pkg, int userId) {
- enforceStatusBar();
+ enforceStatusBarService();
synchronized (mLock) {
disableLocked(userId, what, token, pkg, 2);
diff --git a/services/core/java/com/android/server/storage/AppCollector.java b/services/core/java/com/android/server/storage/AppCollector.java
index 03b754f..0b51f9c 100644
--- a/services/core/java/com/android/server/storage/AppCollector.java
+++ b/services/core/java/com/android/server/storage/AppCollector.java
@@ -135,7 +135,7 @@
PackageStats packageStats = new PackageStats(app.packageName,
user.id);
packageStats.cacheSize = storageStats.getCacheBytes();
- packageStats.codeSize = storageStats.getCodeBytes();
+ packageStats.codeSize = storageStats.getAppBytes();
packageStats.dataSize = storageStats.getDataBytes();
stats.add(packageStats);
} catch (NameNotFoundException | IOException e) {
diff --git a/services/core/java/com/android/server/storage/DiskStatsFileLogger.java b/services/core/java/com/android/server/storage/DiskStatsFileLogger.java
index 0094ab5..1db3ec4 100644
--- a/services/core/java/com/android/server/storage/DiskStatsFileLogger.java
+++ b/services/core/java/com/android/server/storage/DiskStatsFileLogger.java
@@ -56,10 +56,12 @@
public static final String SYSTEM_KEY = "systemSize";
public static final String MISC_KEY = "otherSize";
public static final String APP_SIZE_AGG_KEY = "appSize";
+ public static final String APP_DATA_SIZE_AGG_KEY = "appDataSize";
public static final String APP_CACHE_AGG_KEY = "cacheSize";
public static final String PACKAGE_NAMES_KEY = "packageNames";
public static final String APP_SIZES_KEY = "appSizes";
public static final String APP_CACHES_KEY = "cacheSizes";
+ public static final String APP_DATA_KEY = "appDataSizes";
public static final String LAST_QUERY_TIMESTAMP_KEY = "queryTime";
private MeasurementResult mResult;
@@ -114,31 +116,39 @@
private void addAppsToJson(JSONObject json) throws JSONException {
JSONArray names = new JSONArray();
JSONArray appSizeList = new JSONArray();
+ JSONArray appDataSizeList = new JSONArray();
JSONArray cacheSizeList = new JSONArray();
long appSizeSum = 0L;
+ long appDataSizeSum = 0L;
long cacheSizeSum = 0L;
boolean isExternal = Environment.isExternalStorageEmulated();
for (Map.Entry<String, PackageStats> entry : filterOnlyPrimaryUser().entrySet()) {
PackageStats stat = entry.getValue();
- long appSize = stat.codeSize + stat.dataSize;
+ long appSize = stat.codeSize;
+ long appDataSize = stat.dataSize;
long cacheSize = stat.cacheSize;
if (isExternal) {
- appSize += stat.externalCodeSize + stat.externalDataSize;
+ appSize += stat.externalCodeSize;
+ appDataSize += stat.externalDataSize;
cacheSize += stat.externalCacheSize;
}
appSizeSum += appSize;
+ appDataSizeSum += appDataSize;
cacheSizeSum += cacheSize;
names.put(stat.packageName);
appSizeList.put(appSize);
+ appDataSizeList.put(appDataSize);
cacheSizeList.put(cacheSize);
}
json.put(PACKAGE_NAMES_KEY, names);
json.put(APP_SIZES_KEY, appSizeList);
json.put(APP_CACHES_KEY, cacheSizeList);
+ json.put(APP_DATA_KEY, appDataSizeList);
json.put(APP_SIZE_AGG_KEY, appSizeSum);
json.put(APP_CACHE_AGG_KEY, cacheSizeSum);
+ json.put(APP_DATA_SIZE_AGG_KEY, appDataSizeSum);
}
/**
diff --git a/services/core/java/com/android/server/timezone/IntentHelperImpl.java b/services/core/java/com/android/server/timezone/IntentHelperImpl.java
index bc0f6e4..6e6259d 100644
--- a/services/core/java/com/android/server/timezone/IntentHelperImpl.java
+++ b/services/core/java/com/android/server/timezone/IntentHelperImpl.java
@@ -24,6 +24,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.PatternMatcher;
+import android.os.UserHandle;
import android.util.Slog;
/**
@@ -76,7 +77,9 @@
// not expected to need local data.
Receiver packageUpdateReceiver = new Receiver(packageTracker);
- mContext.registerReceiver(packageUpdateReceiver, packageIntentFilter);
+ mContext.registerReceiverAsUser(
+ packageUpdateReceiver, UserHandle.SYSTEM, packageIntentFilter,
+ null /* broadcastPermission */, null /* default handler */);
}
/** Sends an intent to trigger an update check. */
diff --git a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java
index 2e0c21b..b89dd38 100644
--- a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java
+++ b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java
@@ -26,6 +26,7 @@
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.Slog;
import java.util.List;
@@ -114,8 +115,8 @@
@Override
public boolean contentProviderRegistered(String authority, String requiredPackageName) {
int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
- ProviderInfo providerInfo =
- mPackageManager.resolveContentProvider(authority, flags);
+ ProviderInfo providerInfo = mPackageManager.resolveContentProviderAsUser(
+ authority, flags, UserHandle.SYSTEM.getIdentifier());
if (providerInfo == null) {
Slog.i(TAG, "contentProviderRegistered: No content provider registered with authority="
+ authority);
@@ -136,7 +137,8 @@
throws PackageManager.NameNotFoundException {
int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
- List<ResolveInfo> resolveInfo = mPackageManager.queryBroadcastReceivers(intent, flags);
+ List<ResolveInfo> resolveInfo = mPackageManager.queryBroadcastReceiversAsUser(
+ intent, flags, UserHandle.SYSTEM);
if (resolveInfo.size() != 1) {
Slog.i(TAG, "receiverRegistered: Zero or multiple broadcast receiver registered for"
+ " intent=" + intent + ", found=" + resolveInfo);
diff --git a/services/core/java/com/android/server/utils/ManagedApplicationService.java b/services/core/java/com/android/server/utils/ManagedApplicationService.java
index 0f251fd..c555388 100644
--- a/services/core/java/com/android/server/utils/ManagedApplicationService.java
+++ b/services/core/java/com/android/server/utils/ManagedApplicationService.java
@@ -16,19 +16,24 @@
package com.android.server.utils;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.IInterface;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
+import java.text.SimpleDateFormat;
import java.util.Objects;
+import java.util.Date;
/**
* Manages the lifecycle of an application-provided service bound from system server.
@@ -38,39 +43,126 @@
public class ManagedApplicationService {
private final String TAG = getClass().getSimpleName();
+ /**
+ * Attempt to reconnect service forever if an onBindingDied or onServiceDisconnected event
+ * is received.
+ */
+ public static final int RETRY_FOREVER = 1;
+
+ /**
+ * Never attempt to reconnect the service - a single onBindingDied or onServiceDisconnected
+ * event will cause this to fully unbind the service and never attempt to reconnect.
+ */
+ public static final int RETRY_NEVER = 2;
+
+ /**
+ * Attempt to reconnect the service until the maximum number of retries is reached, then stop.
+ *
+ * The first retry will occur MIN_RETRY_DURATION_MS after the disconnection, and each
+ * subsequent retry will occur after 2x the duration used for the previous retry up to the
+ * MAX_RETRY_DURATION_MS duration.
+ *
+ * In this case, retries mean a full unbindService/bindService pair to handle cases when the
+ * usual service re-connection logic in ActiveServices has very high backoff times or when the
+ * serviceconnection has fully died due to a package update or similar.
+ */
+ public static final int RETRY_BEST_EFFORT = 3;
+
+ // Maximum number of retries before giving up (for RETRY_BEST_EFFORT).
+ private static final int MAX_RETRY_COUNT = 4;
+ // Max time between retry attempts.
+ private static final long MAX_RETRY_DURATION_MS = 16000;
+ // Min time between retry attempts.
+ private static final long MIN_RETRY_DURATION_MS = 2000;
+ // Time since the last retry attempt after which to clear the retry attempt counter.
+ private static final long RETRY_RESET_TIME_MS = MAX_RETRY_DURATION_MS * 4;
+
private final Context mContext;
private final int mUserId;
private final ComponentName mComponent;
private final int mClientLabel;
private final String mSettingsAction;
private final BinderChecker mChecker;
-
- private final DeathRecipient mDeathRecipient = new DeathRecipient() {
- @Override
- public void binderDied() {
- synchronized (mLock) {
- mBoundInterface = null;
- }
- }
- };
+ private final boolean mIsImportant;
+ private final int mRetryType;
+ private final Handler mHandler;
+ private final Runnable mRetryRunnable = this::doRetry;
+ private final EventCallback mEventCb;
private final Object mLock = new Object();
// State protected by mLock
- private ServiceConnection mPendingConnection;
private ServiceConnection mConnection;
private IInterface mBoundInterface;
private PendingEvent mPendingEvent;
+ private int mRetryCount;
+ private long mLastRetryTimeMs;
+ private long mNextRetryDurationMs = MIN_RETRY_DURATION_MS;
+ private boolean mRetrying;
+
+ public static interface LogFormattable {
+ String toLogString(SimpleDateFormat dateFormat);
+ }
+
+ /**
+ * Lifecycle event of this managed service.
+ */
+ public static class LogEvent implements LogFormattable {
+ public static final int EVENT_CONNECTED = 1;
+ public static final int EVENT_DISCONNECTED = 2;
+ public static final int EVENT_BINDING_DIED = 3;
+ public static final int EVENT_STOPPED_PERMANENTLY = 4;
+
+ // Time of the events in "current time ms" timebase.
+ public final long timestamp;
+ // Name of the component for this system service.
+ public final ComponentName component;
+ // ID of the event that occurred.
+ public final int event;
+
+ public LogEvent(long timestamp, ComponentName component, int event) {
+ this.timestamp = timestamp;
+ this.component = component;
+ this.event = event;
+ }
+
+ @Override
+ public String toLogString(SimpleDateFormat dateFormat) {
+ return dateFormat.format(new Date(timestamp)) + " " + eventToString(event)
+ + " Managed Service: "
+ + ((component == null) ? "None" : component.flattenToString());
+ }
+
+ public static String eventToString(int event) {
+ switch (event) {
+ case EVENT_CONNECTED:
+ return "Connected";
+ case EVENT_DISCONNECTED:
+ return "Disconnected";
+ case EVENT_BINDING_DIED:
+ return "Binding Died For";
+ case EVENT_STOPPED_PERMANENTLY:
+ return "Permanently Stopped";
+ default:
+ return "Unknown Event Occurred";
+ }
+ }
+ }
private ManagedApplicationService(final Context context, final ComponentName component,
final int userId, int clientLabel, String settingsAction,
- BinderChecker binderChecker) {
+ BinderChecker binderChecker, boolean isImportant, int retryType, Handler handler,
+ EventCallback eventCallback) {
mContext = context;
mComponent = component;
mUserId = userId;
mClientLabel = clientLabel;
mSettingsAction = settingsAction;
mChecker = binderChecker;
+ mIsImportant = isImportant;
+ mRetryType = retryType;
+ mHandler = handler;
+ mEventCb = eventCallback;
}
/**
@@ -85,7 +177,17 @@
* Implement to call IInterface methods after service is connected.
*/
public interface PendingEvent {
- void runEvent(IInterface service) throws RemoteException;
+ void runEvent(IInterface service) throws RemoteException;
+ }
+
+ /**
+ * Implement to be notified about any problems with remote service.
+ */
+ public interface EventCallback {
+ /**
+ * Called when an sevice lifecycle event occurs.
+ */
+ void onServiceEvent(LogEvent event);
}
/**
@@ -95,19 +197,28 @@
* @param component the {@link ComponentName} of the application service to bind.
* @param userId the user ID of user to bind the application service as.
* @param clientLabel the resource ID of a label displayed to the user indicating the
- * binding service.
+ * binding service, or 0 if none is desired.
* @param settingsAction an action that can be used to open the Settings UI to enable/disable
- * binding to these services.
- * @param binderChecker an interface used to validate the returned binder object.
+ * binding to these services, or null if none is desired.
+ * @param binderChecker an interface used to validate the returned binder object, or null if
+ * this interface is unchecked.
+ * @param isImportant bind the user service with BIND_IMPORTANT.
+ * @param retryType reconnect behavior to have when bound service is disconnected.
+ * @param handler the Handler to use for retries and delivering EventCallbacks.
+ * @param eventCallback a callback used to deliver disconnection events, or null if you
+ * don't care.
* @return a ManagedApplicationService instance.
*/
public static ManagedApplicationService build(@NonNull final Context context,
- @NonNull final ComponentName component, final int userId, @NonNull int clientLabel,
- @NonNull String settingsAction, @NonNull BinderChecker binderChecker) {
+ @NonNull final ComponentName component, final int userId, int clientLabel,
+ @Nullable String settingsAction, @Nullable BinderChecker binderChecker,
+ boolean isImportant, int retryType, @NonNull Handler handler,
+ @Nullable EventCallback eventCallback) {
return new ManagedApplicationService(context, component, userId, clientLabel,
- settingsAction, binderChecker);
+ settingsAction, binderChecker, isImportant, retryType, handler, eventCallback);
}
+
/**
* @return the user ID of the user that owns the bound service.
*/
@@ -138,13 +249,12 @@
return true;
}
-
- /**
- * Send an event to run as soon as the binder interface is available.
- *
- * @param event a {@link PendingEvent} to send.
- */
- public void sendEvent(@NonNull PendingEvent event) {
+ /**
+ * Send an event to run as soon as the binder interface is available.
+ *
+ * @param event a {@link PendingEvent} to send.
+ */
+ public void sendEvent(@NonNull PendingEvent event) {
IInterface iface;
synchronized (mLock) {
iface = mBoundInterface;
@@ -167,15 +277,13 @@
*/
public void disconnect() {
synchronized (mLock) {
- // Wipe out pending connections
- mPendingConnection = null;
-
// Unbind existing connection, if it exists
- if (mConnection != null) {
- mContext.unbindService(mConnection);
- mConnection = null;
+ if (mConnection == null) {
+ return;
}
+ mContext.unbindService(mConnection);
+ mConnection = null;
mBoundInterface = null;
}
}
@@ -185,48 +293,70 @@
*/
public void connect() {
synchronized (mLock) {
- if (mConnection != null || mPendingConnection != null) {
+ if (mConnection != null) {
// We're already connected or are trying to connect
return;
}
- final PendingIntent pendingIntent = PendingIntent.getActivity(
- mContext, 0, new Intent(mSettingsAction), 0);
- final Intent intent = new Intent().setComponent(mComponent).
- putExtra(Intent.EXTRA_CLIENT_LABEL, mClientLabel).
- putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
+ Intent intent = new Intent().setComponent(mComponent);
+ if (mClientLabel != 0) {
+ intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mClientLabel);
+ }
+ if (mSettingsAction != null) {
+ intent.putExtra(Intent.EXTRA_CLIENT_INTENT,
+ PendingIntent.getActivity(mContext, 0, new Intent(mSettingsAction), 0));
+ }
- final ServiceConnection serviceConnection = new ServiceConnection() {
+ mConnection = new ServiceConnection() {
+ @Override
+ public void onBindingDied(ComponentName componentName) {
+ final long timestamp = System.currentTimeMillis();
+ Slog.w(TAG, "Service binding died: " + componentName);
+ synchronized (mLock) {
+ if (mConnection != this) {
+ return;
+ }
+ mHandler.post(() -> {
+ mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
+ LogEvent.EVENT_BINDING_DIED));
+ });
+
+ mBoundInterface = null;
+ startRetriesLocked();
+ }
+ }
+
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+ final long timestamp = System.currentTimeMillis();
+ Slog.i(TAG, "Service connected: " + componentName);
IInterface iface = null;
PendingEvent pendingEvent = null;
synchronized (mLock) {
- if (mPendingConnection == this) {
- // No longer pending, remove from pending connection
- mPendingConnection = null;
- mConnection = this;
- } else {
- // Service connection wasn't pending, must have been disconnected
- mContext.unbindService(this);
+ if (mConnection != this) {
+ // Must've been unbound.
return;
}
+ mHandler.post(() -> {
+ mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
+ LogEvent.EVENT_CONNECTED));
+ });
- try {
- iBinder.linkToDeath(mDeathRecipient, 0);
+ stopRetriesLocked();
+
+ mBoundInterface = null;
+ if (mChecker != null) {
mBoundInterface = mChecker.asInterface(iBinder);
if (!mChecker.checkType(mBoundInterface)) {
- // Received an invalid binder, disconnect
- mContext.unbindService(this);
+ // Received an invalid binder, disconnect.
mBoundInterface = null;
+ Slog.w(TAG, "Invalid binder from " + componentName);
+ startRetriesLocked();
+ return;
}
iface = mBoundInterface;
pendingEvent = mPendingEvent;
mPendingEvent = null;
- } catch (RemoteException e) {
- // DOA
- Slog.w(TAG, "Unable to bind service: " + intent, e);
- mBoundInterface = null;
}
}
if (iface != null && pendingEvent != null) {
@@ -234,28 +364,44 @@
pendingEvent.runEvent(iface);
} catch (RuntimeException | RemoteException ex) {
Slog.e(TAG, "Received exception from user service: ", ex);
+ startRetriesLocked();
}
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
- Slog.w(TAG, "Service disconnected: " + intent);
- mConnection = null;
- mBoundInterface = null;
+ final long timestamp = System.currentTimeMillis();
+ Slog.w(TAG, "Service disconnected: " + componentName);
+ synchronized (mLock) {
+ if (mConnection != this) {
+ return;
+ }
+
+ mHandler.post(() -> {
+ mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
+ LogEvent.EVENT_DISCONNECTED));
+ });
+
+ mBoundInterface = null;
+ startRetriesLocked();
+ }
}
};
- mPendingConnection = serviceConnection;
-
+ int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
+ if (mIsImportant) {
+ flags |= Context.BIND_IMPORTANT;
+ }
try {
- if (!mContext.bindServiceAsUser(intent, serviceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ if (!mContext.bindServiceAsUser(intent, mConnection, flags,
new UserHandle(mUserId))) {
Slog.w(TAG, "Unable to bind service: " + intent);
+ startRetriesLocked();
}
} catch (SecurityException e) {
Slog.w(TAG, "Unable to bind service: " + intent, e);
+ startRetriesLocked();
}
}
}
@@ -263,4 +409,81 @@
private boolean matches(final ComponentName component, final int userId) {
return Objects.equals(mComponent, component) && mUserId == userId;
}
+
+ private void startRetriesLocked() {
+ if (checkAndDeliverServiceDiedCbLocked()) {
+ // If we delivered the service callback, disconnect and stop retrying.
+ disconnect();
+ return;
+ }
+
+ if (mRetrying) {
+ // Retry already queued, don't queue a new one.
+ return;
+ }
+ mRetrying = true;
+ queueRetryLocked();
+ }
+
+ private void stopRetriesLocked() {
+ mRetrying = false;
+ mHandler.removeCallbacks(mRetryRunnable);
+ }
+
+ private void queueRetryLocked() {
+ long now = SystemClock.uptimeMillis();
+ if ((now - mLastRetryTimeMs) > RETRY_RESET_TIME_MS) {
+ // It's been longer than the reset time since we last had to retry. Re-initialize.
+ mNextRetryDurationMs = MIN_RETRY_DURATION_MS;
+ mRetryCount = 0;
+ }
+ mLastRetryTimeMs = now;
+ mHandler.postDelayed(mRetryRunnable, mNextRetryDurationMs);
+ mNextRetryDurationMs = Math.min(2 * mNextRetryDurationMs, MAX_RETRY_DURATION_MS);
+ mRetryCount++;
+ }
+
+ private boolean checkAndDeliverServiceDiedCbLocked() {
+
+ if (mRetryType == RETRY_NEVER || (mRetryType == RETRY_BEST_EFFORT
+ && mRetryCount >= MAX_RETRY_COUNT)) {
+ // If we never retry, or we've exhausted our retries, post the onServiceDied callback.
+ Slog.e(TAG, "Service " + mComponent + " has died too much, not retrying.");
+ if (mEventCb != null) {
+ final long timestamp = System.currentTimeMillis();
+ mHandler.post(() -> {
+ mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
+ LogEvent.EVENT_STOPPED_PERMANENTLY));
+ });
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void doRetry() {
+ synchronized (mLock) {
+ if (mConnection == null) {
+ // We disconnected for good. Don't attempt to retry.
+ return;
+ }
+ if (!mRetrying) {
+ // We successfully connected. Don't attempt to retry.
+ return;
+ }
+ Slog.i(TAG, "Attempting to reconnect " + mComponent + "...");
+ // While frameworks may restart the remote Service if we stay bound, we have little
+ // control of the backoff timing for reconnecting the service. In the event of a
+ // process crash, the backoff time can be very large (1-30 min), which is not
+ // acceptable for the types of services this is used for. Instead force an unbind/bind
+ // sequence to cause a more immediate retry.
+ disconnect();
+ if (checkAndDeliverServiceDiedCbLocked()) {
+ // No more retries.
+ return;
+ }
+ queueRetryLocked();
+ connect();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index 8f50a39..95d03d4 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -294,6 +294,9 @@
int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
mVirtualDisplay = mDisplayManager.createVirtualDisplay(null /* projection */,
DISPLAY_NAME, mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi,
null /* surface */, flags, null /* callback */, null /* handler */,
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 1f0b2f0..1511aee3 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -66,6 +66,8 @@
import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.utils.ManagedApplicationService.PendingEvent;
+import com.android.server.utils.ManagedApplicationService.LogEvent;
+import com.android.server.utils.ManagedApplicationService.LogFormattable;
import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
import com.android.server.utils.ManagedApplicationService;
import com.android.server.utils.ManagedApplicationService.BinderChecker;
@@ -108,7 +110,7 @@
static final boolean DBG = false;
private static final int PENDING_STATE_DELAY_MS = 300;
- private static final int EVENT_LOG_SIZE = 32;
+ private static final int EVENT_LOG_SIZE = 64;
private static final int INVALID_APPOPS_MODE = -1;
/** Null set of sleep sleep flags. */
private static final int FLAG_NONE = 0;
@@ -134,6 +136,7 @@
private int mVrAppProcessId;
private EnabledComponentsObserver mComponentObserver;
private ManagedApplicationService mCurrentVrService;
+ private ManagedApplicationService mCurrentVrCompositorService;
private ComponentName mDefaultVrService;
private Context mContext;
private ComponentName mCurrentVrModeComponent;
@@ -147,7 +150,8 @@
private int mPreviousCoarseLocationMode = INVALID_APPOPS_MODE;
private int mPreviousManageOverlayMode = INVALID_APPOPS_MODE;
private VrState mPendingState;
- private final ArrayDeque<VrState> mLoggingDeque = new ArrayDeque<>(EVENT_LOG_SIZE);
+ private boolean mLogLimitHit;
+ private final ArrayDeque<LogFormattable> mLoggingDeque = new ArrayDeque<>(EVENT_LOG_SIZE);
private final NotificationAccessManager mNotifAccessManager = new NotificationAccessManager();
private INotificationManager mNotificationManager;
/** Tracks the state of the screen and keyguard UI.*/
@@ -160,6 +164,30 @@
private Vr2dDisplay mVr2dDisplay;
private boolean mBootsToVr;
+ // Handles events from the managed services (e.g. VrListenerService and any bound VR compositor
+ // service).
+ private final ManagedApplicationService.EventCallback mEventCallback
+ = new ManagedApplicationService.EventCallback() {
+ @Override
+ public void onServiceEvent(LogEvent event) {
+ logEvent(event);
+
+ ComponentName component = null;
+ synchronized (mLock) {
+ component = ((mCurrentVrService == null) ? null : mCurrentVrService.getComponent());
+ }
+
+ // If not on an AIO device and we permanently stopped trying to connect to the
+ // VrListenerService (or don't have one bound), leave persistent VR mode and VR mode.
+ if (!mBootsToVr && event.event == LogEvent.EVENT_STOPPED_PERMANENTLY &&
+ (component == null || component.equals(event.component))) {
+ Slog.e(TAG, "VrListenerSevice has died permanently, leaving system VR mode.");
+ // We're not a native VR device. Leave VR + persistent mode.
+ setPersistentVrModeEnabled(false);
+ }
+ }
+ };
+
private static final int MSG_VR_STATE_CHANGE = 0;
private static final int MSG_PENDING_VR_STATE_CHANGE = 1;
private static final int MSG_PERSISTENT_VR_MODE_STATE_CHANGE = 2;
@@ -276,7 +304,24 @@
}
};
- private static class VrState {
+ // Event used to log when settings are changed for dumpsys logs.
+ private static class SettingEvent implements LogFormattable {
+ public final long timestamp;
+ public final String what;
+
+ SettingEvent(String what) {
+ this.timestamp = System.currentTimeMillis();
+ this.what = what;
+ }
+
+ @Override
+ public String toLogString(SimpleDateFormat dateFormat) {
+ return dateFormat.format(new Date(timestamp)) + " " + what;
+ }
+ }
+
+ // Event used to track changes of the primary on-screen VR activity.
+ private static class VrState implements LogFormattable {
final boolean enabled;
final boolean running2dInVr;
final int userId;
@@ -286,7 +331,6 @@
final long timestamp;
final boolean defaultPermissionsGranted;
-
VrState(boolean enabled, boolean running2dInVr, ComponentName targetPackageName, int userId,
int processId, ComponentName callingPackage) {
this.enabled = enabled;
@@ -310,6 +354,39 @@
this.defaultPermissionsGranted = defaultPermissionsGranted;
this.timestamp = System.currentTimeMillis();
}
+
+ @Override
+ public String toLogString(SimpleDateFormat dateFormat) {
+ String tab = " ";
+ String newLine = "\n";
+ StringBuilder sb = new StringBuilder(dateFormat.format(new Date(timestamp)));
+ sb.append(tab);
+ sb.append("State changed to:");
+ sb.append(tab);
+ sb.append((enabled) ? "ENABLED" : "DISABLED");
+ sb.append(newLine);
+ if (enabled) {
+ sb.append(tab);
+ sb.append("User=");
+ sb.append(userId);
+ sb.append(newLine);
+ sb.append(tab);
+ sb.append("Current VR Activity=");
+ sb.append((callingPackage == null) ? "None" : callingPackage.flattenToString());
+ sb.append(newLine);
+ sb.append(tab);
+ sb.append("Bound VrListenerService=");
+ sb.append((targetPackageName == null) ? "None"
+ : targetPackageName.flattenToString());
+ sb.append(newLine);
+ if (defaultPermissionsGranted) {
+ sb.append(tab);
+ sb.append("Default permissions granted to the bound VrListenerService.");
+ sb.append(newLine);
+ }
+ }
+ return sb.toString();
+ }
}
private static final BinderChecker sBinderChecker = new BinderChecker() {
@@ -490,6 +567,13 @@
}
@Override
+ public void setAndBindCompositor(String componentName) {
+ enforceCallerPermissionAnyOf(Manifest.permission.RESTRICTED_VR_ACCESS);
+ VrManagerService.this.setAndBindCompositor(
+ (componentName == null) ? null : ComponentName.unflattenFromString(componentName));
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -497,6 +581,12 @@
pw.println("VR mode is currently: " + ((mVrModeAllowed) ? "allowed" : "disallowed"));
pw.println("Persistent VR mode is currently: " +
((mPersistentVrModeEnabled) ? "enabled" : "disabled"));
+ pw.println("Currently bound VR listener service: "
+ + ((mCurrentVrService == null)
+ ? "None" : mCurrentVrService.getComponent().flattenToString()));
+ pw.println("Currently bound VR compositor service: "
+ + ((mCurrentVrCompositorService == null)
+ ? "None" : mCurrentVrCompositorService.getComponent().flattenToString()));
pw.println("Previous state transitions:\n");
String tab = " ";
dumpStateTransitions(pw);
@@ -785,6 +875,7 @@
+ mCurrentVrService.getComponent() + " for user "
+ mCurrentVrService.getUserId());
mCurrentVrService.disconnect();
+ updateCompositorServiceLocked(UserHandle.USER_NULL, null);
mCurrentVrService = null;
} else {
nothingChanged = true;
@@ -798,6 +889,7 @@
Slog.i(TAG, "VR mode component changed to " + component
+ ", disconnecting " + mCurrentVrService.getComponent()
+ " for user " + mCurrentVrService.getUserId());
+ updateCompositorServiceLocked(UserHandle.USER_NULL, null);
createAndConnectService(component, userId);
sendUpdatedCaller = true;
} else {
@@ -985,7 +1077,7 @@
private void createAndConnectService(@NonNull ComponentName component, int userId) {
- mCurrentVrService = VrManagerService.create(mContext, component, userId);
+ mCurrentVrService = createVrListenerService(component, userId);
mCurrentVrService.connect();
Slog.i(TAG, "Connecting " + component + " for user " + userId);
}
@@ -1020,13 +1112,27 @@
}
/**
- * Helper function for making ManagedApplicationService instances.
+ * Helper function for making ManagedApplicationService for VrListenerService instances.
*/
- private static ManagedApplicationService create(@NonNull Context context,
- @NonNull ComponentName component, int userId) {
- return ManagedApplicationService.build(context, component, userId,
+ private ManagedApplicationService createVrListenerService(@NonNull ComponentName component,
+ int userId) {
+ int retryType = (mBootsToVr) ? ManagedApplicationService.RETRY_FOREVER
+ : ManagedApplicationService.RETRY_NEVER;
+ return ManagedApplicationService.build(mContext, component, userId,
R.string.vr_listener_binding_label, Settings.ACTION_VR_LISTENER_SETTINGS,
- sBinderChecker);
+ sBinderChecker, /*isImportant*/true, retryType, mHandler, mEventCallback);
+ }
+
+ /**
+ * Helper function for making ManagedApplicationService for VR Compositor instances.
+ */
+ private ManagedApplicationService createVrCompositorService(@NonNull ComponentName component,
+ int userId) {
+ int retryType = (mBootsToVr) ? ManagedApplicationService.RETRY_FOREVER
+ : ManagedApplicationService.RETRY_BEST_EFFORT;
+ return ManagedApplicationService.build(mContext, component, userId, /*clientLabel*/0,
+ /*settingsAction*/null, /*binderChecker*/null, /*isImportant*/true, retryType,
+ mHandler, /*disconnectCallback*/mEventCallback);
}
/**
@@ -1057,44 +1163,35 @@
private void logStateLocked() {
ComponentName currentBoundService = (mCurrentVrService == null) ? null :
- mCurrentVrService.getComponent();
- VrState current = new VrState(mVrModeEnabled, mRunning2dInVr, currentBoundService,
- mCurrentVrModeUser, mVrAppProcessId, mCurrentVrModeComponent, mWasDefaultGranted);
- if (mLoggingDeque.size() == EVENT_LOG_SIZE) {
- mLoggingDeque.removeFirst();
+ mCurrentVrService.getComponent();
+ logEvent(new VrState(mVrModeEnabled, mRunning2dInVr, currentBoundService,
+ mCurrentVrModeUser, mVrAppProcessId, mCurrentVrModeComponent, mWasDefaultGranted));
+ }
+
+ private void logEvent(LogFormattable event) {
+ synchronized (mLoggingDeque) {
+ if (mLoggingDeque.size() == EVENT_LOG_SIZE) {
+ mLoggingDeque.removeFirst();
+ mLogLimitHit = true;
+ }
+ mLoggingDeque.add(event);
}
- mLoggingDeque.add(current);
}
private void dumpStateTransitions(PrintWriter pw) {
SimpleDateFormat d = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
- String tab = " ";
- if (mLoggingDeque.size() == 0) {
- pw.print(tab);
- pw.println("None");
- }
- for (VrState state : mLoggingDeque) {
- pw.print(d.format(new Date(state.timestamp)));
- pw.print(tab);
- pw.print("State changed to:");
- pw.print(tab);
- pw.println((state.enabled) ? "ENABLED" : "DISABLED");
- if (state.enabled) {
- pw.print(tab);
- pw.print("User=");
- pw.println(state.userId);
- pw.print(tab);
- pw.print("Current VR Activity=");
- pw.println((state.callingPackage == null) ?
- "None" : state.callingPackage.flattenToString());
- pw.print(tab);
- pw.print("Bound VrListenerService=");
- pw.println((state.targetPackageName == null) ?
- "None" : state.targetPackageName.flattenToString());
- if (state.defaultPermissionsGranted) {
- pw.print(tab);
- pw.println("Default permissions granted to the bound VrListenerService.");
- }
+ synchronized (mLoggingDeque) {
+ if (mLoggingDeque.size() == 0) {
+ pw.print(" ");
+ pw.println("None");
+ }
+
+ if (mLogLimitHit) {
+ pw.println("..."); // Indicates log overflow
+ }
+
+ for (LogFormattable event : mLoggingDeque) {
+ pw.println(event.toLogString(d));
}
}
}
@@ -1177,10 +1274,41 @@
return INVALID_DISPLAY;
}
+ private void setAndBindCompositor(ComponentName componentName) {
+ final int userId = UserHandle.getCallingUserId();
+ final long token = Binder.clearCallingIdentity();
+ synchronized (mLock) {
+ updateCompositorServiceLocked(userId, componentName);
+ }
+ Binder.restoreCallingIdentity(token);
+ }
+
+ private void updateCompositorServiceLocked(int userId, ComponentName componentName) {
+ if (mCurrentVrCompositorService != null
+ && mCurrentVrCompositorService.disconnectIfNotMatching(componentName, userId)) {
+ Slog.i(TAG, "Disconnecting compositor service: "
+ + mCurrentVrCompositorService.getComponent());
+ // Check if existing service matches the requested one, if not (or if the requested
+ // component is null) disconnect it.
+ mCurrentVrCompositorService = null;
+ }
+
+ if (componentName != null && mCurrentVrCompositorService == null) {
+ // We don't have an existing service matching the requested component, so attempt to
+ // connect one.
+ Slog.i(TAG, "Connecting compositor service: " + componentName);
+ mCurrentVrCompositorService = createVrCompositorService(componentName, userId);
+ mCurrentVrCompositorService.connect();
+ }
+ }
+
private void setPersistentModeAndNotifyListenersLocked(boolean enabled) {
if (mPersistentVrModeEnabled == enabled) {
return;
}
+ String eventName = "Persistent VR mode " + ((enabled) ? "enabled" : "disabled");
+ Slog.i(TAG, eventName);
+ logEvent(new SettingEvent(eventName));
mPersistentVrModeEnabled = enabled;
mHandler.sendMessage(mHandler.obtainMessage(MSG_PERSISTENT_VR_MODE_STATE_CHANGE,
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index c76b905..5365e27 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -16,10 +16,10 @@
package com.android.server.wm;
-import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -78,11 +78,8 @@
// requires that the duration of the two animations are the same.
SurfaceControl thumbnail;
int thumbnailTransactionSeq;
- // TODO(b/62029108): combine both members into a private one. Create a member function to set
- // the thumbnail layer to +1 to the highest layer position and replace all setter instances
- // with this function. Remove all unnecessary calls to both variables in other classes.
- int thumbnailLayer;
- int thumbnailForceAboveLayer;
+ private int mThumbnailLayer;
+
Animation thumbnailAnimation;
final Transformation thumbnailTransformation = new Transformation();
// This flag indicates that the destruction of the thumbnail surface is synchronized with
@@ -256,7 +253,7 @@
private void updateLayers() {
mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */);
- thumbnailLayer = mAppToken.getHighestAnimLayer();
+ updateThumbnailLayer();
}
private void stepThumbnailAnimation(long currentTime) {
@@ -280,26 +277,35 @@
thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
"thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
- + " layer=" + thumbnailLayer
+ + " layer=" + mThumbnailLayer
+ " matrix=[" + tmpFloats[Matrix.MSCALE_X]
+ "," + tmpFloats[Matrix.MSKEW_Y]
+ "][" + tmpFloats[Matrix.MSKEW_X]
+ "," + tmpFloats[Matrix.MSCALE_Y] + "]");
thumbnail.setAlpha(thumbnailTransformation.getAlpha());
- if (thumbnailForceAboveLayer > 0) {
- thumbnail.setLayer(thumbnailForceAboveLayer + 1);
- } else {
- // The thumbnail is layered below the window immediately above this
- // token's anim layer.
- thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
- - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
- }
+ updateThumbnailLayer();
thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
}
/**
+ * Updates the thumbnail layer z order to just above the highest animation layer if changed
+ */
+ void updateThumbnailLayer() {
+ if (thumbnail != null) {
+ final int layer = mAppToken.getHighestAnimLayer();
+ if (layer != mThumbnailLayer) {
+ if (DEBUG_LAYERS) Slog.v(TAG,
+ "Setting thumbnail layer " + mAppToken + ": layer=" + layer);
+ thumbnail.setLayer(layer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
+ - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
+ mThumbnailLayer = layer;
+ }
+ }
+ }
+
+ /**
* Sometimes we need to synchronize the first frame of animation with some external event, e.g.
* Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
* and keep producing the first frame of the animation.
@@ -473,7 +479,7 @@
}
if (thumbnail != null) {
pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
- pw.print(" layer="); pw.println(thumbnailLayer);
+ pw.print(" layer="); pw.println(mThumbnailLayer);
pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
pw.print(prefix); pw.print("thumbnailTransformation=");
pw.println(thumbnailTransformation.toShortString());
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 2d1fc91..a1eeff8 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -16,8 +16,7 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
@@ -1171,7 +1170,6 @@
wAppAnimator.thumbnail.destroy();
}
wAppAnimator.thumbnail = tAppAnimator.thumbnail;
- wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer;
wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation;
tAppAnimator.thumbnail = null;
}
@@ -1312,7 +1310,8 @@
// Notify the pinned stack upon all windows drawn. If there was an animation in
// progress then this signal will resume that animation.
- final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID);
+ final TaskStack pinnedStack =
+ mDisplayContent.getStack(WINDOWING_MODE_PINNED);
if (pinnedStack != null) {
pinnedStack.onAllWindowsDrawn();
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 1da94da..9e028d3 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -21,8 +21,10 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
import static com.android.server.wm.proto.ConfigurationContainerProto.FULL_CONFIGURATION;
import static com.android.server.wm.proto.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION;
@@ -152,6 +154,17 @@
onOverrideConfigurationChanged(mTmpConfig);
}
+ /**
+ * Returns true if this container is currently in multi-window mode. I.e. sharing the screen
+ * with another activity.
+ */
+ public boolean inMultiWindowMode() {
+ /*@WindowConfiguration.WindowingMode*/ int windowingMode =
+ mFullConfiguration.windowConfiguration.getWindowingMode();
+ return windowingMode != WINDOWING_MODE_FULLSCREEN
+ && windowingMode != WINDOWING_MODE_UNDEFINED;
+ }
+
/** Returns true if this container is currently in split-screen windowing mode. */
public boolean inSplitScreenWindowingMode() {
/*@WindowConfiguration.WindowingMode*/ int windowingMode =
@@ -175,7 +188,7 @@
* {@link WindowConfiguration##WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} windowing modes based on
* its current state.
*/
- public boolean supportSplitScreenWindowingMode() {
+ public boolean supportsSplitScreenWindowingMode() {
return mFullConfiguration.windowConfiguration.supportSplitScreenWindowingMode();
}
@@ -225,9 +238,48 @@
/*@WindowConfiguration.ActivityType*/ int thisType = getActivityType();
/*@WindowConfiguration.ActivityType*/ int otherType = other.getActivityType();
- return thisType == otherType
- || thisType == ACTIVITY_TYPE_UNDEFINED
- || otherType == ACTIVITY_TYPE_UNDEFINED;
+ if (thisType == otherType) {
+ return true;
+ }
+ if (thisType == ACTIVITY_TYPE_ASSISTANT) {
+ // Assistant activities are only compatible with themselves...
+ return false;
+ }
+ // Otherwise we are compatible if us or other is not currently defined.
+ return thisType == ACTIVITY_TYPE_UNDEFINED || otherType == ACTIVITY_TYPE_UNDEFINED;
+ }
+
+ /**
+ * Returns true if this container is compatible with the input windowing mode and activity type.
+ * The container is compatible:
+ * - If {@param activityType} and {@param windowingMode} match this container activity type and
+ * windowing mode.
+ * - If {@param activityType} is {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
+ * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} and this containers activity type is also
+ * standard or undefined and its windowing mode matches {@param windowingMode}.
+ * - If {@param activityType} isn't {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
+ * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} or this containers activity type isn't
+ * also standard or undefined and its activity type matches {@param activityType} regardless of
+ * if {@param windowingMode} matches the containers windowing mode.
+ */
+ public boolean isCompatible(int windowingMode, int activityType) {
+ final int thisActivityType = getActivityType();
+ final int thisWindowingMode = getWindowingMode();
+ final boolean sameActivityType = thisActivityType == activityType;
+ final boolean sameWindowingMode = thisWindowingMode == windowingMode;
+
+ if (sameActivityType && sameWindowingMode) {
+ return true;
+ }
+
+ if ((activityType != ACTIVITY_TYPE_UNDEFINED && activityType != ACTIVITY_TYPE_STANDARD)
+ || !isActivityTypeStandardOrUndefined()) {
+ // Only activity type need to match for non-standard activity types that are defined.
+ return sameActivityType;
+ }
+
+ // Otherwise we are compatible if the windowing mode is the same.
+ return sameWindowingMode;
}
public void registerConfigurationChangeListener(ConfigurationContainerListener listener) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 98a1bd3..0e68a8f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -18,9 +18,11 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -117,7 +119,6 @@
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.app.ActivityManager.StackId;
-import android.app.WindowConfiguration;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
@@ -391,10 +392,6 @@
!= mTmpWindowAnimator.mAnimTransactionSequence) {
appAnimator.thumbnailTransactionSeq =
mTmpWindowAnimator.mAnimTransactionSequence;
- appAnimator.thumbnailLayer = 0;
- }
- if (appAnimator.thumbnailLayer < winAnimator.mAnimLayer) {
- appAnimator.thumbnailLayer = winAnimator.mAnimLayer;
}
}
};
@@ -968,10 +965,10 @@
final int lastOrientation = mLastOrientation;
final boolean oldAltOrientation = mAltOrientation;
int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation);
- final boolean rotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
+ boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
rotation);
- if (rotateSeamlessly) {
+ if (mayRotateSeamlessly) {
final WindowState seamlessRotated = getWindow((w) -> w.mSeamlesslyRotated);
if (seamlessRotated != null) {
// We can't rotate (seamlessly or not) while waiting for the last seamless rotation
@@ -980,7 +977,20 @@
// window-removal.
return false;
}
+
+ // In the presence of the PINNED stack or System Alert
+ // windows we unforuntately can not seamlessly rotate.
+ if (getStackById(PINNED_STACK_ID) != null) {
+ mayRotateSeamlessly = false;
+ }
+ for (int i = 0; i < mService.mSessions.size(); i++) {
+ if (mService.mSessions.valueAt(i).hasAlertWindowSurfaces()) {
+ mayRotateSeamlessly = false;
+ break;
+ }
+ }
}
+ final boolean rotateSeamlessly = mayRotateSeamlessly;
// TODO: Implement forced rotation changes.
// Set mAltOrientation to indicate that the application is receiving
@@ -1457,16 +1467,38 @@
return null;
}
+ /**
+ * Returns the topmost stack on the display that is compatible with the input windowing mode.
+ * Null is no compatible stack on the display.
+ */
+ TaskStack getStack(int windowingMode) {
+ return getStack(windowingMode, ACTIVITY_TYPE_UNDEFINED);
+ }
+
+ /**
+ * Returns the topmost stack on the display that is compatible with the input windowing mode and
+ * activity type. Null is no compatible stack on the display.
+ */
+ TaskStack getStack(int windowingMode, int activityType) {
+ for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
+ final TaskStack stack = mTaskStackContainers.get(i);
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
@VisibleForTesting
int getStackCount() {
return mTaskStackContainers.size();
}
@VisibleForTesting
- int getStackPosById(int stackId) {
+ int getStackPosition(int windowingMode, int activityType) {
for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
final TaskStack stack = mTaskStackContainers.get(i);
- if (stack.mStackId == stackId) {
+ if (stack.isCompatible(windowingMode, activityType)) {
return i;
}
}
@@ -1501,7 +1533,7 @@
// If there was no pinned stack, we still need to notify the controller of the display info
// update as a result of the config change. We do this here to consolidate the flow between
// changes when there is and is not a stack.
- if (getStackById(PINNED_STACK_ID) == null) {
+ if (getStack(WINDOWING_MODE_PINNED) == null) {
mPinnedStackControllerLocked.onDisplayInfoChanged();
}
}
@@ -2026,8 +2058,8 @@
for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
final TaskStack stack = mTaskStackContainers.get(i);
final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM;
- if (stack.isVisible() && (imeOnBottom || isDockedOnBottom) &&
- StackId.isStackAffectedByDragResizing(stack.mStackId)) {
+ if (stack.isVisible() && (imeOnBottom || isDockedOnBottom)
+ && stack.inSplitScreenWindowingMode()) {
stack.setAdjustedForIme(imeWin, imeOnBottom && imeHeightChanged);
} else {
stack.resetAdjustedForIme(false);
@@ -2238,7 +2270,7 @@
* @return The docked stack, but only if it is visible, and {@code null} otherwise.
*/
TaskStack getDockedStackLocked() {
- final TaskStack stack = getStackById(DOCKED_STACK_ID);
+ final TaskStack stack = getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
return (stack != null && stack.isVisible()) ? stack : null;
}
@@ -2247,7 +2279,7 @@
* visible.
*/
TaskStack getDockedStackIgnoringVisibility() {
- return getStackById(DOCKED_STACK_ID);
+ return getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
}
/** Find the visible, touch-deliverable window under the given point */
@@ -3356,7 +3388,7 @@
* @see WindowManagerService#addStackToDisplay(int, int, boolean)
*/
void addStackToDisplay(TaskStack stack, boolean onTop) {
- if (stack.mStackId == HOME_STACK_ID) {
+ if (stack.isActivityTypeHome()) {
if (mHomeStack != null) {
throw new IllegalArgumentException("attachStack: HOME_STACK_ID (0) not first.");
}
@@ -3421,11 +3453,11 @@
: requestedPosition >= topChildPosition;
int targetPosition = requestedPosition;
- if (toTop && stack.mStackId != PINNED_STACK_ID
- && getStackById(PINNED_STACK_ID) != null) {
+ if (toTop && stack.getWindowingMode() != WINDOWING_MODE_PINNED
+ && getStack(WINDOWING_MODE_PINNED) != null) {
// The pinned stack is always the top most stack (always-on-top) when it is present.
TaskStack topStack = mChildren.get(topChildPosition);
- if (topStack.mStackId != PINNED_STACK_ID) {
+ if (topStack.getWindowingMode() != WINDOWING_MODE_PINNED) {
throw new IllegalStateException("Pinned stack isn't top stack??? " + mChildren);
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 030b986..6f441b9 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -17,8 +17,10 @@
package com.android.server.wm;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Surface.ROTATION_270;
@@ -330,7 +332,7 @@
mLastVisibility = visible;
notifyDockedDividerVisibilityChanged(visible);
if (!visible) {
- setResizeDimLayer(false, INVALID_STACK_ID, 0f);
+ setResizeDimLayer(false, WINDOWING_MODE_UNDEFINED, 0f);
}
}
@@ -456,7 +458,8 @@
boolean isHomeStackResizable) {
long animDuration = 0;
if (animate) {
- final TaskStack stack = mDisplayContent.getStackById(DOCKED_STACK_ID);
+ final TaskStack stack =
+ mDisplayContent.getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final long transitionDuration = isAnimationMaximizing()
? mService.mAppTransition.getLastClipRevealTransitionDuration()
: DEFAULT_APP_TRANSITION_DURATION;
@@ -517,9 +520,18 @@
}
- void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
+ /**
+ * Shows a dim layer with {@param alpha} if {@param visible} is true and
+ * {@param targetWindowingMode} isn't
+ * {@link android.app.WindowConfiguration#WINDOWING_MODE_UNDEFINED} and there is a stack on the
+ * display in that windowing mode.
+ */
+ void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
mService.openSurfaceTransaction();
- final TaskStack stack = mDisplayContent.getStackById(targetStackId);
+ // TODO: Maybe only allow split-screen windowing modes?
+ final TaskStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED
+ ? mDisplayContent.getStack(targetWindowingMode)
+ : null;
final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
boolean visibleAndValid = visible && stack != null && dockedStack != null;
if (visibleAndValid) {
@@ -605,8 +617,8 @@
if (mMinimizedDock && mService.mPolicy.isKeyguardShowingAndNotOccluded()) {
return;
}
- final TaskStack fullscreenStack =
- mDisplayContent.getStackById(FULLSCREEN_WORKSPACE_STACK_ID);
+ final TaskStack fullscreenStack = mDisplayContent.getStack(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
final boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
final boolean homeBehind = fullscreenStack != null && fullscreenStack.isVisible();
setMinimizedDockedStack(homeVisible && !homeBehind, animate);
@@ -801,7 +813,8 @@
}
private boolean animateForMinimizedDockedStack(long now) {
- final TaskStack stack = mDisplayContent.getStackById(DOCKED_STACK_ID);
+ final TaskStack stack =
+ mDisplayContent.getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
if (!mAnimationStarted) {
mAnimationStarted = true;
mAnimationStartTime = now;
diff --git a/services/core/java/com/android/server/wm/DragResizeMode.java b/services/core/java/com/android/server/wm/DragResizeMode.java
index 8ab0406..c0bf1e8 100644
--- a/services/core/java/com/android/server/wm/DragResizeMode.java
+++ b/services/core/java/com/android/server/wm/DragResizeMode.java
@@ -16,11 +16,7 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
/**
* Describes the mode in which a window is drag resizing.
@@ -39,15 +35,12 @@
*/
static final int DRAG_RESIZE_MODE_DOCKED_DIVIDER = 1;
- static boolean isModeAllowedForStack(int stackId, int mode) {
+ static boolean isModeAllowedForStack(TaskStack stack, int mode) {
switch (mode) {
case DRAG_RESIZE_MODE_FREEFORM:
- return stackId == FREEFORM_WORKSPACE_STACK_ID;
+ return stack.getWindowingMode() == WINDOWING_MODE_FREEFORM;
case DRAG_RESIZE_MODE_DOCKED_DIVIDER:
- return stackId == DOCKED_STACK_ID
- || stackId == FULLSCREEN_WORKSPACE_STACK_ID
- || stackId == HOME_STACK_ID
- || stackId == RECENTS_STACK_ID;
+ return stack.inSplitScreenWindowingMode();
default:
return false;
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 1e7140a..ef31598 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -16,7 +16,8 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -404,27 +405,29 @@
*/
private void notifyMovementBoundsChanged(boolean fromImeAdjustement) {
synchronized (mService.mWindowMap) {
- if (mPinnedStackListener != null) {
- try {
- final Rect insetBounds = new Rect();
- getInsetBounds(insetBounds);
- final Rect normalBounds = getDefaultBounds();
- if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
- transformBoundsToAspectRatio(normalBounds, mAspectRatio,
- false /* useCurrentMinEdgeSize */);
- }
- final Rect animatingBounds = mTmpAnimatingBoundsRect;
- final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID);
- if (pinnedStack != null) {
- pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
- } else {
- animatingBounds.set(normalBounds);
- }
- mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds,
- animatingBounds, fromImeAdjustement, mDisplayInfo.rotation);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering actions changed event.", e);
+ if (mPinnedStackListener == null) {
+ return;
+ }
+ try {
+ final Rect insetBounds = new Rect();
+ getInsetBounds(insetBounds);
+ final Rect normalBounds = getDefaultBounds();
+ if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
+ transformBoundsToAspectRatio(normalBounds, mAspectRatio,
+ false /* useCurrentMinEdgeSize */);
}
+ final Rect animatingBounds = mTmpAnimatingBoundsRect;
+ final TaskStack pinnedStack =
+ mDisplayContent.getStack(WINDOWING_MODE_PINNED);
+ if (pinnedStack != null) {
+ pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
+ } else {
+ animatingBounds.set(normalBounds);
+ }
+ mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds,
+ animatingBounds, fromImeAdjustement, mDisplayInfo.rotation);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering actions changed event.", e);
}
}
}
@@ -491,7 +494,7 @@
pw.println(prefix + "PinnedStackController");
pw.print(prefix + " defaultBounds="); getDefaultBounds().printShortString(pw);
pw.println();
- mService.getStackBounds(PINNED_STACK_ID, mTmpRect);
+ mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect);
pw.print(prefix + " movementBounds="); getMovementBounds(mTmpRect).printShortString(pw);
pw.println();
pw.println(prefix + " mIsImeShowing=" + mIsImeShowing);
@@ -512,7 +515,7 @@
void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
getDefaultBounds().writeToProto(proto, DEFAULT_BOUNDS);
- mService.getStackBounds(PINNED_STACK_ID, mTmpRect);
+ mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect);
getMovementBounds(mTmpRect).writeToProto(proto, MOVEMENT_BOUNDS);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 590ac6e..41f076d 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -16,19 +16,16 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
import android.app.RemoteAction;
-import android.content.res.Configuration;
import android.graphics.Rect;
-import com.android.server.UiThread;
-
import java.util.List;
/**
@@ -40,8 +37,8 @@
private Rect mTmpToBounds = new Rect();
public PinnedStackWindowController(int stackId, PinnedStackWindowListener listener,
- int displayId, boolean onTop, Rect outBounds) {
- super(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
+ int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
+ super(stackId, listener, displayId, onTop, outBounds, service);
}
/**
@@ -101,7 +98,8 @@
}
schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
- mService.getStackBounds(FULLSCREEN_WORKSPACE_STACK_ID, mTmpToBounds);
+ mService.getStackBounds(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds);
if (!mTmpToBounds.isEmpty()) {
// If there is a fullscreen bounds, use that
toBounds = new Rect(mTmpToBounds);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 227e4b2..7832f5d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -422,6 +422,17 @@
return null;
}
+ TaskStack getStack(int windowingMode, int activityType) {
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final DisplayContent dc = mChildren.get(i);
+ final TaskStack stack = dc.getStack(windowingMode, activityType);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
void setSecureSurfaceState(int userId, boolean disabled) {
forAllWindows((w) -> {
if (w.mHasSurface && userId == UserHandle.getUserId(w.mOwnerUid)) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 1781247..4dd147e 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -719,4 +719,8 @@
public String toString() {
return mStringName;
}
+
+ boolean hasAlertWindowSurfaces() {
+ return !mAlertWindowSurfaces.isEmpty();
+ }
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7464a9b..7e8d130 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -23,6 +23,7 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.res.Configuration.EMPTY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static com.android.server.EventLogTags.WM_TASK_REMOVED;
@@ -37,7 +38,6 @@
import static com.android.server.wm.proto.TaskProto.WINDOW_CONTAINER;
import android.annotation.CallSuper;
-import android.app.ActivityManager.StackId;
import android.app.ActivityManager.TaskDescription;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -280,15 +280,9 @@
// WindowConfiguration long term.
private int setBounds(Rect bounds, Configuration overrideConfig) {
if (overrideConfig == null) {
- overrideConfig = Configuration.EMPTY;
+ overrideConfig = EMPTY;
}
- if (bounds == null && !Configuration.EMPTY.equals(overrideConfig)) {
- throw new IllegalArgumentException("null bounds but non empty configuration: "
- + overrideConfig);
- }
- if (bounds != null && Configuration.EMPTY.equals(overrideConfig)) {
- throw new IllegalArgumentException("non null bounds, but empty configuration");
- }
+
boolean oldFullscreen = mFillsParent;
int rotation = Surface.ROTATION_0;
final DisplayContent displayContent = mStack.getDisplayContent();
@@ -323,7 +317,7 @@
if (displayContent != null) {
displayContent.mDimLayerController.updateDimLayer(this);
}
- onOverrideConfigurationChanged(mFillsParent ? Configuration.EMPTY : overrideConfig);
+ onOverrideConfigurationChanged(overrideConfig);
return boundsChange;
}
@@ -406,7 +400,7 @@
* the adjusted bounds's top.
*/
void alignToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds, boolean alignBottom) {
- if (!isResizeable() || Configuration.EMPTY.equals(getOverrideConfiguration())) {
+ if (!isResizeable() || EMPTY.equals(getOverrideConfiguration())) {
return;
}
@@ -531,7 +525,7 @@
void setDragResizing(boolean dragResizing, int dragResizeMode) {
if (mDragResizing != dragResizing) {
- if (!DragResizeMode.isModeAllowedForStack(mStack.mStackId, dragResizeMode)) {
+ if (!DragResizeMode.isModeAllowedForStack(mStack, dragResizeMode)) {
throw new IllegalArgumentException("Drag resize mode not allow for stack stackId="
+ mStack.mStackId + " dragResizeMode=" + dragResizeMode);
}
@@ -554,7 +548,9 @@
return;
}
if (mFillsParent) {
- setBounds(null, Configuration.EMPTY);
+ // TODO: Yeah...not sure if this works with WindowConfiguration, but shouldn't be a
+ // problem once we move mBounds into WindowConfiguration.
+ setBounds(null, getOverrideConfiguration());
return;
}
final int newRotation = displayContent.getDisplayInfo().rotation;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 751769a..bff24f6 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -254,7 +254,7 @@
@VisibleForTesting
int getSnapshotMode(Task task) {
final AppWindowToken topChild = task.getTopChild();
- if (StackId.isHomeOrRecentsStack(task.mStack.mStackId)) {
+ if (!task.isActivityTypeStandardOrUndefined()) {
return SNAPSHOT_MODE_NONE;
} else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) {
return SNAPSHOT_MODE_APP_THEME;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 7cb90de..65278837 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -19,8 +19,11 @@
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -44,7 +47,6 @@
import static com.android.server.wm.proto.StackProto.WINDOW_CONTAINER;
import android.annotation.CallSuper;
-import android.app.ActivityManager.StackId;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
@@ -736,7 +738,7 @@
outTempTaskBounds.setEmpty();
// When the home stack is resizable, should always have the same stack and task bounds
- if (mStackId == HOME_STACK_ID) {
+ if (isActivityTypeHome()) {
final Task homeTask = findHomeTask();
if (homeTask != null && homeTask.isResizeable()) {
// Calculate the home stack bounds when in docked mode and the home stack is
@@ -921,7 +923,9 @@
void resetAnimationBackgroundAnimator() {
mAnimationBackgroundAnimator = null;
- mAnimationBackgroundSurface.hide();
+ if (mAnimationBackgroundSurface != null) {
+ mAnimationBackgroundSurface.hide();
+ }
}
void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
@@ -1008,7 +1012,7 @@
mAdjustImeAmount = 0f;
mAdjustDividerAmount = 0f;
updateAdjustedBounds();
- mService.setResizeDimLayer(false, mStackId, 1.0f);
+ mService.setResizeDimLayer(false, getWindowingMode(), 1.0f);
} else {
mImeGoingAway |= mAdjustedForIme;
}
@@ -1204,7 +1208,7 @@
if (mAdjustedForIme && adjust && !isImeTarget) {
final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount)
* IME_ADJUST_DIM_AMOUNT;
- mService.setResizeDimLayer(true, mStackId, alpha);
+ mService.setResizeDimLayer(true, getWindowingMode(), alpha);
}
}
@@ -1283,7 +1287,7 @@
@Override
public boolean dimFullscreen() {
- return StackId.isHomeOrRecentsStack(mStackId) || fillsParent();
+ return !isActivityTypeStandard() || fillsParent();
}
@Override
@@ -1663,7 +1667,15 @@
@Override
int getOrientation() {
- return (StackId.canSpecifyOrientation(mStackId))
- ? super.getOrientation() : SCREEN_ORIENTATION_UNSET;
+ return (canSpecifyOrientation()) ? super.getOrientation() : SCREEN_ORIENTATION_UNSET;
+ }
+
+ private boolean canSpecifyOrientation() {
+ final int windowingMode = getWindowingMode();
+ final int activityType = getActivityType();
+ return windowingMode == WINDOWING_MODE_FULLSCREEN
+ || activityType == ACTIVITY_TYPE_HOME
+ || activityType == ACTIVITY_TYPE_RECENTS
+ || activityType == ACTIVITY_TYPE_ASSISTANT;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index 857b13d..7caf2fe 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -21,11 +21,8 @@
import java.util.ArrayDeque;
import java.util.function.Consumer;
-import static android.app.ActivityManager.StackId;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
@@ -187,12 +184,13 @@
}
}
- final int stackId = w.getAppToken() != null ? w.getStackId() : INVALID_STACK_ID;
- if (stackId == PINNED_STACK_ID) {
+ final int windowingMode = w.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_PINNED) {
mPinnedWindows.add(w);
- } else if (stackId == DOCKED_STACK_ID) {
+ } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
mDockedWindows.add(w);
- } else if (stackId == ASSISTANT_STACK_ID) {
+ }
+ if (w.isActivityTypeAssistant()) {
mAssistantWindows.add(w);
}
}
@@ -268,20 +266,8 @@
w.mLayer = layer;
w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment()
+ w.getSpecialWindowAnimLayerAdjustment();
- if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0) {
- if (w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
- w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
- }
- // TODO(b/62029108): the entire contents of the if statement should call the refactored
- // function to set the thumbnail layer for w.AppToken
- int highestLayer = w.mAppToken.getHighestAnimLayer();
- if (highestLayer > 0) {
- if (w.mAppToken.mAppAnimator.thumbnail != null
- && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer != highestLayer) {
- w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = highestLayer;
- w.mAppToken.mAppAnimator.thumbnail.setLayer(highestLayer + 1);
- }
- }
+ if (w.mAppToken != null) {
+ w.mAppToken.mAppAnimator.updateThumbnailLayer();
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6a5f6fa..1fb2188 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2043,10 +2043,14 @@
Slog.i(TAG_WM, "Relayout " + win + ": oldVis=" + oldVisibility
+ " newVis=" + viewVisibility, stack);
}
- if (viewVisibility == View.VISIBLE &&
- (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
- || !win.mAppToken.isClientHidden())) {
+ // We should only relayout if the view is visible, it is a starting window, or the
+ // associated appToken is not hidden.
+ final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
+ (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
+ || !win.mAppToken.isClientHidden());
+
+ if (shouldRelayout) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
// We are about to create a surface, but we didn't run a layout yet. So better run
@@ -2193,8 +2197,18 @@
// and needs process it before handling the corresponding window frame. the variable
// {@code mergedConfiguration} is an out parameter that will be passed back to the
// client over IPC and checked there.
- win.getMergedConfiguration(mergedConfiguration);
- win.setReportedConfiguration(mergedConfiguration);
+ // Note: in the cases where the window is tied to an activity, we should not send a
+ // configuration update when the window has requested to be hidden. Doing so can lead
+ // to the client erroneously accepting a configuration that would have otherwise caused
+ // an activity restart. We instead hand back the last reported
+ // {@link MergedConfiguration}.
+ if (shouldRelayout) {
+ win.getMergedConfiguration(mergedConfiguration);
+ } else {
+ win.getLastReportedMergedConfiguration(mergedConfiguration);
+ }
+
+ win.setLastReportedMergedConfiguration(mergedConfiguration);
outFrame.set(win.mCompatFrame);
outOverscanInsets.set(win.mOverscanInsets);
@@ -2895,9 +2909,9 @@
}
@Override
- public void getStackBounds(int stackId, Rect bounds) {
+ public void getStackBounds(int windowingMode, int activityType, Rect bounds) {
synchronized (mWindowMap) {
- final TaskStack stack = mRoot.getStackById(stackId);
+ final TaskStack stack = mRoot.getStack(windowingMode, activityType);
if (stack != null) {
stack.getBounds(bounds);
return;
@@ -7141,10 +7155,10 @@
}
@Override
- public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
+ public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
synchronized (mWindowMap) {
getDefaultDisplayContentLocked().getDockedDividerController().setResizeDimLayer(
- visible, targetStackId, alpha);
+ visible, targetWindowingMode, alpha);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8a2cb5a..4ff0f39 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -257,7 +257,7 @@
* We'll send configuration to client only if it is different from the last applied one and
* client won't perform unnecessary updates.
*/
- private final Configuration mLastReportedConfiguration = new Configuration();
+ private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration();
/**
* Actual position of the surface shown on-screen (may be modified by animation). These are
@@ -1244,7 +1244,7 @@
// this is not necessarily what the client has processed yet. Find a
// better indicator consistent with the client.
return (mOrientationChanging || (isVisible()
- && getConfiguration().orientation != mLastReportedConfiguration.orientation))
+ && getConfiguration().orientation != getLastReportedConfiguration().orientation))
&& !mSeamlesslyRotated
&& !mOrientationChangeTimedOut;
}
@@ -1758,7 +1758,7 @@
/** Returns true if last applied config was not yet requested by client. */
boolean isConfigChanged() {
- return !mLastReportedConfiguration.equals(getConfiguration());
+ return !getLastReportedConfiguration().equals(getConfiguration());
}
void onWindowReplacementTimeout() {
@@ -2312,8 +2312,16 @@
outConfiguration.setConfiguration(globalConfig, overrideConfig);
}
- void setReportedConfiguration(MergedConfiguration config) {
- mLastReportedConfiguration.setTo(config.getMergedConfiguration());
+ void setLastReportedMergedConfiguration(MergedConfiguration config) {
+ mLastReportedConfiguration.setTo(config);
+ }
+
+ void getLastReportedMergedConfiguration(MergedConfiguration config) {
+ config.setTo(mLastReportedConfiguration);
+ }
+
+ private Configuration getLastReportedConfiguration() {
+ return mLastReportedConfiguration.getMergedConfiguration();
}
void adjustStartingWindowFlags() {
@@ -2853,7 +2861,7 @@
new MergedConfiguration(mService.mRoot.getConfiguration(),
getMergedOverrideConfiguration());
- setReportedConfiguration(mergedConfiguration);
+ setLastReportedMergedConfiguration(mergedConfiguration);
if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING)
Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
@@ -3080,7 +3088,7 @@
if (task == null) {
return false;
}
- if (!StackId.isStackAffectedByDragResizing(getStackId())) {
+ if (!inSplitScreenWindowingMode()) {
return false;
}
if (mAttrs.width != MATCH_PARENT || mAttrs.height != MATCH_PARENT) {
@@ -3173,7 +3181,7 @@
pw.print(" mShowToOwnerOnly="); pw.print(mShowToOwnerOnly);
pw.print(" package="); pw.print(mAttrs.packageName);
pw.print(" appop="); pw.println(AppOpsManager.opToName(mAppOp));
- pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs);
+ pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs.toString(prefix));
pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth);
pw.print(" h="); pw.print(mRequestedHeight);
pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
@@ -3254,7 +3262,7 @@
}
pw.print(prefix); pw.print("mFullConfiguration="); pw.println(getConfiguration());
pw.print(prefix); pw.print("mLastReportedConfiguration=");
- pw.println(mLastReportedConfiguration);
+ pw.println(getLastReportedConfiguration());
}
pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
pw.print(" mShownPosition="); mShownPosition.printShortString(pw);
@@ -3314,7 +3322,7 @@
pw.print(prefix); pw.print("mOrientationChanging=");
pw.print(mOrientationChanging);
pw.print(" configOrientationChanging=");
- pw.print(mLastReportedConfiguration.orientation
+ pw.print(getLastReportedConfiguration().orientation
!= getConfiguration().orientation);
pw.print(" mAppFreezing="); pw.print(mAppFreezing);
pw.print(" mTurnOnScreen="); pw.print(mTurnOnScreen);
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 88625d3..af1fa2f 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -342,10 +342,7 @@
mTmpLayerAndToken.token = null;
handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken);
final AppWindowToken topClosingApp = mTmpLayerAndToken.token;
- final int topClosingLayer = mTmpLayerAndToken.layer;
-
- final AppWindowToken topOpeningApp = handleOpeningApps(transit,
- animLp, voiceInteraction, topClosingLayer);
+ final AppWindowToken topOpeningApp = handleOpeningApps(transit, animLp, voiceInteraction);
mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp);
@@ -387,8 +384,9 @@
}
private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp,
- boolean voiceInteraction, int topClosingLayer) {
+ boolean voiceInteraction) {
AppWindowToken topOpeningApp = null;
+ int topOpeningLayer = Integer.MIN_VALUE;
final int appsCount = mService.mOpeningApps.size();
for (int i = 0; i < appsCount; i++) {
AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
@@ -422,7 +420,6 @@
}
mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
- int topOpeningLayer = 0;
if (animLp != null) {
final int layer = wtoken.getHighestAnimLayer();
if (topOpeningApp == null || layer > topOpeningLayer) {
@@ -431,7 +428,7 @@
}
}
if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) {
- createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
+ createThumbnailAppAnimator(transit, wtoken);
}
}
return topOpeningApp;
@@ -473,7 +470,7 @@
}
}
if (mService.mAppTransition.isNextAppTransitionThumbnailDown()) {
- createThumbnailAppAnimator(transit, wtoken, 0, layerAndToken.layer);
+ createThumbnailAppAnimator(transit, wtoken);
}
}
}
@@ -666,8 +663,7 @@
}
}
- private void createThumbnailAppAnimator(int transit, AppWindowToken appToken,
- int openingLayer, int closingLayer) {
+ private void createThumbnailAppAnimator(int transit, AppWindowToken appToken) {
AppWindowAnimator openingAppAnimator = (appToken == null) ? null : appToken.mAppAnimator;
if (openingAppAnimator == null || openingAppAnimator.animation == null) {
return;
@@ -724,7 +720,6 @@
anim = mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(appRect,
insets, thumbnailHeader, taskId, displayConfig.uiMode,
displayConfig.orientation);
- openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer);
openingAppAnimator.deferThumbnailDestruction =
!mService.mAppTransition.isNextThumbnailTransitionScaleUp();
} else {
@@ -734,8 +729,8 @@
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
+ openingAppAnimator.updateThumbnailLayer();
openingAppAnimator.thumbnail = surfaceControl;
- openingAppAnimator.thumbnailLayer = openingLayer;
openingAppAnimator.thumbnailAnimation = anim;
mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
} catch (Surface.OutOfResourcesException e) {
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index bf9f941..36e9e7f 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -78,7 +78,6 @@
libschedulerservicehidl \
libsensorservice \
libsensorservicehidl \
- libskia \
libgui \
libusbhost \
libsuspend \
@@ -93,6 +92,7 @@
android.hardware.audio.common@2.0 \
android.hardware.broadcastradio@1.0 \
android.hardware.broadcastradio@1.1 \
+ android.hardware.broadcastradio@1.2 \
android.hardware.contexthub@1.0 \
android.hardware.gnss@1.0 \
android.hardware.ir@1.0 \
@@ -110,5 +110,5 @@
android.frameworks.sensorservice@1.0 \
LOCAL_STATIC_LIBRARIES += \
- android.hardware.broadcastradio@1.1-utils-lib \
+ android.hardware.broadcastradio@common-utils-lib \
libscrypt_static \
diff --git a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
index b3817db..e1dbdeb 100644
--- a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
+++ b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
@@ -23,8 +23,9 @@
#include "convert.h"
#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
-#include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
+#include <android/hardware/broadcastradio/1.2/IBroadcastRadioFactory.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <broadcastradio-utils/Utils.h>
#include <core_jni_helpers.h>
#include <hidl/ServiceManagement.h>
#include <nativehelper/JNIHelp.h>
@@ -44,14 +45,16 @@
namespace V1_0 = hardware::broadcastradio::V1_0;
namespace V1_1 = hardware::broadcastradio::V1_1;
-
-using V1_0::Class;
-using V1_0::Result;
+namespace V1_2 = hardware::broadcastradio::V1_2;
+namespace utils = hardware::broadcastradio::utils;
using V1_0::BandConfig;
-using V1_0::ProgramInfo;
-using V1_0::MetaData;
+using V1_0::Class;
using V1_0::ITuner;
+using V1_0::MetaData;
+using V1_0::ProgramInfo;
+using V1_0::Result;
+using utils::HalRevision;
static mutex gContextMutex;
@@ -67,10 +70,15 @@
} Tuner;
} gjni;
+struct Module {
+ sp<V1_0::IBroadcastRadio> radioModule;
+ HalRevision halRev;
+};
+
struct ServiceContext {
ServiceContext() {}
- std::vector<sp<V1_0::IBroadcastRadio>> mModules;
+ std::vector<Module> mModules;
private:
DISALLOW_COPY_AND_ASSIGN(ServiceContext);
@@ -139,6 +147,17 @@
continue;
}
+ auto halRev = HalRevision::V1_0;
+ auto halMinor = 0;
+ if (V1_2::IBroadcastRadioFactory::castFrom(factory).withDefault(nullptr) != nullptr) {
+ halRev = HalRevision::V1_2;
+ halMinor = 2;
+ } else if (V1_1::IBroadcastRadioFactory::castFrom(factory).withDefault(nullptr)
+ != nullptr) {
+ halRev = HalRevision::V1_1;
+ halMinor = 1;
+ }
+
// Second level of scanning - that's unfortunate.
for (auto&& clazz : gAllClasses) {
sp<V1_0::IBroadcastRadio> module10 = nullptr;
@@ -155,9 +174,9 @@
if (module10 == nullptr) continue;
auto idx = ctx.mModules.size();
- ctx.mModules.push_back(module10);
- ALOGI("loaded broadcast radio module %zu: %s:%s",
- idx, serviceName.c_str(), V1_0::toString(clazz).c_str());
+ ctx.mModules.push_back({module10, halRev});
+ ALOGI("loaded broadcast radio module %zu: %s:%s (HAL 1.%d)",
+ idx, serviceName.c_str(), V1_0::toString(clazz).c_str(), halMinor);
JavaRef<jobject> jModule = nullptr;
Result halResult = Result::OK;
@@ -198,22 +217,15 @@
ALOGE("Invalid module ID: %d", moduleId);
return nullptr;
}
- auto module = ctx.mModules[moduleId];
- HalRevision halRev;
- if (V1_1::IBroadcastRadio::castFrom(module).withDefault(nullptr) != nullptr) {
- ALOGI("Opening tuner %d with broadcast radio HAL 1.1", moduleId);
- halRev = HalRevision::V1_1;
- } else {
- ALOGI("Opening tuner %d with broadcast radio HAL 1.0", moduleId);
- halRev = HalRevision::V1_0;
- }
+ ALOGI("Opening tuner %d", moduleId);
+ auto module = ctx.mModules[moduleId];
Region region;
BandConfig bandConfigHal = convert::BandConfigToHal(env, bandConfig, region);
auto tuner = make_javaref(env, env->NewObject(gjni.Tuner.clazz, gjni.Tuner.cstor,
- callback, halRev, region, withAudio, bandConfigHal.type));
+ callback, module.halRev, region, withAudio, bandConfigHal.type));
if (tuner == nullptr) {
ALOGE("Unable to create new tuner object.");
return nullptr;
@@ -223,7 +235,7 @@
Result halResult;
sp<ITuner> halTuner = nullptr;
- auto hidlResult = module->openTuner(bandConfigHal, withAudio, tunerCb,
+ auto hidlResult = module.radioModule->openTuner(bandConfigHal, withAudio, tunerCb,
[&](Result result, const sp<ITuner>& tuner) {
halResult = result;
halTuner = tuner;
@@ -235,7 +247,7 @@
return nullptr;
}
- Tuner::assignHalInterfaces(env, tuner, module, halTuner);
+ Tuner::assignHalInterfaces(env, tuner, module.radioModule, halTuner);
ALOGD("Opened tuner %p", halTuner.get());
return tuner.release();
}
diff --git a/services/core/jni/BroadcastRadio/Tuner.cpp b/services/core/jni/BroadcastRadio/Tuner.cpp
index e1ade4d..6403a5a 100644
--- a/services/core/jni/BroadcastRadio/Tuner.cpp
+++ b/services/core/jni/BroadcastRadio/Tuner.cpp
@@ -22,7 +22,7 @@
#include "convert.h"
#include "TunerCallback.h"
-#include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
+#include <android/hardware/broadcastradio/1.2/IBroadcastRadioFactory.h>
#include <binder/IPCThreadState.h>
#include <broadcastradio-utils/Utils.h>
#include <core_jni_helpers.h>
@@ -44,13 +44,16 @@
namespace V1_0 = hardware::broadcastradio::V1_0;
namespace V1_1 = hardware::broadcastradio::V1_1;
+namespace V1_2 = hardware::broadcastradio::V1_2;
+namespace utils = hardware::broadcastradio::utils;
using V1_0::Band;
using V1_0::BandConfig;
using V1_0::MetaData;
using V1_0::Result;
-using V1_1::ITunerCallback;
+using V1_2::ITunerCallback;
using V1_1::ProgramListResult;
+using utils::HalRevision;
static mutex gContextMutex;
@@ -310,7 +313,7 @@
convert::ThrowIfFailed(env, halTuner11->tuneByProgramSelector(selector));
} else {
uint32_t channel, subChannel;
- if (!V1_1::utils::getLegacyChannel(selector, &channel, &subChannel)) {
+ if (!utils::getLegacyChannel(selector, &channel, &subChannel)) {
jniThrowException(env, "java/lang/IllegalArgumentException",
"Can't tune to non-AM/FM channel with HAL<1.1");
return;
diff --git a/services/core/jni/BroadcastRadio/Tuner.h b/services/core/jni/BroadcastRadio/Tuner.h
index 818597b..48c3bc7 100644
--- a/services/core/jni/BroadcastRadio/Tuner.h
+++ b/services/core/jni/BroadcastRadio/Tuner.h
@@ -22,8 +22,8 @@
#include "JavaRef.h"
#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
-#include <android/hardware/broadcastradio/1.1/ITuner.h>
-#include <android/hardware/broadcastradio/1.1/ITunerCallback.h>
+#include <android/hardware/broadcastradio/1.2/ITuner.h>
+#include <android/hardware/broadcastradio/1.2/ITunerCallback.h>
#include <jni.h>
#include <utils/StrongPointer.h>
@@ -39,7 +39,7 @@
sp<hardware::broadcastradio::V1_0::IBroadcastRadio> halModule,
sp<hardware::broadcastradio::V1_0::ITuner> halTuner);
-sp<hardware::broadcastradio::V1_1::ITunerCallback>
+sp<hardware::broadcastradio::V1_2::ITunerCallback>
getNativeCallback(JNIEnv *env, JavaRef<jobject> const &tuner);
Region getRegion(JNIEnv *env, jobject obj);
diff --git a/services/core/jni/BroadcastRadio/TunerCallback.cpp b/services/core/jni/BroadcastRadio/TunerCallback.cpp
index d53721f..ed7c9c4 100644
--- a/services/core/jni/BroadcastRadio/TunerCallback.cpp
+++ b/services/core/jni/BroadcastRadio/TunerCallback.cpp
@@ -40,15 +40,19 @@
namespace V1_0 = hardware::broadcastradio::V1_0;
namespace V1_1 = hardware::broadcastradio::V1_1;
+namespace V1_2 = hardware::broadcastradio::V1_2;
+namespace utils = hardware::broadcastradio::utils;
using V1_0::Band;
using V1_0::BandConfig;
using V1_0::MetaData;
using V1_0::Result;
-using V1_1::ITunerCallback;
using V1_1::ProgramInfo;
using V1_1::ProgramListResult;
using V1_1::ProgramSelector;
+using V1_1::VendorKeyValue;
+using V1_2::ITunerCallback;
+using utils::HalRevision;
static JavaVM *gvm = nullptr;
@@ -117,6 +121,7 @@
virtual Return<void> backgroundScanComplete(ProgramListResult result);
virtual Return<void> programListChanged();
virtual Return<void> currentProgramInfoChanged(const ProgramInfo& info);
+ virtual Return<void> parametersUpdated(const hidl_vec<VendorKeyValue>& parameters);
};
struct TunerCallbackContext {
@@ -203,7 +208,7 @@
return {};
}
- auto selector = V1_1::utils::make_selector(mBand, info.channel, info.subChannel);
+ auto selector = utils::make_selector(mBand, info.channel, info.subChannel);
return tuneComplete_1_1(result, selector);
}
@@ -338,6 +343,14 @@
return Return<void>();
}
+Return<void> NativeCallback::parametersUpdated(const hidl_vec<VendorKeyValue>& parameters) {
+ ALOGV("%s", __func__);
+
+ // TODO(b/65862441): pass this callback to the front-end
+
+ return {};
+}
+
static TunerCallbackContext& getNativeContext(jlong nativeContextHandle) {
auto nativeContext = reinterpret_cast<TunerCallbackContext*>(nativeContextHandle);
LOG_ALWAYS_FATAL_IF(nativeContext == nullptr, "Native context not initialized");
diff --git a/services/core/jni/BroadcastRadio/TunerCallback.h b/services/core/jni/BroadcastRadio/TunerCallback.h
index af12d21..7e776c2 100644
--- a/services/core/jni/BroadcastRadio/TunerCallback.h
+++ b/services/core/jni/BroadcastRadio/TunerCallback.h
@@ -21,7 +21,7 @@
#include "NativeCallbackThread.h"
#include "types.h"
-#include <android/hardware/broadcastradio/1.1/ITunerCallback.h>
+#include <android/hardware/broadcastradio/1.2/ITunerCallback.h>
#include <jni.h>
namespace android {
@@ -32,7 +32,7 @@
namespace BroadcastRadio {
namespace TunerCallback {
-sp<hardware::broadcastradio::V1_1::ITunerCallback>
+sp<hardware::broadcastradio::V1_2::ITunerCallback>
getNativeCallback(JNIEnv *env, jobject jTunerCallback);
} // namespace TunerCallback
diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index ae278de..8dfa14f 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -31,7 +31,7 @@
namespace BroadcastRadio {
namespace convert {
-namespace utils = V1_1::utils;
+namespace utils = hardware::broadcastradio::utils;
using hardware::Return;
using hardware::hidl_vec;
diff --git a/services/core/jni/BroadcastRadio/regions.cpp b/services/core/jni/BroadcastRadio/regions.cpp
index b856419..b7fd0f3 100644
--- a/services/core/jni/BroadcastRadio/regions.cpp
+++ b/services/core/jni/BroadcastRadio/regions.cpp
@@ -27,7 +27,7 @@
namespace BroadcastRadio {
namespace regions {
-namespace utils = hardware::broadcastradio::V1_1::utils;
+namespace utils = hardware::broadcastradio::utils;
using hardware::hidl_vec;
diff --git a/services/core/jni/BroadcastRadio/types.h b/services/core/jni/BroadcastRadio/types.h
index f726af3..64a4f63 100644
--- a/services/core/jni/BroadcastRadio/types.h
+++ b/services/core/jni/BroadcastRadio/types.h
@@ -27,11 +27,6 @@
* frameworks/base/core/java/android/hardware/radio/RadioManager.java.
*/
-enum class HalRevision : jint {
- V1_0,
- V1_1,
-};
-
// Keep in sync with STATUS_* constants from RadioManager.java.
enum class Status : jint {
OK = 0,
diff --git a/services/core/jni/com_android_server_UsbDescriptorParser.cpp b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
index 98c5ec1..df85e31 100644
--- a/services/core/jni/com_android_server_UsbDescriptorParser.cpp
+++ b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
@@ -18,7 +18,7 @@
#include "utils/Log.h"
#include "jni.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include <usbhost/usbhost.h>
diff --git a/services/core/jni/com_android_server_locksettings_SyntheticPasswordManager.cpp b/services/core/jni/com_android_server_locksettings_SyntheticPasswordManager.cpp
index 248dedb..bc13fde 100644
--- a/services/core/jni/com_android_server_locksettings_SyntheticPasswordManager.cpp
+++ b/services/core/jni/com_android_server_locksettings_SyntheticPasswordManager.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "SyntheticPasswordManager"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include "jni.h"
#include <android_runtime/Log.h>
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 39f90ca..b6c3df70 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -19,7 +19,7 @@
//#define LOG_NDEBUG 0
#include <android/hardware/power/1.1/IPower.h>
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include "jni.h"
#include <nativehelper/ScopedUtfChars.h>
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6c859f7..c59f44e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -97,8 +97,8 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
@@ -5350,7 +5350,7 @@
}
}
- private void forceWipeUser(int userId) {
+ private void forceWipeUser(int userId, String wipeReasonForUser) {
try {
IActivityManager am = mInjector.getIActivityManager();
if (am.getCurrentUser().id == userId) {
@@ -5361,7 +5361,7 @@
if (!userRemoved) {
Slog.w(LOG_TAG, "Couldn't remove user " + userId);
} else if (isManagedProfile(userId)) {
- sendWipeProfileNotification();
+ sendWipeProfileNotification(wipeReasonForUser);
}
} catch (RemoteException re) {
// Shouldn't happen
@@ -5369,23 +5369,26 @@
}
@Override
- public void wipeData(int flags) {
+ public void wipeDataWithReason(int flags, String wipeReasonForUser) {
if (!mHasFeature) {
return;
}
+ Preconditions.checkStringNotEmpty(wipeReasonForUser, "wipeReasonForUser is null or empty");
enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId());
final ActiveAdmin admin;
synchronized (this) {
admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA);
}
- String reason = "DevicePolicyManager.wipeData() from "
+ String internalReason = "DevicePolicyManager.wipeDataWithReason() from "
+ admin.info.getComponent().flattenToShortString();
wipeDataNoLock(
- admin.info.getComponent(), flags, reason, admin.getUserHandle().getIdentifier());
+ admin.info.getComponent(), flags, internalReason, wipeReasonForUser,
+ admin.getUserHandle().getIdentifier());
}
- private void wipeDataNoLock(ComponentName admin, int flags, String reason, int userId) {
+ private void wipeDataNoLock(ComponentName admin, int flags, String internalReason,
+ String wipeReasonForUser, int userId) {
wtfIfInLock();
long ident = mInjector.binderClearCallingIdentity();
@@ -5420,25 +5423,26 @@
// (rather than system), we should probably trigger factory reset. Current code just
// removes that user (but still clears FRP...)
if (userId == UserHandle.USER_SYSTEM) {
- forceWipeDeviceNoLock(/*wipeExtRequested=*/ (flags & WIPE_EXTERNAL_STORAGE) != 0,
- reason, /*wipeEuicc=*/ (flags & WIPE_EUICC) != 0);
+ forceWipeDeviceNoLock(/*wipeExtRequested=*/ (
+ flags & WIPE_EXTERNAL_STORAGE) != 0,
+ internalReason,
+ /*wipeEuicc=*/ (flags & WIPE_EUICC) != 0);
} else {
- forceWipeUser(userId);
+ forceWipeUser(userId, wipeReasonForUser);
}
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
}
- private void sendWipeProfileNotification() {
- String contentText = mContext.getString(R.string.work_profile_deleted_description_dpm_wipe);
+ private void sendWipeProfileNotification(String wipeReasonForUser) {
Notification notification =
new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
.setSmallIcon(android.R.drawable.stat_sys_warning)
.setContentTitle(mContext.getString(R.string.work_profile_deleted))
- .setContentText(contentText)
+ .setContentText(wipeReasonForUser)
.setColor(mContext.getColor(R.color.system_notification_accent_color))
- .setStyle(new Notification.BigTextStyle().bigText(contentText))
+ .setStyle(new Notification.BigTextStyle().bigText(wipeReasonForUser))
.build();
mInjector.getNotificationManager().notify(SystemMessage.NOTE_PROFILE_WIPED, notification);
}
@@ -5610,9 +5614,12 @@
// able to do so).
// IMPORTANT: Call without holding the lock to prevent deadlock.
try {
+ String wipeReasonForUser = mContext.getString(
+ R.string.work_profile_deleted_reason_maximum_password_failure);
wipeDataNoLock(strictestAdmin.info.getComponent(),
/*flags=*/ 0,
/*reason=*/ "reportFailedPasswordAttempt()",
+ wipeReasonForUser,
userId);
} catch (SecurityException e) {
Slog.w(LOG_TAG, "Failed to wipe user " + userId
@@ -5621,7 +5628,8 @@
}
if (mInjector.securityLogIsLoggingEnabled()) {
- SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0,
+ SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT,
+ /*result*/ 0,
/*method strength*/ 1);
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 948c028..92cbd3d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -152,7 +152,7 @@
* them from the build system somehow.
*/
private static final String BACKUP_MANAGER_SERVICE_CLASS =
- "com.android.server.backup.BackupManagerService$Lifecycle";
+ "com.android.server.backup.RefactoredBackupManagerService$Lifecycle";
private static final String APPWIDGET_SERVICE_CLASS =
"com.android.server.appwidget.AppWidgetService";
private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS =
@@ -668,9 +668,11 @@
traceEnd();
// Tracks whether the updatable WebView is in a ready state and watches for update installs.
- traceBeginAndSlog("StartWebViewUpdateService");
- mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
- traceEnd();
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
+ traceBeginAndSlog("StartWebViewUpdateService");
+ mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
+ traceEnd();
+ }
}
/**
@@ -682,6 +684,7 @@
VibratorService vibrator = null;
IStorageManager storageManager = null;
NetworkManagementService networkManagement = null;
+ IpSecService ipSecService = null;
NetworkStatsService networkStats = null;
NetworkPolicyManagerService networkPolicy = null;
ConnectivityService connectivity = null;
@@ -1031,6 +1034,15 @@
reportWtf("starting NetworkManagement Service", e);
}
traceEnd();
+
+ traceBeginAndSlog("StartIpSecService");
+ try {
+ ipSecService = IpSecService.create(context);
+ ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService);
+ } catch (Throwable e) {
+ reportWtf("starting IpSec Service", e);
+ }
+ traceEnd();
}
if (!disableNonCoreServices && !disableTextServices) {
@@ -1081,6 +1093,14 @@
traceBeginAndSlog("StartWifiRtt");
mSystemServiceManager.startService("com.android.server.wifi.RttService");
traceEnd();
+
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_RTT)) {
+ traceBeginAndSlog("StartRttService");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.rtt.RttService");
+ traceEnd();
+ }
}
if (context.getPackageManager().hasSystemFeature(
@@ -1088,8 +1108,6 @@
traceBeginAndSlog("StartWifiAware");
mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
traceEnd();
- } else {
- Slog.i(TAG, "No Wi-Fi Aware Service (Aware support Not Present)");
}
if (context.getPackageManager().hasSystemFeature(
@@ -1654,6 +1672,7 @@
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
final MediaRouterService mediaRouterF = mediaRouter;
final MmsServiceBroker mmsServiceF = mmsService;
+ final IpSecService ipSecServiceF = ipSecService;
final WindowManagerService windowManagerF = wm;
// We now tell the activity manager it is okay to run third party
@@ -1676,10 +1695,10 @@
traceEnd();
// No dependency on Webview preparation in system server. But this should
- // be completed before allowring 3rd party
+ // be completed before allowing 3rd party
final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation";
Future<?> webviewPrep = null;
- if (!mOnlyCore) {
+ if (!mOnlyCore && mWebViewUpdateService != null) {
webviewPrep = SystemServerInitThreadPool.get().submit(() -> {
Slog.i(TAG, WEBVIEW_PREPARATION);
TimingsTraceLog traceLog = new TimingsTraceLog(
@@ -1724,6 +1743,13 @@
.networkScoreAndNetworkManagementServiceReady();
}
traceEnd();
+ traceBeginAndSlog("MakeIpSecServiceReady");
+ try {
+ if (ipSecServiceF != null) ipSecServiceF.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making IpSec Service ready", e);
+ }
+ traceEnd();
traceBeginAndSlog("MakeNetworkStatsServiceReady");
try {
if (networkStatsF != null) networkStatsF.systemReady();
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
index 884a8a7..0230f36 100644
--- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java
+++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
@@ -61,11 +61,11 @@
private static final String MARK_STOP = "--- STOP ---";
private final String mTag;
- private final Handler mHandler;
private final LocalLog mLog;
private final BlockingSocketReader mPacketListener;
+ private boolean mRunning;
- public ConnectivityPacketTracker(NetworkInterface netif, LocalLog log) {
+ public ConnectivityPacketTracker(Handler h, NetworkInterface netif, LocalLog log) {
final String ifname;
final int ifindex;
final byte[] hwaddr;
@@ -81,44 +81,40 @@
}
mTag = TAG + "." + ifname;
- mHandler = new Handler();
mLog = log;
- mPacketListener = new PacketListener(ifindex, hwaddr, mtu);
+ mPacketListener = new PacketListener(h, ifindex, hwaddr, mtu);
}
public void start() {
- mLog.log(MARK_START);
+ mRunning = true;
mPacketListener.start();
}
public void stop() {
mPacketListener.stop();
- mLog.log(MARK_STOP);
+ mRunning = false;
}
private final class PacketListener extends BlockingSocketReader {
private final int mIfIndex;
private final byte mHwAddr[];
- PacketListener(int ifindex, byte[] hwaddr, int mtu) {
- super(mtu);
+ PacketListener(Handler h, int ifindex, byte[] hwaddr, int mtu) {
+ super(h, mtu);
mIfIndex = ifindex;
mHwAddr = hwaddr;
}
@Override
- protected FileDescriptor createSocket() {
+ protected FileDescriptor createFd() {
FileDescriptor s = null;
try {
- // TODO: Evaluate switching to SOCK_DGRAM and changing the
- // BlockingSocketReader's read() to recvfrom(), so that this
- // might work on non-ethernet-like links (via SLL).
s = Os.socket(AF_PACKET, SOCK_RAW, 0);
NetworkUtils.attachControlPacketFilter(s, ARPHRD_ETHER);
Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mIfIndex));
} catch (ErrnoException | IOException e) {
logError("Failed to create packet tracking socket: ", e);
- closeSocket(s);
+ closeFd(s);
return null;
}
return s;
@@ -136,13 +132,27 @@
}
@Override
+ protected void onStart() {
+ mLog.log(MARK_START);
+ }
+
+ @Override
+ protected void onStop() {
+ if (mRunning) {
+ mLog.log(MARK_STOP);
+ } else {
+ mLog.log(MARK_STOP + " (packet listener stopped unexpectedly)");
+ }
+ }
+
+ @Override
protected void logError(String msg, Exception e) {
Log.e(mTag, msg, e);
addLogEntry(msg + e);
}
private void addLogEntry(String entry) {
- mHandler.post(() -> mLog.log(entry));
+ mLog.log(entry);
}
}
}
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index b1eb085..bc07b81 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -1515,7 +1515,8 @@
private ConnectivityPacketTracker createPacketTracker() {
try {
- return new ConnectivityPacketTracker(mNetworkInterface, mConnectivityPacketLog);
+ return new ConnectivityPacketTracker(
+ getHandler(), mNetworkInterface, mConnectivityPacketLog);
} catch (IllegalArgumentException e) {
return null;
}
diff --git a/services/net/java/android/net/util/BlockingSocketReader.java b/services/net/java/android/net/util/BlockingSocketReader.java
index 12fa1e5..99bf469 100644
--- a/services/net/java/android/net/util/BlockingSocketReader.java
+++ b/services/net/java/android/net/util/BlockingSocketReader.java
@@ -16,81 +16,106 @@
package android.net.util;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
+
import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.MessageQueue;
+import android.os.MessageQueue.OnFileDescriptorEventListener;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
-import libcore.io.IoBridge;
+import libcore.io.IoUtils;
import java.io.FileDescriptor;
-import java.io.InterruptedIOException;
import java.io.IOException;
/**
- * A thread that reads from a socket and passes the received packets to a
- * subclass's handlePacket() method. The packet receive buffer is recycled
- * on every read call, so subclasses should make any copies they would like
- * inside their handlePacket() implementation.
+ * This class encapsulates the mechanics of registering a file descriptor
+ * with a thread's Looper and handling read events (and errors).
*
- * All public methods may be called from any thread.
+ * Subclasses MUST implement createFd() and SHOULD override handlePacket().
+
+ * Subclasses can expect a call life-cycle like the following:
+ *
+ * [1] start() calls createFd() and (if all goes well) onStart()
+ *
+ * [2] yield, waiting for read event or error notification:
+ *
+ * [a] readPacket() && handlePacket()
+ *
+ * [b] if (no error):
+ * goto 2
+ * else:
+ * goto 3
+ *
+ * [3] stop() calls onStop() if not previously stopped
+ *
+ * The packet receive buffer is recycled on every read call, so subclasses
+ * should make any copies they would like inside their handlePacket()
+ * implementation.
+ *
+ * All public methods MUST only be called from the same thread with which
+ * the Handler constructor argument is associated.
+ *
+ * TODO: rename this class to something more correctly descriptive (something
+ * like [or less horrible than] FdReadEventsHandler?).
*
* @hide
*/
public abstract class BlockingSocketReader {
+ private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
+ private static final int UNREGISTER_THIS_FD = 0;
+
public static final int DEFAULT_RECV_BUF_SIZE = 2 * 1024;
+ private final Handler mHandler;
+ private final MessageQueue mQueue;
private final byte[] mPacket;
- private final Thread mThread;
- private volatile FileDescriptor mSocket;
- private volatile boolean mRunning;
- private volatile long mPacketsReceived;
+ private FileDescriptor mFd;
+ private long mPacketsReceived;
- // Make it slightly easier for subclasses to properly close a socket
- // without having to know this incantation.
- public static final void closeSocket(@Nullable FileDescriptor fd) {
- try {
- IoBridge.closeAndSignalBlockedThreads(fd);
- } catch (IOException ignored) {}
+ protected static void closeFd(FileDescriptor fd) {
+ IoUtils.closeQuietly(fd);
}
- protected BlockingSocketReader() {
- this(DEFAULT_RECV_BUF_SIZE);
+ protected BlockingSocketReader(Handler h) {
+ this(h, DEFAULT_RECV_BUF_SIZE);
}
- protected BlockingSocketReader(int recvbufsize) {
- if (recvbufsize < DEFAULT_RECV_BUF_SIZE) {
- recvbufsize = DEFAULT_RECV_BUF_SIZE;
+ protected BlockingSocketReader(Handler h, int recvbufsize) {
+ mHandler = h;
+ mQueue = mHandler.getLooper().getQueue();
+ mPacket = new byte[Math.max(recvbufsize, DEFAULT_RECV_BUF_SIZE)];
+ }
+
+ public final void start() {
+ if (onCorrectThread()) {
+ createAndRegisterFd();
+ } else {
+ mHandler.post(() -> {
+ logError("start() called from off-thread", null);
+ createAndRegisterFd();
+ });
}
- mPacket = new byte[recvbufsize];
- mThread = new Thread(() -> { mainLoop(); });
- }
-
- public final boolean start() {
- if (mSocket != null) return false;
-
- try {
- mSocket = createSocket();
- } catch (Exception e) {
- logError("Failed to create socket: ", e);
- return false;
- }
-
- if (mSocket == null) return false;
-
- mRunning = true;
- mThread.start();
- return true;
}
public final void stop() {
- mRunning = false;
- closeSocket(mSocket);
- mSocket = null;
+ if (onCorrectThread()) {
+ unregisterAndDestroyFd();
+ } else {
+ mHandler.post(() -> {
+ logError("stop() called from off-thread", null);
+ unregisterAndDestroyFd();
+ });
+ }
}
- public final boolean isRunning() { return mRunning; }
+ public final int recvBufSize() { return mPacket.length; }
public final long numPacketsReceived() { return mPacketsReceived; }
@@ -98,11 +123,21 @@
* Subclasses MUST create the listening socket here, including setting
* all desired socket options, interface or address/port binding, etc.
*/
- protected abstract FileDescriptor createSocket();
+ protected abstract FileDescriptor createFd();
+
+ /**
+ * Subclasses MAY override this to change the default read() implementation
+ * in favour of, say, recvfrom().
+ *
+ * Implementations MUST return the bytes read or throw an Exception.
+ */
+ protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception {
+ return Os.read(fd, packetBuffer, 0, packetBuffer.length);
+ }
/**
* Called by the main loop for every packet. Any desired copies of
- * |recvbuf| should be made in here, and the underlying byte array is
+ * |recvbuf| should be made in here, as the underlying byte array is
* reused across all reads.
*/
protected void handlePacket(byte[] recvbuf, int length) {}
@@ -113,43 +148,102 @@
protected void logError(String msg, Exception e) {}
/**
- * Called by the main loop just prior to exiting.
+ * Called by start(), if successful, just prior to returning.
*/
- protected void onExit() {}
+ protected void onStart() {}
- private final void mainLoop() {
+ /**
+ * Called by stop() just prior to returning.
+ */
+ protected void onStop() {}
+
+ private void createAndRegisterFd() {
+ if (mFd != null) return;
+
+ try {
+ mFd = createFd();
+ if (mFd != null) {
+ // Force the socket to be non-blocking.
+ IoUtils.setBlocking(mFd, false);
+ }
+ } catch (Exception e) {
+ logError("Failed to create socket: ", e);
+ closeFd(mFd);
+ mFd = null;
+ return;
+ }
+
+ if (mFd == null) return;
+
+ mQueue.addOnFileDescriptorEventListener(
+ mFd,
+ FD_EVENTS,
+ new OnFileDescriptorEventListener() {
+ @Override
+ public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+ // Always call handleInput() so read/recvfrom are given
+ // a proper chance to encounter a meaningful errno and
+ // perhaps log a useful error message.
+ if (!isRunning() || !handleInput()) {
+ unregisterAndDestroyFd();
+ return UNREGISTER_THIS_FD;
+ }
+ return FD_EVENTS;
+ }
+ });
+ onStart();
+ }
+
+ private boolean isRunning() { return (mFd != null) && mFd.valid(); }
+
+ // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error.
+ private boolean handleInput() {
while (isRunning()) {
final int bytesRead;
try {
- // Blocking read.
- // TODO: See if this can be converted to recvfrom.
- bytesRead = Os.read(mSocket, mPacket, 0, mPacket.length);
+ bytesRead = readPacket(mFd, mPacket);
if (bytesRead < 1) {
if (isRunning()) logError("Socket closed, exiting", null);
break;
}
mPacketsReceived++;
} catch (ErrnoException e) {
- if (e.errno != OsConstants.EINTR) {
- if (isRunning()) logError("read error: ", e);
+ if (e.errno == OsConstants.EAGAIN) {
+ // We've read everything there is to read this time around.
+ return true;
+ } else if (e.errno == OsConstants.EINTR) {
+ continue;
+ } else {
+ if (isRunning()) logError("readPacket error: ", e);
break;
}
- continue;
- } catch (IOException ioe) {
- if (isRunning()) logError("read error: ", ioe);
- continue;
+ } catch (Exception e) {
+ if (isRunning()) logError("readPacket error: ", e);
+ break;
}
try {
handlePacket(mPacket, bytesRead);
} catch (Exception e) {
- logError("Unexpected exception: ", e);
+ logError("handlePacket error: ", e);
break;
}
}
- stop();
- onExit();
+ return false;
+ }
+
+ private void unregisterAndDestroyFd() {
+ if (mFd == null) return;
+
+ mQueue.removeOnFileDescriptorEventListener(mFd);
+ closeFd(mFd);
+ mFd = null;
+ onStop();
+ }
+
+ private boolean onCorrectThread() {
+ return (mHandler.getLooper() == Looper.myLooper());
}
}
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index 6065268..5a3a8be 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -107,6 +107,20 @@
public static final int RFC6177_MIN_PREFIX_LENGTH = 48;
/**
+ * ICMP common (v4/v6) constants.
+ *
+ * See also:
+ * - https://tools.ietf.org/html/rfc792
+ * - https://tools.ietf.org/html/rfc4443
+ */
+ public static final int ICMP_HEADER_TYPE_OFFSET = 0;
+ public static final int ICMP_HEADER_CODE_OFFSET = 1;
+ public static final int ICMP_HEADER_CHECKSUM_OFFSET = 2;
+ public static final int ICMP_ECHO_IDENTIFIER_OFFSET = 4;
+ public static final int ICMP_ECHO_SEQUENCE_NUMBER_OFFSET = 6;
+ public static final int ICMP_ECHO_DATA_OFFSET = 8;
+
+ /**
* ICMPv6 constants.
*
* See also:
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 529ac3a..9fa1d68 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -558,6 +558,7 @@
// the phone is quiet
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
mService.buzzBeepBlinkLocked(r);
@@ -568,6 +569,22 @@
}
@Test
+ public void testNoDemoteSoundToVibrateIfNonNotificationStream() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ assertTrue(r.getSound() != null);
+ assertNull(r.getVibration());
+
+ // the phone is quiet
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(1);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverVibrate();
+ verifyBeepLooped();
+ }
+
+ @Test
public void testDemoteSoundToVibrate() throws Exception {
NotificationRecord r = getBeepyNotification();
assertTrue(r.getSound() != null);
@@ -575,6 +592,7 @@
// the phone is quiet
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
mService.buzzBeepBlinkLocked(r);
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index ddd21df..71a024c 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -16,12 +16,15 @@
package com.android.server.notification;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -56,6 +59,7 @@
import android.graphics.Color;
import android.media.AudioManager;
import android.os.Binder;
+import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings.Secure;
@@ -241,6 +245,7 @@
nb.build(), new UserHandle(mUid), null, 0);
return new NotificationRecord(mContext, sbn, channel);
}
+
private NotificationRecord generateNotificationRecord(NotificationChannel channel) {
return generateNotificationRecord(channel, null);
}
@@ -342,7 +347,7 @@
// Recreating the channel doesn't throw, but ignores importance.
final NotificationChannel dupeChannel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_HIGH);
+ new NotificationChannel("id", "name", IMPORTANCE_HIGH);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(dupeChannel)));
final NotificationChannel createdChannel =
@@ -378,7 +383,7 @@
// The user modifies importance directly, can no longer be changed by the app.
final NotificationChannel updatedChannel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_HIGH);
+ new NotificationChannel("id", "name", IMPORTANCE_HIGH);
mBinderService.updateNotificationChannelForPackage(PKG, mUid, updatedChannel);
// Recreating with a lower importance leaves channel unchanged.
@@ -388,7 +393,7 @@
new ParceledListSlice(Arrays.asList(dupeChannel)));
final NotificationChannel createdChannel =
mBinderService.getNotificationChannel(PKG, "id");
- assertEquals(NotificationManager.IMPORTANCE_HIGH, createdChannel.getImportance());
+ assertEquals(IMPORTANCE_HIGH, createdChannel.getImportance());
}
@Test
@@ -397,7 +402,7 @@
final NotificationChannel channel1 =
new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
final NotificationChannel channel2 =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_HIGH);
+ new NotificationChannel("id", "name", IMPORTANCE_HIGH);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(channel1, channel2)));
final NotificationChannel createdChannel =
@@ -410,7 +415,7 @@
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(true);
NotificationChannel channel = new NotificationChannel("id", "name",
- NotificationManager.IMPORTANCE_HIGH);
+ IMPORTANCE_HIGH);
NotificationRecord r = generateNotificationRecord(channel);
assertTrue(mNotificationManagerService.isBlocked(r, mUsageStats));
verify(mUsageStats, times(1)).registerSuspendedByAdmin(eq(r));
@@ -421,11 +426,68 @@
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
NotificationChannel channel = new NotificationChannel("id", "name",
- NotificationManager.IMPORTANCE_HIGH);
- channel.setImportance(IMPORTANCE_NONE);
+ NotificationManager.IMPORTANCE_NONE);
NotificationRecord r = generateNotificationRecord(channel);
assertTrue(mNotificationManagerService.isBlocked(r, mUsageStats));
verify(mUsageStats, times(1)).registerBlocked(eq(r));
+
+ mBinderService.createNotificationChannels(
+ PKG, new ParceledListSlice(Arrays.asList(channel)));
+ final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ waitForIdle();
+ assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
+ }
+
+ @Test
+ public void testEnqueuedBlockedNotifications_appBlockedChannelForegroundService()
+ throws Exception {
+ when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+
+ NotificationChannel channel = new NotificationChannel("blocked", "name",
+ NotificationManager.IMPORTANCE_NONE);
+ mBinderService.createNotificationChannels(
+ PKG, new ParceledListSlice(Arrays.asList(channel)));
+
+ final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
+ sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ waitForIdle();
+ assertEquals(1, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
+ assertEquals(IMPORTANCE_LOW,
+ mNotificationManagerService.getNotificationRecord(sbn.getKey()).getImportance());
+ assertEquals(IMPORTANCE_LOW,
+ mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance());
+ }
+
+ @Test
+ public void testEnqueuedBlockedNotifications_userBlockedChannelForegroundService()
+ throws Exception {
+ when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+
+ NotificationChannel channel =
+ new NotificationChannel("blockedbyuser", "name", IMPORTANCE_HIGH);
+ mBinderService.createNotificationChannels(
+ PKG, new ParceledListSlice(Arrays.asList(channel)));
+
+ NotificationChannel update =
+ new NotificationChannel("blockedbyuser", "name", IMPORTANCE_NONE);
+ mBinderService.updateNotificationChannelForPackage(PKG, mUid, update);
+ waitForIdle();
+ assertEquals(IMPORTANCE_NONE,
+ mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance());
+
+ final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
+ sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ waitForIdle();
+ assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
+ assertNull(mNotificationManagerService.getNotificationRecord(sbn.getKey()));
+ assertEquals(IMPORTANCE_NONE,
+ mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance());
}
@Test
@@ -456,6 +518,21 @@
}
@Test
+ public void testEnqueuedBlockedNotifications_blockedAppForegroundService() throws Exception {
+ when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
+
+ final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ waitForIdle();
+ assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
+ assertNull(mNotificationManagerService.getNotificationRecord(sbn.getKey()));
+ }
+
+ @Test
public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception {
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
generateNotificationRecord(null).getNotification(), 0);
@@ -812,7 +889,7 @@
mNotificationManagerService.setRankingHelper(mRankingHelper);
when(mRankingHelper.getNotificationChannel(
anyString(), anyInt(), eq("foo"), anyBoolean())).thenReturn(
- new NotificationChannel("foo", "foo", NotificationManager.IMPORTANCE_HIGH));
+ new NotificationChannel("foo", "foo", IMPORTANCE_HIGH));
Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
@@ -941,7 +1018,8 @@
mBinderService.updateNotificationChannelFromPrivilegedListener(
null, PKG, Process.myUserHandle(), mTestNotificationChannel);
- verify(mRankingHelper, times(1)).updateNotificationChannel(anyString(), anyInt(), any());
+ verify(mRankingHelper, times(1)).updateNotificationChannel(
+ anyString(), anyInt(), any(), anyBoolean());
verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG),
eq(Process.myUserHandle()), eq(mTestNotificationChannel),
@@ -962,7 +1040,8 @@
// pass
}
- verify(mRankingHelper, never()).updateNotificationChannel(anyString(), anyInt(), any());
+ verify(mRankingHelper, never()).updateNotificationChannel(
+ anyString(), anyInt(), any(), anyBoolean());
verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG),
eq(Process.myUserHandle()), eq(mTestNotificationChannel),
@@ -988,7 +1067,8 @@
// pass
}
- verify(mRankingHelper, never()).updateNotificationChannel(anyString(), anyInt(), any());
+ verify(mRankingHelper, never()).updateNotificationChannel(
+ anyString(), anyInt(), any(), anyBoolean());
verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG),
eq(Process.myUserHandle()), eq(mTestNotificationChannel),
@@ -1359,7 +1439,8 @@
@Test
public void testOnlyAutogroupIfGroupChanged_groupChanged_autogroups()
throws Exception {
- NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, "group", false);
+ NotificationRecord r =
+ generateNotificationRecord(mTestNotificationChannel, 0, "group", false);
mNotificationManagerService.addNotification(r);
r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
@@ -1439,12 +1520,16 @@
// Same notifications are enqueued as posted, everything counts b/c id and tag don't match
int userId = new UserHandle(mUid).getIdentifier();
- assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, null));
- assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag2"));
- assertEquals(2, mNotificationManagerService.getNotificationCountLocked("a", userId, 0, "banana"));
+ assertEquals(40,
+ mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, null));
+ assertEquals(40,
+ mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag2"));
+ assertEquals(2,
+ mNotificationManagerService.getNotificationCountLocked("a", userId, 0, "banana"));
// exclude a known notification - it's excluded from only the posted list, not enqueued
- assertEquals(39, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag"));
+ assertEquals(39,
+ mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag"));
}
@Test
@@ -1574,4 +1659,51 @@
verify(mZenModeHelper, times(1)).updateDefaultZenRules();
}
+
+ @Test
+ public void testBumpFGImportance_noChannelChangePreOApp() throws Exception {
+ String preOPkg = "preO";
+ int preOUid = 145;
+ final ApplicationInfo legacy = new ApplicationInfo();
+ legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+ when(mPackageManagerClient.getApplicationInfoAsUser(eq(preOPkg), anyInt(), anyInt()))
+ .thenReturn(legacy);
+ when(mPackageManagerClient.getPackageUidAsUser(eq(preOPkg), anyInt())).thenReturn(preOUid);
+ getContext().setMockPackageManager(mPackageManagerClient);
+
+ Notification.Builder nb = new Notification.Builder(mContext,
+ NotificationChannel.DEFAULT_CHANNEL_ID)
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .setPriority(Notification.PRIORITY_MIN);
+
+ StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", preOUid,
+ 0, nb.build(), new UserHandle(preOUid), null, 0);
+
+ mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg, "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ waitForIdle();
+ assertEquals(IMPORTANCE_LOW,
+ mNotificationManagerService.getNotificationRecord(sbn.getKey()).getImportance());
+
+ nb = new Notification.Builder(mContext)
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .setPriority(Notification.PRIORITY_MIN);
+
+ sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", preOUid,
+ 0, nb.build(), new UserHandle(preOUid), null, 0);
+
+ mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg, "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ waitForIdle();
+ assertEquals(IMPORTANCE_LOW,
+ mNotificationManagerService.getNotificationRecord(sbn.getKey()).getImportance());
+
+ NotificationChannel defaultChannel = mBinderService.getNotificationChannel(
+ preOPkg, NotificationChannel.DEFAULT_CHANNEL_ID);
+ assertEquals(IMPORTANCE_UNSPECIFIED, defaultChannel.getImportance());
+ }
}
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 306dd98..df989f7 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -25,25 +25,13 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.fail;
-import org.json.JSONArray;
-import org.json.JSONObject;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import com.android.internal.util.FastXmlSerializer;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
-
import android.app.Notification;
-import android.app.NotificationChannelGroup;
-import android.content.Context;
import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.IContentProvider;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -52,14 +40,28 @@
import android.net.Uri;
import android.os.Build;
import android.os.UserHandle;
+import android.provider.Settings;
import android.provider.Settings.Secure;
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContentResolver;
import android.util.ArrayMap;
import android.util.Xml;
+import com.android.internal.util.FastXmlSerializer;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -76,6 +78,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
@@ -95,10 +98,17 @@
private static final int UID2 = 1111;
private static final UserHandle USER2 = UserHandle.of(10);
private static final String TEST_CHANNEL_ID = "test_channel_id";
+ private static final String TEST_AUTHORITY = "test";
+ private static final Uri SOUND_URI =
+ Uri.parse("content://" + TEST_AUTHORITY + "/internal/audio/media/10");
+ private static final Uri CANONICAL_SOUND_URI =
+ Uri.parse("content://" + TEST_AUTHORITY
+ + "/internal/audio/media/10?title=Test&canonical=1");
@Mock NotificationUsageStats mUsageStats;
@Mock RankingHandler mHandler;
@Mock PackageManager mPm;
+ @Mock IContentProvider mTestIContentProvider;
@Mock Context mContext;
private Notification mNotiGroupGSortA;
@@ -134,9 +144,22 @@
when(mContext.getPackageManager()).thenReturn(mPm);
when(mContext.getApplicationInfo()).thenReturn(legacy);
// most tests assume badging is enabled
- Secure.putIntForUser(getContext().getContentResolver(),
+ TestableContentResolver contentResolver = getContext().getContentResolver();
+ contentResolver.setFallbackToExisting(false);
+ Secure.putIntForUser(contentResolver,
Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID));
+ ContentProvider testContentProvider = mock(ContentProvider.class);
+ when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
+ contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
+
+ when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI)))
+ .thenReturn(CANONICAL_SOUND_URI);
+ when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(CANONICAL_SOUND_URI);
+ when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(SOUND_URI);
+
mHelper = new RankingHelper(getContext(), mPm, mHandler, mUsageStats,
new String[] {ImportanceExtractor.class.getName()});
@@ -214,9 +237,12 @@
}
private void loadStreamXml(ByteArrayOutputStream stream, boolean forRestore) throws Exception {
+ loadByteArrayXml(stream.toByteArray(), forRestore);
+ }
+
+ private void loadByteArrayXml(byte[] byteArray, boolean forRestore) throws Exception {
XmlPullParser parser = Xml.newPullParser();
- parser.setInput(new BufferedInputStream(new ByteArrayInputStream(stream.toByteArray())),
- null);
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
parser.nextTag();
mHelper.readXml(parser, forRestore);
}
@@ -377,7 +403,7 @@
NotificationChannel channel2 =
new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
channel2.setDescription("descriptions for all");
- channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+ channel2.setSound(SOUND_URI, mAudioAttributes);
channel2.enableLights(true);
channel2.setBypassDnd(true);
channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
@@ -439,6 +465,109 @@
}
@Test
+ public void testBackupXml_backupCanonicalizedSoundUri() throws Exception {
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(SOUND_URI, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG, UID, channel, true);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+ // Testing that in restore we are given the canonical version
+ loadStreamXml(baos, true);
+ verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
+ }
+
+ @Test
+ public void testRestoreXml_withExistentCanonicalizedSoundUri() throws Exception {
+ Uri localUri = Uri.parse("content://" + TEST_AUTHORITY + "/local/url");
+ Uri canonicalBasedOnLocal = localUri.buildUpon()
+ .appendQueryParameter("title", "Test")
+ .appendQueryParameter("canonical", "1")
+ .build();
+ when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(canonicalBasedOnLocal);
+ when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(localUri);
+ when(mTestIContentProvider.uncanonicalize(any(), eq(canonicalBasedOnLocal)))
+ .thenReturn(localUri);
+
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(SOUND_URI, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG, UID, channel, true);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+ loadStreamXml(baos, true);
+
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(
+ PKG, UID, channel.getId(), false);
+ assertEquals(localUri, actualChannel.getSound());
+ }
+
+ @Test
+ public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
+ Thread.sleep(3000);
+ when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(null);
+ when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(null);
+
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(SOUND_URI, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG, UID, channel, true);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+ loadStreamXml(baos, true);
+
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(
+ PKG, UID, channel.getId(), false);
+ assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
+ }
+
+
+ /**
+ * Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to
+ * handle its restore properly.
+ */
+ @Test
+ public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
+ // Not a local uncanonicalized uri, simulating that it fails to exist locally
+ when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))).thenReturn(null);
+ String id = "id";
+ String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
+ + "<package name=\"com.android.server.notification\" show_badge=\"true\">\n"
+ + "<channel id=\"" + id + "\" name=\"name\" importance=\"2\" "
+ + "sound=\"" + SOUND_URI + "\" "
+ + "usage=\"6\" content_type=\"0\" flags=\"1\" show_badge=\"true\" />\n"
+ + "<channel id=\"miscellaneous\" name=\"Uncategorized\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "</package>\n"
+ + "</ranking>\n";
+
+ loadByteArrayXml(backupWithUncanonicalizedSoundUri.getBytes(), true);
+
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG, UID, id, false);
+ assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
+ }
+
+ @Test
+ public void testBackupRestoreXml_withNullSoundUri() throws Exception {
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(null, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG, UID, channel, true);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+ loadStreamXml(baos, true);
+
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(
+ PKG, UID, channel.getId(), false);
+ assertEquals(null, actualChannel.getSound());
+ }
+
+ @Test
public void testChannelXml_backup() throws Exception {
NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
@@ -497,7 +626,7 @@
final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG, UID,
NotificationChannel.DEFAULT_CHANNEL_ID, false);
defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
- mHelper.updateNotificationChannel(PKG, UID, defaultChannel);
+ mHelper.updateNotificationChannel(PKG, UID, defaultChannel, true);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
NotificationChannel.DEFAULT_CHANNEL_ID);
@@ -646,7 +775,7 @@
channel2.setBypassDnd(false);
channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
- mHelper.updateNotificationChannel(PKG, UID, channel2);
+ mHelper.updateNotificationChannel(PKG, UID, channel2, true);
// all fields should be changed
assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
@@ -670,7 +799,7 @@
defaultChannel.setBypassDnd(true);
defaultChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- mHelper.updateNotificationChannel(PKG, UID, defaultChannel);
+ mHelper.updateNotificationChannel(PKG, UID, defaultChannel, true);
// ensure app level fields are changed
assertFalse(mHelper.canShowBadge(PKG, UID));
@@ -694,7 +823,7 @@
channel.setBypassDnd(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- mHelper.updateNotificationChannel(PKG, UID, channel);
+ mHelper.updateNotificationChannel(PKG, UID, channel, true);
// ensure app level fields are not changed
assertTrue(mHelper.canShowBadge(PKG, UID));
@@ -785,14 +914,14 @@
update1.setSound(new Uri.Builder().scheme("test").build(),
new AudioAttributes.Builder().build());
update1.lockFields(NotificationChannel.USER_LOCKED_PRIORITY); // should be ignored
- mHelper.updateNotificationChannel(PKG, UID, update1);
+ mHelper.updateNotificationChannel(PKG, UID, update1, true);
assertEquals(NotificationChannel.USER_LOCKED_SOUND,
mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
.getUserLockedFields());
NotificationChannel update2 = getChannel();
update2.enableVibration(true);
- mHelper.updateNotificationChannel(PKG, UID, update2);
+ mHelper.updateNotificationChannel(PKG, UID, update2, true);
assertEquals(NotificationChannel.USER_LOCKED_SOUND
| NotificationChannel.USER_LOCKED_VIBRATION,
mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
@@ -805,14 +934,14 @@
final NotificationChannel update1 = getChannel();
update1.setVibrationPattern(new long[]{7945, 46 ,246});
- mHelper.updateNotificationChannel(PKG, UID, update1);
+ mHelper.updateNotificationChannel(PKG, UID, update1, true);
assertEquals(NotificationChannel.USER_LOCKED_VIBRATION,
mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
.getUserLockedFields());
final NotificationChannel update2 = getChannel();
update2.enableLights(true);
- mHelper.updateNotificationChannel(PKG, UID, update2);
+ mHelper.updateNotificationChannel(PKG, UID, update2, true);
assertEquals(NotificationChannel.USER_LOCKED_VIBRATION
| NotificationChannel.USER_LOCKED_LIGHTS,
mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
@@ -825,14 +954,14 @@
final NotificationChannel update1 = getChannel();
update1.setLightColor(Color.GREEN);
- mHelper.updateNotificationChannel(PKG, UID, update1);
+ mHelper.updateNotificationChannel(PKG, UID, update1, true);
assertEquals(NotificationChannel.USER_LOCKED_LIGHTS,
mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
.getUserLockedFields());
final NotificationChannel update2 = getChannel();
update2.setImportance(IMPORTANCE_DEFAULT);
- mHelper.updateNotificationChannel(PKG, UID, update2);
+ mHelper.updateNotificationChannel(PKG, UID, update2, true);
assertEquals(NotificationChannel.USER_LOCKED_LIGHTS
| NotificationChannel.USER_LOCKED_IMPORTANCE,
mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
@@ -848,14 +977,14 @@
final NotificationChannel update1 = getChannel();
update1.setBypassDnd(true);
- mHelper.updateNotificationChannel(PKG, UID, update1);
+ mHelper.updateNotificationChannel(PKG, UID, update1, true);
assertEquals(NotificationChannel.USER_LOCKED_PRIORITY,
mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
.getUserLockedFields());
final NotificationChannel update2 = getChannel();
update2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- mHelper.updateNotificationChannel(PKG, UID, update2);
+ mHelper.updateNotificationChannel(PKG, UID, update2, true);
assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
| NotificationChannel.USER_LOCKED_VISIBILITY,
mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
@@ -863,7 +992,7 @@
final NotificationChannel update3 = getChannel();
update3.setShowBadge(false);
- mHelper.updateNotificationChannel(PKG, UID, update3);
+ mHelper.updateNotificationChannel(PKG, UID, update3, true);
assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
| NotificationChannel.USER_LOCKED_VISIBILITY
| NotificationChannel.USER_LOCKED_SHOW_BADGE,
@@ -1276,7 +1405,7 @@
mHelper.getNotificationChannelGroups(PKG, UID, true).getList();
channel1.setImportance(IMPORTANCE_LOW);
- mHelper.updateNotificationChannel(PKG, UID, channel1);
+ mHelper.updateNotificationChannel(PKG, UID, channel1, true);
List<NotificationChannelGroup> actual =
mHelper.getNotificationChannelGroups(PKG, UID, true).getList();
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 526f815..679be1d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -16,9 +16,15 @@
package com.android.server.am;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManagerPolicy.NAV_BAR_BOTTOM;
import static android.view.WindowManagerPolicy.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicy.NAV_BAR_RIGHT;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -26,14 +32,13 @@
import static org.mockito.Mockito.when;
import android.content.ComponentName;
-import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
-import android.view.Display;
import org.junit.runner.RunWith;
+import org.junit.Before;
import org.junit.Test;
/**
@@ -46,64 +51,53 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityRecordTests extends ActivityTestsBase {
- private static final int TEST_STACK_ID = 100;
-
private final ComponentName testActivityComponent =
ComponentName.unflattenFromString("com.foo/.BarActivity");
private final ComponentName secondaryActivityComponent =
ComponentName.unflattenFromString("com.foo/.BarActivity2");
+
+ private ActivityManagerService mService;
+ private ActivityStack mStack;
+ private TaskRecord mTask;
+ private ActivityRecord mActivity;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mService = createActivityManagerService();
+ mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ mTask = createTask(mService.mStackSupervisor, testActivityComponent, mStack);
+ mActivity = createActivity(mService, testActivityComponent, mTask);
+ }
+
@Test
public void testStackCleanupOnClearingTask() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord record = createActivity(service, testActivityComponent, task);
-
- record.setTask(null);
- assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 1);
+ mActivity.setTask(null);
+ assertEquals(getActivityRemovedFromStackCount(), 1);
}
@Test
public void testStackCleanupOnActivityRemoval() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord record = createActivity(service, testActivityComponent, task);
-
- task.removeActivity(record);
- assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 1);
+ mTask.removeActivity(mActivity);
+ assertEquals(getActivityRemovedFromStackCount(), 1);
}
@Test
public void testStackCleanupOnTaskRemoval() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord record = createActivity(service, testActivityComponent, task);
-
- service.mStackSupervisor.getStack(TEST_STACK_ID)
- .removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING);
-
+ mStack.removeTask(mTask, null /*reason*/, REMOVE_TASK_MODE_MOVING);
// Stack should be gone on task removal.
- assertNull(service.mStackSupervisor.getStack(TEST_STACK_ID));
+ assertNull(mService.mStackSupervisor.getStack(mStack.mStackId));
}
@Test
public void testNoCleanupMovingActivityInSameStack() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord oldTask = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord record = createActivity(service, testActivityComponent, oldTask);
- final TaskRecord newTask = createTask(service, testActivityComponent, TEST_STACK_ID);
-
- record.reparent(newTask, 0, null /*reason*/);
- assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 0);
- }
-
- private static int getActivityRemovedFromStackCount(ActivityManagerService service,
- int stackId) {
- final ActivityStack stack = service.mStackSupervisor.getStack(stackId);
- if (stack instanceof ActivityStackReporter) {
- return ((ActivityStackReporter) stack).onActivityRemovedFromStackInvocationCount();
- }
-
- return -1;
+ final TaskRecord newTask =
+ createTask(mService.mStackSupervisor, testActivityComponent, mStack);
+ mActivity.reparent(newTask, 0, null /*reason*/);
+ assertEquals(getActivityRemovedFromStackCount(), 0);
}
@Test
@@ -126,16 +120,21 @@
private void verifyPositionWithLimitedAspectRatio(int navBarPosition, Rect taskBounds,
float aspectRatio, Rect expectedActivityBounds) {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord record = createActivity(service, testActivityComponent, task);
-
// Verify with nav bar on the right.
- when(service.mWindowManager.getNavBarPosition()).thenReturn(navBarPosition);
- task.getConfiguration().windowConfiguration.setAppBounds(taskBounds);
- record.info.maxAspectRatio = aspectRatio;
- record.ensureActivityConfigurationLocked(0 /* globalChanges */, false /* preserveWindow */);
- assertEquals(expectedActivityBounds, record.getBounds());
+ when(mService.mWindowManager.getNavBarPosition()).thenReturn(navBarPosition);
+ mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds);
+ mActivity.info.maxAspectRatio = aspectRatio;
+ mActivity.ensureActivityConfigurationLocked(
+ 0 /* globalChanges */, false /* preserveWindow */);
+ assertEquals(expectedActivityBounds, mActivity.getBounds());
+ }
+
+ private int getActivityRemovedFromStackCount() {
+ if (mStack instanceof ActivityStackReporter) {
+ return ((ActivityStackReporter) mStack).onActivityRemovedFromStackInvocationCount();
+ }
+
+ return -1;
}
@@ -156,26 +155,22 @@
private void testSupportsLaunchingResizeable(boolean taskPresent, boolean taskResizeable,
boolean activityResizeable, boolean expected) {
- final ActivityManagerService service = createActivityManagerService();
- service.mSupportsMultiWindow = true;
-
+ mService.mSupportsMultiWindow = true;
final TaskRecord task = taskPresent
- ? createTask(service, testActivityComponent, TEST_STACK_ID) : null;
+ ? createTask(mService.mStackSupervisor, testActivityComponent, mStack) : null;
if (task != null) {
- task.setResizeMode(taskResizeable ? ActivityInfo.RESIZE_MODE_RESIZEABLE
- : ActivityInfo.RESIZE_MODE_UNRESIZEABLE);
+ task.setResizeMode(taskResizeable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE);
}
- final ActivityRecord record = createActivity(service, secondaryActivityComponent,
- task);
- record.info.resizeMode = activityResizeable ? ActivityInfo.RESIZE_MODE_RESIZEABLE
- : ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ final ActivityRecord record = createActivity(mService, secondaryActivityComponent, task);
+ record.info.resizeMode = activityResizeable
+ ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
- record.canBeLaunchedOnDisplay(Display.DEFAULT_DISPLAY);
+ record.canBeLaunchedOnDisplay(DEFAULT_DISPLAY);
- assertEquals(((TestActivityStackSupervisor) service.mStackSupervisor)
+ assertEquals(((TestActivityStackSupervisor) mService.mStackSupervisor)
.getLastResizeableFromCanPlaceEntityOnDisplay(), expected);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index cd1843b..6b93d0e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -16,11 +16,10 @@
package com.android.server.am;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -33,6 +32,7 @@
import android.support.test.runner.AndroidJUnit4;
import org.junit.runner.RunWith;
+import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
@@ -54,6 +54,21 @@
private final ComponentName testActivityComponent =
ComponentName.unflattenFromString("com.foo/.BarActivity");
+ private ActivityManagerService mService;
+ private ActivityStackSupervisor mSupervisor;
+ private ActivityStack mFullscreenStack;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mService = createActivityManagerService();
+ mSupervisor = mService.mStackSupervisor;
+ mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ }
+
/**
* This test ensures that we do not try to restore a task based off an invalid task id. The
* stack supervisor is a test version so there will be no tasks present. We should expect
@@ -61,8 +76,7 @@
*/
@Test
public void testRestoringInvalidTask() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- TaskRecord task = service.mStackSupervisor.anyTaskForIdLocked(0 /*taskId*/,
+ TaskRecord task = mSupervisor.anyTaskForIdLocked(0 /*taskId*/,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null);
assertNull(task);
}
@@ -73,43 +87,44 @@
*/
@Test
public void testReplacingTaskInPinnedStack() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord firstTask = createTask(service, testActivityComponent,
- FULLSCREEN_WORKSPACE_STACK_ID);
- final ActivityRecord firstActivity = createActivity(service, testActivityComponent,
+ final TaskRecord firstTask = createTask(
+ mSupervisor, testActivityComponent, mFullscreenStack);
+ final ActivityRecord firstActivity = createActivity(mService, testActivityComponent,
firstTask);
// Create a new task on the full screen stack
- final TaskRecord secondTask = createTask(service, testActivityComponent,
- FULLSCREEN_WORKSPACE_STACK_ID);
- final ActivityRecord secondActivity = createActivity(service, testActivityComponent,
+ final TaskRecord secondTask = createTask(
+ mSupervisor, testActivityComponent, mFullscreenStack);
+ final ActivityRecord secondActivity = createActivity(mService, testActivityComponent,
secondTask);
- service.mStackSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack",
- service.mStackSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID));
+ mSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack", mFullscreenStack);
// Ensure full screen stack has both tasks.
- ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, firstTask,
- secondTask);
+ ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
// Move first activity to pinned stack.
- service.mStackSupervisor.moveActivityToPinnedStackLocked(firstActivity,
- new Rect() /*sourceBounds*/, 0f /*aspectRatio*/, false, "initialMove");
+ final Rect sourceBounds = new Rect();
+ mSupervisor.moveActivityToPinnedStackLocked(firstActivity, sourceBounds,
+ 0f /*aspectRatio*/, false, "initialMove");
+ final ActivityDisplay display = mFullscreenStack.getDisplay();
+ ActivityStack pinnedStack = display.getPinnedStack();
// Ensure a task has moved over.
- ensureStackPlacement(service.mStackSupervisor, PINNED_STACK_ID, firstTask);
- ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, secondTask);
+ ensureStackPlacement(pinnedStack, firstTask);
+ ensureStackPlacement(mFullscreenStack, secondTask);
// Move second activity to pinned stack.
- service.mStackSupervisor.moveActivityToPinnedStackLocked(secondActivity,
- new Rect() /*sourceBounds*/, 0f /*aspectRatio*/ /*destBounds*/, false, "secondMove");
+ mSupervisor.moveActivityToPinnedStackLocked(secondActivity, sourceBounds,
+ 0f /*aspectRatio*/, false, "secondMove");
+
+ // Need to get pinned stack again as a new instance might have been created.
+ pinnedStack = display.getPinnedStack();
// Ensure stacks have swapped tasks.
- ensureStackPlacement(service.mStackSupervisor, PINNED_STACK_ID, secondTask);
- ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, firstTask);
+ ensureStackPlacement(pinnedStack, secondTask);
+ ensureStackPlacement(mFullscreenStack, firstTask);
}
- private static void ensureStackPlacement(ActivityStackSupervisor supervisor, int stackId,
- TaskRecord... tasks) {
- final ActivityStack stack = supervisor.getStack(stackId);
+ private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
@@ -127,15 +142,14 @@
*/
@Test
public void testStoppingActivityRemovedWhenResumed() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord firstTask = createTask(service, testActivityComponent,
- FULLSCREEN_WORKSPACE_STACK_ID);
- final ActivityRecord firstActivity = createActivity(service, testActivityComponent,
+ final TaskRecord firstTask = createTask(
+ mSupervisor, testActivityComponent, mFullscreenStack);
+ final ActivityRecord firstActivity = createActivity(mService, testActivityComponent,
firstTask);
- service.mStackSupervisor.mStoppingActivities.add(firstActivity);
+ mSupervisor.mStoppingActivities.add(firstActivity);
firstActivity.completeResumeLocked();
- assertFalse(service.mStackSupervisor.mStoppingActivities.contains(firstActivity));
+ assertFalse(mSupervisor.mStoppingActivities.contains(firstActivity));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 02fba08..4ee1f47 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -16,12 +16,14 @@
package com.android.server.am;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.os.UserHandle;
@@ -30,6 +32,7 @@
import android.support.test.runner.AndroidJUnit4;
import org.junit.runner.RunWith;
+import org.junit.Before;
import org.junit.Test;
/**
@@ -48,78 +51,80 @@
private static final ComponentName testOverlayComponent =
ComponentName.unflattenFromString("com.foo/.OverlayActivity");
+ private ActivityManagerService mService;
+ private ActivityStackSupervisor mSupervisor;
+ private ActivityStack mStack;
+ private TaskRecord mTask;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mService = createActivityManagerService();
+ mSupervisor = mService.mStackSupervisor;
+ mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ mTask = createTask(mSupervisor, testActivityComponent, mStack);
+ }
+
@Test
public void testEmptyTaskCleanupOnRemove() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- assertNotNull(task.getWindowContainerController());
- service.mStackSupervisor.getStack(TEST_STACK_ID).removeTask(task,
- "testEmptyTaskCleanupOnRemove", ActivityStack.REMOVE_TASK_MODE_DESTROYING);
- assertNull(task.getWindowContainerController());
+ assertNotNull(mTask.getWindowContainerController());
+ mStack.removeTask(mTask, "testEmptyTaskCleanupOnRemove", REMOVE_TASK_MODE_DESTROYING);
+ assertNull(mTask.getWindowContainerController());
}
@Test
public void testOccupiedTaskCleanupOnRemove() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task);
- assertNotNull(task.getWindowContainerController());
- service.mStackSupervisor.getStack(TEST_STACK_ID).removeTask(task,
- "testOccupiedTaskCleanupOnRemove", ActivityStack.REMOVE_TASK_MODE_DESTROYING);
- assertNotNull(task.getWindowContainerController());
+ final ActivityRecord r = createActivity(mService, testActivityComponent, mTask);
+ assertNotNull(mTask.getWindowContainerController());
+ mStack.removeTask(mTask, "testOccupiedTaskCleanupOnRemove", REMOVE_TASK_MODE_DESTROYING);
+ assertNotNull(mTask.getWindowContainerController());
}
@Test
public void testNoPauseDuringResumeTopActivity() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task);
- final ActivityStack testStack = service.mStackSupervisor.getStack(TEST_STACK_ID);
+ final ActivityRecord r = createActivity(mService, testActivityComponent, mTask);
// Simulate the a resumed activity set during
// {@link ActivityStack#resumeTopActivityUncheckedLocked}.
- service.mStackSupervisor.inResumeTopActivity = true;
- testStack.mResumedActivity = activityRecord;
+ mSupervisor.inResumeTopActivity = true;
+ mStack.mResumedActivity = r;
- final boolean waiting = testStack.goToSleepIfPossible(false);
+ final boolean waiting = mStack.goToSleepIfPossible(false);
// Ensure we report not being ready for sleep.
assertFalse(waiting);
// Make sure the resumed activity is untouched.
- assertEquals(testStack.mResumedActivity, activityRecord);
+ assertEquals(mStack.mResumedActivity, r);
}
@Test
public void testStopActivityWhenActivityDestroyed() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task);
- activityRecord.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
- final ActivityStack testStack = service.mStackSupervisor.getStack(TEST_STACK_ID);
- service.mStackSupervisor.setFocusStackUnchecked("testStopActivityWithDestroy", testStack);
-
- testStack.stopActivityLocked(activityRecord);
+ final ActivityRecord r = createActivity(mService, testActivityComponent, mTask);
+ r.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
+ mSupervisor.setFocusStackUnchecked("testStopActivityWithDestroy", mStack);
+ mStack.stopActivityLocked(r);
+ // Mostly testing to make sure there is a crash in the call part, so if we get here we are
+ // good-to-go!
}
@Test
public void testFindTaskWithOverlay() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
- final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task,
- 0);
+ final ActivityRecord r = createActivity(mService, testActivityComponent, mTask, 0);
// Overlay must be for a different user to prevent recognizing a matching top activity
- final ActivityRecord taskOverlay = createActivity(service, testOverlayComponent, task,
+ final ActivityRecord taskOverlay = createActivity(mService, testOverlayComponent, mTask,
UserHandle.PER_USER_RANGE * 2);
taskOverlay.mTaskOverlay = true;
- final ActivityStack testStack = service.mStackSupervisor.getStack(TEST_STACK_ID);
final ActivityStackSupervisor.FindTaskResult result =
new ActivityStackSupervisor.FindTaskResult();
- testStack.findTaskLocked(activityRecord, result);
+ mStack.findTaskLocked(r, result);
- assertEquals(task.getTopActivity(false /* includeOverlays */), activityRecord);
- assertEquals(task.getTopActivity(true /* includeOverlays */), taskOverlay);
+ assertEquals(mTask.getTopActivity(false /* includeOverlays */), r);
+ assertEquals(mTask.getTopActivity(true /* includeOverlays */), taskOverlay);
assertNotNull(result.r);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
new file mode 100644
index 0000000..855b82c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 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 static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import static com.android.server.am.ActivityManagerService.ANIMATE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.times;
+
+/**
+ * Tests for the {@link ActivityStack} class.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.am.ActivityStarterTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActivityStarterTests extends ActivityTestsBase {
+ private static final ComponentName testActivityComponent =
+ ComponentName.unflattenFromString("com.foo/.BarActivity");
+
+ private ActivityManagerService mService;
+ private ActivityStarter mStarter;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mService = createActivityManagerService();
+ mStarter = new ActivityStarter(mService, mService.mStackSupervisor);
+ }
+
+ @Test
+ public void testUpdateLaunchBounds() throws Exception {
+ // When in a non-resizeable stack, the task bounds should be updated.
+ final TaskRecord task = createTask(mService.mStackSupervisor, testActivityComponent,
+ mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */));
+ final Rect bounds = new Rect(10, 10, 100, 100);
+
+ mStarter.updateBounds(task, bounds);
+ assertEquals(task.mBounds, bounds);
+ assertEquals(task.getStack().mBounds, null);
+
+ // When in a resizeable stack, the stack bounds should be updated as well.
+ final TaskRecord task2 = createTask(mService.mStackSupervisor, testActivityComponent,
+ mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */));
+ assertTrue(task2.getStack() instanceof PinnedActivityStack);
+ mStarter.updateBounds(task2, bounds);
+
+ verify(mService, times(1)).resizeStack(eq(ActivityManager.StackId.PINNED_STACK_ID),
+ eq(bounds), anyBoolean(), anyBoolean(), anyBoolean(), anyInt());
+
+ // In the case of no animation, the stack and task bounds should be set immediately.
+ if (!ANIMATE) {
+ assertEquals(task2.getStack().mBounds, bounds);
+ assertEquals(task2.mBounds, bounds);
+ } else {
+ assertEquals(task2.mBounds, null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 0cf1df8..1f6dda1 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -16,15 +16,16 @@
package com.android.server.am;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
import org.mockito.invocation.InvocationOnMock;
-import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -32,13 +33,14 @@
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.os.HandlerThread;
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
import com.android.server.AttributeCache;
import com.android.server.wm.AppWindowContainerController;
+import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.StackWindowController;
-
import com.android.server.wm.TaskWindowContainerController;
import com.android.server.wm.WindowManagerService;
import com.android.server.wm.WindowTestUtils;
@@ -70,21 +72,11 @@
}
protected ActivityManagerService createActivityManagerService() {
- final ActivityManagerService service = new TestActivityManagerService(mContext);
- service.mWindowManager = WindowTestUtils.getMockWindowManagerService();
+ final ActivityManagerService service = spy(new TestActivityManagerService(mContext));
+ service.mWindowManager = prepareMockWindowManager();
return service;
}
- protected static ActivityStack createActivityStack(ActivityManagerService service,
- int stackId, int displayId, boolean onTop) {
- if (service.mStackSupervisor instanceof TestActivityStackSupervisor) {
- return ((TestActivityStackSupervisor) service.mStackSupervisor)
- .createTestStack(stackId, onTop);
- }
-
- return null;
- }
-
protected static ActivityRecord createActivity(ActivityManagerService service,
ComponentName component, TaskRecord task) {
return createActivity(service, component, task, 0 /* userId */);
@@ -113,8 +105,8 @@
return activity;
}
- protected static TaskRecord createTask(ActivityManagerService service,
- ComponentName component, int stackId) {
+ protected static TaskRecord createTask(ActivityStackSupervisor supervisor,
+ ComponentName component, ActivityStack stack) {
final ActivityInfo aInfo = new ActivityInfo();
aInfo.applicationInfo = new ApplicationInfo();
aInfo.applicationInfo.packageName = component.getPackageName();
@@ -122,11 +114,9 @@
Intent intent = new Intent();
intent.setComponent(component);
- final TaskRecord task = new TaskRecord(service, 0, aInfo, intent /*intent*/,
+ final TaskRecord task = new TaskRecord(supervisor.mService, 0, aInfo, intent /*intent*/,
null /*_taskDescription*/);
- final ActivityStack stack = service.mStackSupervisor.getStack(stackId,
- true /*createStaticStackIfNeeded*/, true /*onTop*/);
- service.mStackSupervisor.setFocusStackUnchecked("test", stack);
+ supervisor.setFocusStackUnchecked("test", stack);
stack.addTask(task, true, "creating test task");
task.setStack(stack);
task.setWindowContainerController(mock(TaskWindowContainerController.class));
@@ -134,16 +124,18 @@
return task;
}
-
/**
* An {@link ActivityManagerService} subclass which provides a test
* {@link ActivityStackSupervisor}.
*/
protected static class TestActivityManagerService extends ActivityManagerService {
- public TestActivityManagerService(Context context) {
+ TestActivityManagerService(Context context) {
super(context);
mSupportsMultiWindow = true;
mSupportsMultiDisplay = true;
+ mSupportsSplitScreenMultiWindow = true;
+ mSupportsFreeformWindowManagement = true;
+ mSupportsPictureInPicture = true;
mWindowManager = WindowTestUtils.getWindowManagerService(context);
}
@@ -167,8 +159,16 @@
public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
super(service, looper);
+ mDisplayManager =
+ (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
mWindowManager = prepareMockWindowManager();
- mDisplay = new ActivityDisplay();
+ mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
+ attachDisplay(mDisplay);
+ }
+
+ @Override
+ ActivityDisplay getDefaultDisplay() {
+ return mDisplay;
}
// TODO: Use Mockito spy instead. Currently not possible due to TestActivityStackSupervisor
@@ -214,38 +214,6 @@
boolean preserveWindows) {
}
- <T extends ActivityStack> T createTestStack(int stackId, boolean onTop) {
- return (T) createStack(stackId, mDisplay, onTop);
- }
-
- @Override
- ActivityStack createStack(int stackId, ActivityDisplay display, boolean onTop) {
- final RecentTasks recents =
- new RecentTasks(mService, mService.mStackSupervisor);
- if (stackId == PINNED_STACK_ID) {
- return new PinnedActivityStack(display, stackId, this, recents, onTop) {
- @Override
- Rect getDefaultPictureInPictureBounds(float aspectRatio) {
- return new Rect(50, 50, 100, 100);
- }
- };
- } else {
- return new TestActivityStack(display, stackId, this, recents, onTop);
- }
- }
-
- @Override
- protected <T extends ActivityStack> T getStack(int stackId,
- boolean createStaticStackIfNeeded, boolean createOnTop) {
- final T stack = super.getStack(stackId, createStaticStackIfNeeded, createOnTop);
-
- if (stack != null || !createStaticStackIfNeeded) {
- return stack;
- }
-
- return createTestStack(stackId, createOnTop);
- }
-
// Always keep things awake
@Override
boolean hasAwakeDisplay() {
@@ -253,8 +221,39 @@
}
}
+ private static class TestActivityDisplay extends ActivityDisplay {
+
+ private final ActivityStackSupervisor mSupervisor;
+ TestActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
+ super(supervisor, displayId);
+ mSupervisor = supervisor;
+ }
+
+ @Override
+ <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
+ int stackId, boolean onTop) {
+ if (windowingMode == WINDOWING_MODE_PINNED) {
+ return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop) {
+ @Override
+ Rect getDefaultPictureInPictureBounds(float aspectRatio) {
+ return new Rect(50, 50, 100, 100);
+ }
+
+ @Override
+ PinnedStackWindowController createStackWindowController(int displayId,
+ boolean onTop, Rect outBounds) {
+ return mock(PinnedStackWindowController.class);
+ }
+ };
+ } else {
+ return (T) new TestActivityStack(
+ this, stackId, mSupervisor, windowingMode, activityType, onTop);
+ }
+ }
+ }
+
private static WindowManagerService prepareMockWindowManager() {
- final WindowManagerService service = mock(WindowManagerService.class);
+ final WindowManagerService service = WindowTestUtils.getMockWindowManagerService();
doAnswer((InvocationOnMock invocationOnMock) -> {
final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
@@ -272,7 +271,7 @@
}
/**
- * Override of {@link ActivityStack} that tracks test metrics, such as the number of times a
+ * Overrided of {@link ActivityStack} that tracks test metrics, such as the number of times a
* method is called. Note that its functionality depends on the implementations of the
* construction arguments.
*/
@@ -280,9 +279,10 @@
extends ActivityStack<T> implements ActivityStackReporter {
private int mOnActivityRemovedFromStackCount = 0;
private T mContainerController;
- TestActivityStack(ActivityStackSupervisor.ActivityDisplay display, int stackId,
- ActivityStackSupervisor supervisor, RecentTasks recentTasks, boolean onTop) {
- super(display, stackId, supervisor, recentTasks, onTop);
+
+ TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
+ int windowingMode, int activityType, boolean onTop) {
+ super(display, stackId, supervisor, windowingMode, activityType, onTop);
}
@Override
@@ -308,36 +308,4 @@
return mContainerController;
}
}
-
-
- protected static class ActivityStackBuilder {
- private boolean mOnTop = true;
- private int mStackId = 0;
- private int mDisplayId = 1;
-
- private final ActivityManagerService mService;
-
- public ActivityStackBuilder(ActivityManagerService ams) {
- mService = ams;
- }
-
- public ActivityStackBuilder setOnTop(boolean onTop) {
- mOnTop = onTop;
- return this;
- }
-
- public ActivityStackBuilder setStackId(int id) {
- mStackId = id;
- return this;
- }
-
- public ActivityStackBuilder setDisplayId(int id) {
- mDisplayId = id;
- return this;
- }
-
- public ActivityStack build() {
- return createActivityStack(mService, mStackId, mDisplayId, mOnTop);
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 419a161..d6d0209 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -30,6 +30,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.UserManagerInternal;
+import android.platform.test.annotations.Presubmit;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
@@ -59,14 +60,20 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+/**
+ * Usage: bit FrameworksServicesTests:com.android.server.am.UserControllerTest
+ */
+@Presubmit
public class UserControllerTest extends AndroidTestCase {
private static final int TEST_USER_ID = 10;
private static final int NONEXIST_USER_ID = 2;
@@ -96,8 +103,11 @@
@Override
public void setUp() throws Exception {
super.setUp();
- System.setProperty("dexmaker.share_classloader", "true");
- mInjector = new TestInjector(getContext());
+ mInjector = Mockito.spy(new TestInjector(getContext()));
+ doNothing().when(mInjector).clearLockTaskMode(anyString());
+ doNothing().when(mInjector).startHomeActivity(anyInt(), anyString());
+ doReturn(false).when(mInjector).stackSupervisorSwitchUser(anyInt(), any());
+ doNothing().when(mInjector).stackSupervisorResumeFocusedStackTopActivity();
mUserController = new UserController(mInjector);
setUpUser(TEST_USER_ID, 0);
}
@@ -106,6 +116,7 @@
protected void tearDown() throws Exception {
super.tearDown();
mInjector.handlerThread.quit();
+ Mockito.validateMockitoUsage();
}
@SmallTest
@@ -115,7 +126,7 @@
Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(true);
- Mockito.verify(mInjector.getLockTaskController()).clearLockTaskMode(anyString());
+ Mockito.verify(mInjector).clearLockTaskMode(anyString());
startForegroundUserAssertions();
}
@@ -125,7 +136,7 @@
Mockito.verify(
mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
- verifyZeroInteractions(mInjector.getLockTaskController());
+ Mockito.verify(mInjector, never()).clearLockTaskMode(anyString());
startBackgroundUserAssertions();
}
@@ -306,16 +317,14 @@
return result;
}
- private static class TestInjector extends UserController.Injector {
- final Object lock = new Object();
+ // Should be public to allow mocking
+ public static class TestInjector extends UserController.Injector {
TestHandler handler;
TestHandler uiHandler;
HandlerThread handlerThread;
UserManagerService userManagerMock;
UserManagerInternal userManagerInternalMock;
WindowManagerService windowManagerMock;
- ActivityStackSupervisor activityStackSupervisor;
- LockTaskController lockTaskController;
private Context mCtx;
List<Intent> sentIntents = new ArrayList<>();
@@ -329,13 +338,6 @@
userManagerMock = mock(UserManagerService.class);
userManagerInternalMock = mock(UserManagerInternal.class);
windowManagerMock = mock(WindowManagerService.class);
- activityStackSupervisor = mock(ActivityStackSupervisor.class);
- lockTaskController = mock(LockTaskController.class);
- }
-
- @Override
- protected Object getLock() {
- return lock;
}
@Override
@@ -375,12 +377,12 @@
}
@Override
- void updateUserConfigurationLocked() {
- Log.i(TAG, "updateUserConfigurationLocked");
+ void updateUserConfiguration() {
+ Log.i(TAG, "updateUserConfiguration");
}
@Override
- protected int broadcastIntentLocked(Intent intent, String resolvedType,
+ protected int broadcastIntent(Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered,
boolean sticky, int callingPid, int callingUid, int userId) {
@@ -388,20 +390,6 @@
sentIntents.add(intent);
return 0;
}
-
- @Override
- void startHomeActivityLocked(int userId, String reason) {
- Log.i(TAG, "startHomeActivityLocked " + userId);
- }
-
- @Override
- ActivityStackSupervisor getActivityStackSupervisor() {
- return activityStackSupervisor;
- }
-
- LockTaskController getLockTaskController() {
- return lockTaskController;
- }
}
private static class TestHandler extends Handler {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 87b0db8..a8bf8f1 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3421,6 +3421,9 @@
// Even if the caller is the managed profile, the current user is the user 0
when(getServices().iactivityManager.getCurrentUser())
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+ // Get mock reason string since we throw an IAE with empty string input.
+ when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)).
+ thenReturn("Just a test string.");
dpm.wipeData(0);
verify(getServices().userManagerInternal).removeUserEvenWhenDisallowed(
@@ -3440,6 +3443,9 @@
UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
UserHandle.of(MANAGED_PROFILE_USER_ID)))
.thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
+ when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)).
+ thenReturn("Just a test string.");
+
mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
// The PO is not allowed to remove the profile if the user restriction was set on the
// profile by the system
@@ -3453,6 +3459,8 @@
UserManager.DISALLOW_FACTORY_RESET,
UserHandle.SYSTEM))
.thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
+ when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)).
+ thenReturn("Just a test string.");
dpm.wipeData(0);
verify(getServices().recoverySystem).rebootWipeUserData(
@@ -3466,6 +3474,8 @@
UserManager.DISALLOW_FACTORY_RESET,
UserHandle.SYSTEM))
.thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
+ when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)).
+ thenReturn("Just a test string.");
dpm.wipeData(WIPE_EUICC);
verify(getServices().recoverySystem).rebootWipeUserData(
@@ -3479,6 +3489,8 @@
UserManager.DISALLOW_FACTORY_RESET,
UserHandle.SYSTEM))
.thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
+ when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)).
+ thenReturn("Just a test string.");
// The DO is not allowed to wipe the device if the user restriction was set
// by the system
assertExpectException(SecurityException.class, /* messageRegex= */ null,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index 13cf9df..7cba280 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -106,7 +106,8 @@
return mService;
}
};
- mSpManager = new MockSyntheticPasswordManager(mStorage, mGateKeeperService, mUserManager);
+ mSpManager = new MockSyntheticPasswordManager(mContext, mStorage, mGateKeeperService,
+ mUserManager);
mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
mSpManager);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
index cf03593..6f68179 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
@@ -15,6 +15,7 @@
*/
package com.android.server.locksettings;
+import android.content.Context;
import android.hardware.weaver.V1_0.IWeaver;
import android.os.RemoteException;
import android.os.UserManager;
@@ -35,9 +36,9 @@
private FakeGateKeeperService mGateKeeper;
private IWeaver mWeaverService;
- public MockSyntheticPasswordManager(LockSettingsStorage storage,
+ public MockSyntheticPasswordManager(Context context, LockSettingsStorage storage,
FakeGateKeeperService gatekeeper, UserManager userManager) {
- super(storage, userManager);
+ super(context, storage, userManager);
mGateKeeper = gatekeeper;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 2c9aa9d..2ad0580 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -58,7 +58,7 @@
final int USER_ID = 10;
final String PASSWORD = "user-password";
final String BADPASSWORD = "bad-password";
- MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mStorage,
+ MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mContext, mStorage,
mGateKeeperService, mUserManager);
AuthenticationToken authToken = manager.newSyntheticPasswordAndSid(mGateKeeperService, null,
null, USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 3cd24b8..fd105bc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -34,6 +34,7 @@
import static org.junit.Assert.fail;
import android.annotation.NonNull;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
@@ -51,6 +52,9 @@
import com.android.internal.os.AtomicFile;
import com.android.server.LocalServices;
+import com.android.server.pm.permission.PermissionManagerInternal;
+import com.android.server.pm.permission.PermissionManagerService;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
import org.junit.Before;
import org.junit.Test;
@@ -79,8 +83,11 @@
throws ReflectiveOperationException, IllegalAccessException {
/* write out files and read */
writeOldFiles();
+ final Context context = InstrumentationRegistry.getContext();
+ final Object lock = new Object();
+ PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
Settings settings =
- new Settings(InstrumentationRegistry.getContext().getFilesDir(), new Object());
+ new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
verifyKeySetMetaData(settings);
}
@@ -91,8 +98,11 @@
throws ReflectiveOperationException, IllegalAccessException {
// write out files and read
writeOldFiles();
+ final Context context = InstrumentationRegistry.getContext();
+ final Object lock = new Object();
+ PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
Settings settings =
- new Settings(InstrumentationRegistry.getContext().getFilesDir(), new Object());
+ new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
// write out, read back in and verify the same
@@ -105,8 +115,11 @@
public void testSettingsReadOld() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
+ final Context context = InstrumentationRegistry.getContext();
+ final Object lock = new Object();
+ PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
Settings settings =
- new Settings(InstrumentationRegistry.getContext().getFilesDir(), new Object());
+ new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
assertThat(settings.getPackageLPr(PACKAGE_NAME_3), is(notNullValue()));
assertThat(settings.getPackageLPr(PACKAGE_NAME_1), is(notNullValue()));
@@ -125,13 +138,17 @@
public void testNewPackageRestrictionsFile() throws ReflectiveOperationException {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
+ final Context context = InstrumentationRegistry.getContext();
+ final Object lock = new Object();
+ PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
Settings settings =
- new Settings(InstrumentationRegistry.getContext().getFilesDir(), new Object());
+ new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
settings.writeLPr();
// Create Settings again to make it read from the new files
- settings = new Settings(InstrumentationRegistry.getContext().getFilesDir(), new Object());
+ settings =
+ new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
PackageSetting ps = settings.getPackageLPr(PACKAGE_NAME_2);
@@ -143,8 +160,11 @@
public void testEnableDisable() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
+ final Context context = InstrumentationRegistry.getContext();
+ final Object lock = new Object();
+ PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
Settings settings =
- new Settings(InstrumentationRegistry.getContext().getFilesDir(), new Object());
+ new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
// Enable/Disable a package
@@ -334,7 +354,11 @@
/** Update package; changing shared user throws exception */
@Test
public void testUpdatePackageSetting03() {
- final Settings testSettings01 = new Settings(new Object() /*lock*/);
+ final Context context = InstrumentationRegistry.getContext();
+ final Object lock = new Object();
+ PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+ final Settings testSettings01 =
+ new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
final SharedUserSetting testUserSetting01 = createSharedUserSetting(
testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
final PackageSetting testPkgSetting01 =
@@ -449,7 +473,11 @@
/** Create PackageSetting for a shared user */
@Test
public void testCreateNewSetting03() {
- final Settings testSettings01 = new Settings(new Object() /*lock*/);
+ final Context context = InstrumentationRegistry.getContext();
+ final Object lock = new Object();
+ PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+ final Settings testSettings01 =
+ new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
final SharedUserSetting testUserSetting01 = createSharedUserSetting(
testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
final PackageSetting testPkgSetting01 = Settings.createNewSetting(
@@ -542,8 +570,11 @@
final PackageParser.Package pkg = new PackageParser.Package(PACKAGE_NAME);
pkg.applicationInfo.setCodePath(ps.codePathString);
pkg.applicationInfo.setResourcePath(ps.resourcePathString);
+ final Context context = InstrumentationRegistry.getContext();
+ final Object lock = new Object();
+ PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
final Settings settings =
- new Settings(InstrumentationRegistry.getContext().getFilesDir(), new Object());
+ new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
pkg.usesStaticLibraries = new ArrayList<>(
Arrays.asList("foo.bar1", "foo.bar2", "foo.bar3"));
pkg.usesStaticLibrariesVersions = new int[] {2, 4, 6};
diff --git a/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java b/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
index 375edf3..b647b99 100644
--- a/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
@@ -149,11 +149,13 @@
assertThat(json.getLong(DiskStatsFileLogger.DOWNLOADS_KEY)).isEqualTo(3L);
assertThat(json.getLong(DiskStatsFileLogger.SYSTEM_KEY)).isEqualTo(10L);
assertThat(json.getLong(DiskStatsFileLogger.MISC_KEY)).isEqualTo(7L);
- assertThat(json.getLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY)).isEqualTo(15L);
+ assertThat(json.getLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY)).isEqualTo(10L);
+ assertThat(json.getLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY)).isEqualTo(5L);
assertThat(json.getLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY)).isEqualTo(55L);
assertThat(
json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY).length()).isEqualTo(1L);
assertThat(json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY).length()).isEqualTo(1L);
+ assertThat(json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY).length()).isEqualTo(1L);
assertThat(json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY).length()).isEqualTo(1L);
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index f732503..d362c3b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -16,7 +16,8 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -347,17 +348,20 @@
*/
@Test
public void testPinnedStackLocation() {
- createStackControllerOnStackOnDisplay(PINNED_STACK_ID, mDisplayContent);
+ createStackControllerOnStackOnDisplay(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
final int initialStackCount = mDisplayContent.getStackCount();
// Ensure that the pinned stack was placed at the end
- assertEquals(initialStackCount - 1, mDisplayContent.getStackPosById(PINNED_STACK_ID));
+ assertEquals(initialStackCount - 1,
+ mDisplayContent.getStackPosition(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD));
// By default, this should try to create a new stack on top
createTaskStackOnDisplay(mDisplayContent);
final int afterStackCount = mDisplayContent.getStackCount();
// Make sure the stack count has increased
assertEquals(initialStackCount + 1, afterStackCount);
// Ensure that the pinned stack is still on top
- assertEquals(afterStackCount - 1, mDisplayContent.getStackPosById(PINNED_STACK_ID));
+ assertEquals(afterStackCount - 1,
+ mDisplayContent.getStackPosition(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD));
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
index 29bbe6e..536a504 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -29,7 +29,7 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import static android.app.ActivityManager.StackId.getWindowingModeForStackId;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -53,8 +53,7 @@
public void setUp() throws Exception {
super.setUp();
final Configuration overrideConfig = new Configuration();
- overrideConfig.windowConfiguration.setWindowingMode(
- getWindowingModeForStackId(PINNED_STACK_ID, false /* inSplitScreenMode */));
+ overrideConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
mPinnedStack = new StackWindowController(PINNED_STACK_ID, null,
mDisplayContent.getDisplayId(), true /* onTop */, new Rect(), sWm).mContainer;
mPinnedStack.onOverrideConfigurationChanged(overrideConfig);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
index 31aad79..e78224c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -26,10 +26,10 @@
import android.support.test.runner.AndroidJUnit4;
import android.view.DisplayInfo;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
import static android.app.WindowConfiguration.WINDOW_CONFIG_WINDOWING_MODE;
import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
@@ -155,7 +155,7 @@
shiftedBounds.offset(10, 10);
final Rect expectedBounds = new Rect(mParentBounds);
expectedBounds.intersect(shiftedBounds);
- testStackBoundsConfiguration(null /*stackId*/, mParentBounds, shiftedBounds,
+ testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, shiftedBounds,
expectedBounds);
}
@@ -163,7 +163,7 @@
@Test
public void testAppBounds_EmptyBounds() throws Exception {
final Rect emptyBounds = new Rect();
- testStackBoundsConfiguration(null /*stackId*/, mParentBounds, emptyBounds,
+ testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds,
null /*ExpectedBounds*/);
}
@@ -172,7 +172,7 @@
public void testAppBounds_FreeFormBounds() throws Exception {
final Rect freeFormBounds = new Rect(mParentBounds);
freeFormBounds.offset(10, 10);
- testStackBoundsConfiguration(FREEFORM_WORKSPACE_STACK_ID, mParentBounds, freeFormBounds,
+ testStackBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds,
freeFormBounds);
}
@@ -181,7 +181,8 @@
public void testAppBounds_ContainedBounds() throws Exception {
final Rect insetBounds = new Rect(mParentBounds);
insetBounds.inset(5, 5, 5, 5);
- testStackBoundsConfiguration(null /*stackId*/, mParentBounds, insetBounds, insetBounds);
+ testStackBoundsConfiguration(
+ WINDOWING_MODE_FULLSCREEN, mParentBounds, insetBounds, insetBounds);
}
/** Ensures that full screen free form bounds are clipped */
@@ -189,15 +190,14 @@
public void testAppBounds_FullScreenFreeFormBounds() throws Exception {
final Rect fullScreenBounds = new Rect(0, 0, mDisplayInfo.logicalWidth,
mDisplayInfo.logicalHeight);
- testStackBoundsConfiguration(null /*stackId*/, mParentBounds, fullScreenBounds,
+ testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, fullScreenBounds,
mParentBounds);
}
- private void testStackBoundsConfiguration(Integer stackId, Rect parentBounds, Rect bounds,
+ private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
Rect expectedConfigBounds) {
- final StackWindowController stackController = stackId != null ?
- createStackControllerOnStackOnDisplay(stackId, mDisplayContent)
- : createStackControllerOnDisplay(mDisplayContent);
+ final StackWindowController stackController = createStackControllerOnStackOnDisplay(
+ windowingMode, ACTIVITY_TYPE_STANDARD, mDisplayContent);
final Configuration parentConfig = mDisplayContent.getConfiguration();
parentConfig.windowConfiguration.setAppBounds(parentBounds);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
index e1f318d..3c3514f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
@@ -23,9 +23,11 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -160,12 +162,14 @@
@Test
public void testStackLayers() throws Exception {
- WindowState pinnedStackWindow = createWindowOnStack(null, PINNED_STACK_ID,
- TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow");
- WindowState dockedStackWindow = createWindowOnStack(null, DOCKED_STACK_ID,
- TYPE_BASE_APPLICATION, mDisplayContent, "dockedStackWindow");
- WindowState assistantStackWindow = createWindowOnStack(null, ASSISTANT_STACK_ID,
- TYPE_BASE_APPLICATION, mDisplayContent, "assistantStackWindow");
+ WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow");
+ WindowState dockedStackWindow = createWindowOnStack(null,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+ mDisplayContent, "dockedStackWindow");
+ WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+ mDisplayContent, "assistantStackWindow");
mLayersController.assignWindowLayers(mDisplayContent);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 0315c8d..1aafac6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -32,6 +32,7 @@
import static android.content.res.Configuration.EMPTY;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
/**
* A collection of static functions that can be referenced by other test packages to provide access
@@ -51,7 +52,10 @@
* Retrieves an instance of a mock {@link WindowManagerService}.
*/
public static WindowManagerService getMockWindowManagerService() {
- return mock(WindowManagerService.class);
+ final WindowManagerService service = mock(WindowManagerService.class);
+ final WindowHashMap windowMap = new WindowHashMap();
+ when(service.getWindowManagerLock()).thenReturn(windowMap);
+ return service;
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 3df13ab..4e810f2 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -16,7 +16,8 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId.getWindowingModeForStackId;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.View.VISIBLE;
@@ -167,14 +168,14 @@
sWm.mAnimationHandler.runWithScissors(() -> { }, 0);
}
- private WindowToken createWindowToken(DisplayContent dc, int stackId, int type) {
+ private WindowToken createWindowToken(
+ DisplayContent dc, int windowingMode, int activityType, int type) {
if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) {
return new WindowTestUtils.TestWindowToken(type, dc);
}
- final TaskStack stack = stackId == INVALID_STACK_ID
- ? createTaskStackOnDisplay(dc)
- : createStackControllerOnStackOnDisplay(stackId, dc).mContainer;
+ final TaskStack stack =
+ createStackControllerOnStackOnDisplay(windowingMode, activityType, dc).mContainer;
final Task task = createTaskInStack(stack, 0 /* userId */);
final WindowTestUtils.TestAppWindowToken token = new WindowTestUtils.TestAppWindowToken(dc);
task.addChild(token, 0);
@@ -187,9 +188,9 @@
: createWindow(parent, type, parent.mToken, name);
}
- WindowState createWindowOnStack(WindowState parent, int stackId, int type,
- DisplayContent dc, String name) {
- final WindowToken token = createWindowToken(dc, stackId, type);
+ WindowState createWindowOnStack(WindowState parent, int windowingMode, int activityType,
+ int type, DisplayContent dc, String name) {
+ final WindowToken token = createWindowToken(dc, windowingMode, activityType, type);
return createWindow(parent, type, token, name);
}
@@ -200,13 +201,15 @@
}
WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) {
- final WindowToken token = createWindowToken(dc, INVALID_STACK_ID, type);
+ final WindowToken token = createWindowToken(
+ dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
return createWindow(parent, type, token, name);
}
WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
boolean ownerCanAddInternalSystemWindow) {
- final WindowToken token = createWindowToken(dc, INVALID_STACK_ID, type);
+ final WindowToken token = createWindowToken(
+ dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
return createWindow(parent, type, token, name, ownerCanAddInternalSystemWindow);
}
@@ -233,14 +236,16 @@
}
StackWindowController createStackControllerOnDisplay(DisplayContent dc) {
- final int stackId = ++sNextStackId;
- return createStackControllerOnStackOnDisplay(stackId, dc);
+ return createStackControllerOnStackOnDisplay(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc);
}
- StackWindowController createStackControllerOnStackOnDisplay(int stackId, DisplayContent dc) {
+ StackWindowController createStackControllerOnStackOnDisplay(
+ int windowingMode, int activityType, DisplayContent dc) {
final Configuration overrideConfig = new Configuration();
- overrideConfig.windowConfiguration.setWindowingMode(
- getWindowingModeForStackId(stackId, false /* inSplitScreenMode */));
+ overrideConfig.windowConfiguration.setWindowingMode(windowingMode);
+ overrideConfig.windowConfiguration.setActivityType(activityType);
+ final int stackId = ++sNextStackId;
final StackWindowController controller = new StackWindowController(stackId, null,
dc.getDisplayId(), true /* onTop */, new Rect(), sWm);
controller.onOverrideConfigurationChanged(overrideConfig);
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
new file mode 100644
index 0000000..b2446ba
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -0,0 +1,987 @@
+/**
+ * Copyright (C) 2017 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.usage;
+
+import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
+import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
+import static com.android.server.usage.UsageStatsService.MSG_REPORT_EVENT;
+
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.admin.DevicePolicyManager;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.appwidget.AppWidgetManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
+import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
+import android.net.NetworkScoreManager;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.IDeviceIdleController;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages the standby state of an app, listening to various events.
+ */
+public class AppStandbyController {
+
+ private static final String TAG = "AppStandbyController";
+ private static final boolean DEBUG = false;
+
+ static final boolean COMPRESS_TIME = false;
+ private static final long ONE_MINUTE = 60 * 1000;
+
+ // To name the lock for stack traces
+ static class Lock {}
+
+ /** Lock to protect the app's standby state. Required for calls into AppIdleHistory */
+ private final Object mAppIdleLock = new Lock();
+
+ /** Keeps the history and state for each app. */
+ @GuardedBy("mAppIdleLock")
+ private AppIdleHistory mAppIdleHistory;
+
+ @GuardedBy("mAppIdleLock")
+ private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
+ mPackageAccessListeners = new ArrayList<>();
+
+ /** Whether we've queried the list of carrier privileged apps. */
+ @GuardedBy("mAppIdleLock")
+ private boolean mHaveCarrierPrivilegedApps;
+
+ /** List of carrier-privileged apps that should be excluded from standby */
+ @GuardedBy("mAppIdleLock")
+ private List<String> mCarrierPrivilegedApps;
+
+ // Messages for the handler
+ static final int MSG_INFORM_LISTENERS = 3;
+ static final int MSG_FORCE_IDLE_STATE = 4;
+ static final int MSG_CHECK_IDLE_STATES = 5;
+ static final int MSG_CHECK_PAROLE_TIMEOUT = 6;
+ static final int MSG_PAROLE_END_TIMEOUT = 7;
+ static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
+ static final int MSG_PAROLE_STATE_CHANGED = 9;
+ static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
+
+ long mAppIdleScreenThresholdMillis;
+ long mCheckIdleIntervalMillis;
+ long mAppIdleWallclockThresholdMillis;
+ long mAppIdleParoleIntervalMillis;
+ long mAppIdleParoleDurationMillis;
+ boolean mAppIdleEnabled;
+ boolean mAppIdleTempParoled;
+ boolean mCharging;
+ private long mLastAppIdleParoledTime;
+ private boolean mSystemServicesReady = false;
+
+ private volatile boolean mPendingOneTimeCheckIdleStates;
+
+ private final Handler mHandler;
+ private final Context mContext;
+
+ private DisplayManager mDisplayManager;
+ private IDeviceIdleController mDeviceIdleController;
+ private AppWidgetManager mAppWidgetManager;
+ private IBatteryStats mBatteryStats;
+ private PowerManager mPowerManager;
+ private PackageManager mPackageManager;
+ private PackageManagerInternal mPackageManagerInternal;
+
+ AppStandbyController(Context context, Looper looper) {
+ mContext = context;
+ mHandler = new AppStandbyHandler(looper);
+ mPackageManager = mContext.getPackageManager();
+ mAppIdleEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableAutoPowerModes);
+ if (mAppIdleEnabled) {
+ IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
+ deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+ mContext.registerReceiver(new DeviceStateReceiver(), deviceStates);
+ }
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
+ }
+
+ IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme("package");
+
+ mContext.registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
+ null, mHandler);
+ }
+
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ // Observe changes to the threshold
+ SettingsObserver settingsObserver = new SettingsObserver(mHandler);
+ settingsObserver.registerObserver();
+ settingsObserver.updateSettings();
+
+ mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
+ mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+ mBatteryStats = IBatteryStats.Stub.asInterface(
+ ServiceManager.getService(BatteryStats.SERVICE_NAME));
+ mDisplayManager = (DisplayManager) mContext.getSystemService(
+ Context.DISPLAY_SERVICE);
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+
+ mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.updateDisplay(isDisplayOn(), SystemClock.elapsedRealtime());
+ }
+
+ if (mPendingOneTimeCheckIdleStates) {
+ postOneTimeCheckIdleStates();
+ }
+
+ mSystemServicesReady = true;
+ } else if (phase == PHASE_BOOT_COMPLETED) {
+ setChargingState(mContext.getSystemService(BatteryManager.class).isCharging());
+ }
+ }
+
+ void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
+ // Get sync adapters for the authority
+ String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
+ authority, userId);
+ for (String packageName: packages) {
+ // Only force the sync adapters to active if the provider is not in the same package and
+ // the sync adapter is a system package.
+ try {
+ PackageInfo pi = mPackageManager.getPackageInfoAsUser(
+ packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
+ if (pi == null || pi.applicationInfo == null) {
+ continue;
+ }
+ if (!packageName.equals(providerPkgName)) {
+ setAppIdleAsync(packageName, false, userId);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Shouldn't happen
+ }
+ }
+ }
+
+ void setChargingState(boolean charging) {
+ synchronized (mAppIdleLock) {
+ if (mCharging != charging) {
+ mCharging = charging;
+ postParoleStateChanged();
+ }
+ }
+ }
+
+ /** Paroled here means temporary pardon from being inactive */
+ void setAppIdleParoled(boolean paroled) {
+ synchronized (mAppIdleLock) {
+ final long now = System.currentTimeMillis();
+ if (mAppIdleTempParoled != paroled) {
+ mAppIdleTempParoled = paroled;
+ if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled);
+ if (paroled) {
+ postParoleEndTimeout();
+ } else {
+ mLastAppIdleParoledTime = now;
+ postNextParoleTimeout(now);
+ }
+ postParoleStateChanged();
+ }
+ }
+ }
+
+ boolean isParoledOrCharging() {
+ synchronized (mAppIdleLock) {
+ return mAppIdleTempParoled || mCharging;
+ }
+ }
+
+ private void postNextParoleTimeout(long now) {
+ if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
+ mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
+ // Compute when the next parole needs to happen. We check more frequently than necessary
+ // since the message handler delays are based on elapsedRealTime and not wallclock time.
+ // The comparison is done in wallclock time.
+ long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now;
+ if (timeLeft < 0) {
+ timeLeft = 0;
+ }
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
+ }
+
+ private void postParoleEndTimeout() {
+ if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT");
+ mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT);
+ mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis);
+ }
+
+ private void postParoleStateChanged() {
+ if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
+ mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
+ mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
+ }
+
+ void postCheckIdleStates(int userId) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
+ }
+
+ /**
+ * We send a different message to check idle states once, otherwise we would end up
+ * scheduling a series of repeating checkIdleStates each time we fired off one.
+ */
+ void postOneTimeCheckIdleStates() {
+ if (mDeviceIdleController == null) {
+ // Not booted yet; wait for it!
+ mPendingOneTimeCheckIdleStates = true;
+ } else {
+ mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
+ mPendingOneTimeCheckIdleStates = false;
+ }
+ }
+
+ /**
+ * Check all running users' or specified user's apps to see if they enter an idle state.
+ * @return Returns whether checking should continue periodically.
+ */
+ boolean checkIdleStates(int checkUserId) {
+ if (!mAppIdleEnabled) {
+ return false;
+ }
+
+ final int[] runningUserIds;
+ try {
+ runningUserIds = ActivityManager.getService().getRunningUserIds();
+ if (checkUserId != UserHandle.USER_ALL
+ && !ArrayUtils.contains(runningUserIds, checkUserId)) {
+ return false;
+ }
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ for (int i = 0; i < runningUserIds.length; i++) {
+ final int userId = runningUserIds[i];
+ if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
+ continue;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Checking idle state for user " + userId);
+ }
+ List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
+ PackageManager.MATCH_DISABLED_COMPONENTS,
+ userId);
+ final int packageCount = packages.size();
+ for (int p = 0; p < packageCount; p++) {
+ final PackageInfo pi = packages.get(p);
+ final String packageName = pi.packageName;
+ final boolean isIdle = isAppIdleFiltered(packageName,
+ UserHandle.getAppId(pi.applicationInfo.uid),
+ userId, elapsedRealtime);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
+ userId, isIdle ? 1 : 0, packageName));
+ if (isIdle) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
+ }
+ }
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "checkIdleStates took "
+ + (SystemClock.elapsedRealtime() - elapsedRealtime));
+ }
+ return true;
+ }
+
+ /** Check if it's been a while since last parole and let idle apps do some work */
+ void checkParoleTimeout() {
+ boolean setParoled = false;
+ synchronized (mAppIdleLock) {
+ final long now = System.currentTimeMillis();
+ if (!mAppIdleTempParoled) {
+ final long timeSinceLastParole = now - mLastAppIdleParoledTime;
+ if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
+ if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
+ setParoled = true;
+ } else {
+ if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
+ postNextParoleTimeout(now);
+ }
+ }
+ }
+ if (setParoled) {
+ setAppIdleParoled(true);
+ }
+ }
+
+ private void notifyBatteryStats(String packageName, int userId, boolean idle) {
+ try {
+ final int uid = mPackageManager.getPackageUidAsUser(packageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ if (idle) {
+ mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
+ packageName, uid);
+ } else {
+ mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
+ packageName, uid);
+ }
+ } catch (PackageManager.NameNotFoundException | RemoteException e) {
+ }
+ }
+
+ void onDeviceIdleModeChanged() {
+ final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
+ if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
+ boolean paroled = false;
+ synchronized (mAppIdleLock) {
+ final long timeSinceLastParole = System.currentTimeMillis() - mLastAppIdleParoledTime;
+ if (!deviceIdle
+ && timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
+ if (DEBUG) {
+ Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
+ }
+ paroled = true;
+ } else if (deviceIdle) {
+ if (DEBUG) Slog.i(TAG, "Device idle, back to prison");
+ paroled = false;
+ } else {
+ return;
+ }
+ }
+ setAppIdleParoled(paroled);
+ }
+
+ void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) {
+ synchronized (mAppIdleLock) {
+ // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
+ // about apps that are on some kind of whitelist anyway.
+ final boolean previouslyIdle = mAppIdleHistory.isIdle(
+ event.mPackage, userId, elapsedRealtime);
+ // Inform listeners if necessary
+ if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
+ || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
+ || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
+ || event.mEventType == UsageEvents.Event.USER_INTERACTION)) {
+ mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
+ if (previouslyIdle) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+ /* idle = */ 0, event.mPackage));
+ notifyBatteryStats(event.mPackage, userId, false);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
+ * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
+ * the threshold for idle.
+ *
+ * This method is always called from the handler thread, so not much synchronization is
+ * required.
+ */
+ void forceIdleState(String packageName, int userId, boolean idle) {
+ final int appId = getAppId(packageName);
+ if (appId < 0) return;
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+
+ final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
+ userId, elapsedRealtime);
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
+ }
+ final boolean stillIdle = isAppIdleFiltered(packageName, appId,
+ userId, elapsedRealtime);
+ // Inform listeners if necessary
+ if (previouslyIdle != stillIdle) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+ /* idle = */ stillIdle ? 1 : 0, packageName));
+ if (!stillIdle) {
+ notifyBatteryStats(packageName, userId, idle);
+ }
+ }
+ }
+
+ public void onUserRemoved(int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.onUserRemoved(userId);
+ }
+ }
+
+ private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
+ }
+ }
+
+ void addListener(UsageStatsManagerInternal.AppIdleStateChangeListener listener) {
+ synchronized (mAppIdleLock) {
+ if (!mPackageAccessListeners.contains(listener)) {
+ mPackageAccessListeners.add(listener);
+ }
+ }
+ }
+
+ void removeListener(UsageStatsManagerInternal.AppIdleStateChangeListener listener) {
+ synchronized (mAppIdleLock) {
+ mPackageAccessListeners.remove(listener);
+ }
+ }
+
+ int getAppId(String packageName) {
+ try {
+ ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
+ PackageManager.MATCH_ANY_USER
+ | PackageManager.MATCH_DISABLED_COMPONENTS);
+ return ai.uid;
+ } catch (PackageManager.NameNotFoundException re) {
+ return -1;
+ }
+ }
+
+ boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime,
+ boolean shouldObfuscateInstantApps) {
+ if (isParoledOrCharging()) {
+ return false;
+ }
+ if (shouldObfuscateInstantApps &&
+ mPackageManagerInternal.isPackageEphemeral(userId, packageName)) {
+ return false;
+ }
+ return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
+ }
+
+ /**
+ * Checks if an app has been idle for a while and filters out apps that are excluded.
+ * It returns false if the current system state allows all apps to be considered active.
+ * This happens if the device is plugged in or temporarily allowed to make exceptions.
+ * Called by interface impls.
+ */
+ boolean isAppIdleFiltered(String packageName, int appId, int userId,
+ long elapsedRealtime) {
+ if (packageName == null) return false;
+ // If not enabled at all, of course nobody is ever idle.
+ if (!mAppIdleEnabled) {
+ return false;
+ }
+ if (appId < Process.FIRST_APPLICATION_UID) {
+ // System uids never go idle.
+ return false;
+ }
+ if (packageName.equals("android")) {
+ // Nor does the framework (which should be redundant with the above, but for MR1 we will
+ // retain this for safety).
+ return false;
+ }
+ if (mSystemServicesReady) {
+ try {
+ // We allow all whitelisted apps, including those that don't want to be whitelisted
+ // for idle mode, because app idle (aka app standby) is really not as big an issue
+ // for controlling who participates vs. doze mode.
+ if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
+ return false;
+ }
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+
+ if (isActiveDeviceAdmin(packageName, userId)) {
+ return false;
+ }
+
+ if (isActiveNetworkScorer(packageName)) {
+ return false;
+ }
+
+ if (mAppWidgetManager != null
+ && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
+ return false;
+ }
+
+ if (isDeviceProvisioningPackage(packageName)) {
+ return false;
+ }
+ }
+
+ if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
+ return false;
+ }
+
+ // Check this last, as it is the most expensive check
+ // TODO: Optimize this by fetching the carrier privileged apps ahead of time
+ if (isCarrierApp(packageName)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ int[] getIdleUidsForUser(int userId) {
+ if (!mAppIdleEnabled) {
+ return new int[0];
+ }
+
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+
+ List<ApplicationInfo> apps;
+ try {
+ ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
+ .getInstalledApplications(/* flags= */ 0, userId);
+ if (slice == null) {
+ return new int[0];
+ }
+ apps = slice.getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ // State of each uid. Key is the uid. Value lower 16 bits is the number of apps
+ // associated with that uid, upper 16 bits is the number of those apps that is idle.
+ SparseIntArray uidStates = new SparseIntArray();
+
+ // Now resolve all app state. Iterating over all apps, keeping track of how many
+ // we find for each uid and how many of those are idle.
+ for (int i = apps.size() - 1; i >= 0; i--) {
+ ApplicationInfo ai = apps.get(i);
+
+ // Check whether this app is idle.
+ boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
+ userId, elapsedRealtime);
+
+ int index = uidStates.indexOfKey(ai.uid);
+ if (index < 0) {
+ uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
+ } else {
+ int value = uidStates.valueAt(index);
+ uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "getIdleUids took " + (SystemClock.elapsedRealtime() - elapsedRealtime));
+ }
+ int numIdle = 0;
+ for (int i = uidStates.size() - 1; i >= 0; i--) {
+ int value = uidStates.valueAt(i);
+ if ((value&0x7fff) == (value>>16)) {
+ numIdle++;
+ }
+ }
+
+ int[] res = new int[numIdle];
+ numIdle = 0;
+ for (int i = uidStates.size() - 1; i >= 0; i--) {
+ int value = uidStates.valueAt(i);
+ if ((value&0x7fff) == (value>>16)) {
+ res[numIdle] = uidStates.keyAt(i);
+ numIdle++;
+ }
+ }
+
+ return res;
+ }
+
+ void setAppIdleAsync(String packageName, boolean idle, int userId) {
+ if (packageName == null) return;
+
+ mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
+ .sendToTarget();
+ }
+
+ private boolean isActiveDeviceAdmin(String packageName, int userId) {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ if (dpm == null) return false;
+ return dpm.packageHasActiveAdmins(packageName, userId);
+ }
+
+ /**
+ * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
+ * returns {@code false}.
+ */
+ private boolean isDeviceProvisioningPackage(String packageName) {
+ String deviceProvisioningPackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_deviceProvisioningPackage);
+ return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
+ }
+
+ private boolean isCarrierApp(String packageName) {
+ synchronized (mAppIdleLock) {
+ if (!mHaveCarrierPrivilegedApps) {
+ fetchCarrierPrivilegedAppsLA();
+ }
+ if (mCarrierPrivilegedApps != null) {
+ return mCarrierPrivilegedApps.contains(packageName);
+ }
+ return false;
+ }
+ }
+
+ void clearCarrierPrivilegedApps() {
+ if (DEBUG) {
+ Slog.i(TAG, "Clearing carrier privileged apps list");
+ }
+ synchronized (mAppIdleLock) {
+ mHaveCarrierPrivilegedApps = false;
+ mCarrierPrivilegedApps = null; // Need to be refetched.
+ }
+ }
+
+ @GuardedBy("mAppIdleLock")
+ private void fetchCarrierPrivilegedAppsLA() {
+ TelephonyManager telephonyManager =
+ mContext.getSystemService(TelephonyManager.class);
+ mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
+ mHaveCarrierPrivilegedApps = true;
+ if (DEBUG) {
+ Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
+ }
+ }
+
+ private boolean isActiveNetworkScorer(String packageName) {
+ NetworkScoreManager nsm = (NetworkScoreManager) mContext.getSystemService(
+ Context.NETWORK_SCORE_SERVICE);
+ return packageName != null && packageName.equals(nsm.getActiveScorerPackage());
+ }
+
+ void informListeners(String packageName, int userId, boolean isIdle) {
+ for (UsageStatsManagerInternal.AppIdleStateChangeListener listener : mPackageAccessListeners) {
+ listener.onAppIdleStateChanged(packageName, userId, isIdle);
+ }
+ }
+
+ void informParoleStateChanged() {
+ final boolean paroled = isParoledOrCharging();
+ for (UsageStatsManagerInternal.AppIdleStateChangeListener listener : mPackageAccessListeners) {
+ listener.onParoleStateChanged(paroled);
+ }
+ }
+
+ void flushToDisk(int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.writeAppIdleTimes(userId);
+ }
+ }
+
+ void flushDurationsToDisk() {
+ // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
+ // considered not-idle, which is the safest outcome in such an event.
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.writeAppIdleDurations();
+ }
+ }
+
+ boolean isDisplayOn() {
+ return mDisplayManager
+ .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
+ }
+
+ void clearAppIdleForPackage(String packageName, int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.clearUsage(packageName, userId);
+ }
+ }
+
+ private class PackageReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+ || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+ clearCarrierPrivilegedApps();
+ }
+ if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
+ Intent.ACTION_PACKAGE_ADDED.equals(action))
+ && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
+ getSendingUserId());
+ }
+ }
+ }
+
+ void initializeDefaultsForSystemApps(int userId) {
+ Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
+ PackageManager.MATCH_DISABLED_COMPONENTS,
+ userId);
+ final int packageCount = packages.size();
+ synchronized (mAppIdleLock) {
+ for (int i = 0; i < packageCount; i++) {
+ final PackageInfo pi = packages.get(i);
+ String packageName = pi.packageName;
+ if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
+ mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime);
+ }
+ }
+ }
+ }
+
+ void postReportContentProviderUsage(String name, String packageName, int userId) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = name;
+ args.arg2 = packageName;
+ args.arg3 = userId;
+ mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args)
+ .sendToTarget();
+ }
+
+ void dumpHistory(IndentingPrintWriter idpw, int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.dumpHistory(idpw, userId);
+ }
+ }
+
+ void dumpUser(IndentingPrintWriter idpw, int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.dump(idpw, userId);
+ }
+ }
+
+ void dumpState(String[] args, PrintWriter pw) {
+ synchronized (mAppIdleLock) {
+ pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
+ + "): " + mCarrierPrivilegedApps);
+ }
+
+ pw.println();
+ pw.println("Settings:");
+
+ pw.print(" mAppIdleDurationMillis=");
+ TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
+ pw.println();
+
+ pw.print(" mAppIdleWallclockThresholdMillis=");
+ TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
+ pw.println();
+
+ pw.print(" mCheckIdleIntervalMillis=");
+ TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
+ pw.println();
+
+ pw.print(" mAppIdleParoleIntervalMillis=");
+ TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw);
+ pw.println();
+
+ pw.print(" mAppIdleParoleDurationMillis=");
+ TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw);
+ pw.println();
+
+ pw.println();
+ pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
+ pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
+ pw.print(" mCharging="); pw.print(mCharging);
+ pw.print(" mLastAppIdleParoledTime=");
+ TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
+ pw.println();
+ }
+
+ class AppStandbyHandler extends Handler {
+
+ AppStandbyHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_FORCE_IDLE_STATE:
+ forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
+ break;
+
+ case MSG_CHECK_IDLE_STATES:
+ if (checkIdleStates(msg.arg1)) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ MSG_CHECK_IDLE_STATES, msg.arg1, 0),
+ mCheckIdleIntervalMillis);
+ }
+ break;
+
+ case MSG_ONE_TIME_CHECK_IDLE_STATES:
+ mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
+ checkIdleStates(UserHandle.USER_ALL);
+ break;
+
+ case MSG_CHECK_PAROLE_TIMEOUT:
+ checkParoleTimeout();
+ break;
+
+ case MSG_PAROLE_END_TIMEOUT:
+ if (DEBUG) Slog.d(TAG, "Ending parole");
+ setAppIdleParoled(false);
+ break;
+
+ case MSG_REPORT_CONTENT_PROVIDER_USAGE:
+ SomeArgs args = (SomeArgs) msg.obj;
+ reportContentProviderUsage((String) args.arg1, // authority name
+ (String) args.arg2, // package name
+ (int) args.arg3); // userId
+ args.recycle();
+ break;
+
+ case MSG_PAROLE_STATE_CHANGED:
+ if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
+ + ", Charging state:" + mCharging);
+ informParoleStateChanged();
+ break;
+ default:
+ super.handleMessage(msg);
+ break;
+
+ }
+ }
+ };
+
+ private class DeviceStateReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+ setChargingState(intent.getIntExtra("plugged", 0) != 0);
+ } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
+ onDeviceIdleModeChanged();
+ }
+ }
+ }
+
+ private final DisplayManager.DisplayListener mDisplayListener
+ = new DisplayManager.DisplayListener() {
+
+ @Override public void onDisplayAdded(int displayId) {
+ }
+
+ @Override public void onDisplayRemoved(int displayId) {
+ }
+
+ @Override public void onDisplayChanged(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ final boolean displayOn = isDisplayOn();
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.updateDisplay(displayOn, SystemClock.elapsedRealtime());
+ }
+ }
+ }
+ };
+
+ /**
+ * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}.
+ */
+ private class SettingsObserver extends ContentObserver {
+ /**
+ * This flag has been used to disable app idle on older builds with bug b/26355386.
+ */
+ @Deprecated
+ private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
+
+ private static final String KEY_IDLE_DURATION = "idle_duration2";
+ private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
+ private static final String KEY_PAROLE_INTERVAL = "parole_interval";
+ private static final String KEY_PAROLE_DURATION = "parole_duration";
+
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void registerObserver() {
+ mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.APP_IDLE_CONSTANTS), false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ updateSettings();
+ postOneTimeCheckIdleStates();
+ }
+
+ void updateSettings() {
+ synchronized (mAppIdleLock) {
+ // Look at global settings for this.
+ // TODO: Maybe apply different thresholds for different users.
+ try {
+ mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.APP_IDLE_CONSTANTS));
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
+ // fallthrough, mParser is empty and all defaults will be returned.
+ }
+
+ // Default: 12 hours of screen-on time sans dream-time
+ mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
+
+ mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
+ COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
+
+ mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
+ COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
+
+ // Default: 24 hours between paroles
+ mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
+ COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
+
+ mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
+ mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
+ mAppIdleScreenThresholdMillis);
+ }
+ }
+ }
+
+}
+
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 25e471c..afafea1 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -18,37 +18,24 @@
import android.Manifest;
import android.app.ActivityManager;
-import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IUidObserver;
-import android.app.admin.DevicePolicyManager;
import android.app.usage.ConfigurationStats;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManagerInternal;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
-import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
-import android.database.ContentObserver;
-import android.hardware.display.DisplayManager;
-import android.net.NetworkScoreManager;
-import android.os.BatteryManager;
-import android.os.BatteryStats;
import android.os.Binder;
import android.os.Environment;
import android.os.FileUtils;
@@ -56,7 +43,6 @@
import android.os.IDeviceIdleController;
import android.os.Looper;
import android.os.Message;
-import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -64,21 +50,12 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
-import android.telephony.TelephonyManager;
import android.util.ArraySet;
-import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
-import android.util.TimeUtils;
-import android.view.Display;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
@@ -88,7 +65,6 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -107,7 +83,6 @@
static final boolean COMPRESS_TIME = false;
private static final long TEN_SECONDS = 10 * 1000;
- private static final long ONE_MINUTE = 60 * 1000;
private static final long TWENTY_MINUTES = 20 * 60 * 1000;
private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES;
private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
@@ -115,24 +90,10 @@
private static final boolean ENABLE_KERNEL_UPDATES = true;
private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set");
- long mAppIdleScreenThresholdMillis;
- long mCheckIdleIntervalMillis;
- long mAppIdleWallclockThresholdMillis;
- long mAppIdleParoleIntervalMillis;
- long mAppIdleParoleDurationMillis;
-
// Handler message types.
static final int MSG_REPORT_EVENT = 0;
static final int MSG_FLUSH_TO_DISK = 1;
static final int MSG_REMOVE_USER = 2;
- static final int MSG_INFORM_LISTENERS = 3;
- static final int MSG_FORCE_IDLE_STATE = 4;
- static final int MSG_CHECK_IDLE_STATES = 5;
- static final int MSG_CHECK_PAROLE_TIMEOUT = 6;
- static final int MSG_PAROLE_END_TIMEOUT = 7;
- static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
- static final int MSG_PAROLE_STATE_CHANGED = 9;
- static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
private final Object mLock = new Object();
Handler mHandler;
@@ -140,11 +101,7 @@
UserManager mUserManager;
PackageManager mPackageManager;
PackageManagerInternal mPackageManagerInternal;
- AppWidgetManager mAppWidgetManager;
IDeviceIdleController mDeviceIdleController;
- private DisplayManager mDisplayManager;
- private PowerManager mPowerManager;
- private IBatteryStats mBatteryStats;
private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
private final SparseIntArray mUidToKernelCounter = new SparseIntArray();
@@ -152,26 +109,7 @@
long mRealTimeSnapshot;
long mSystemTimeSnapshot;
- boolean mAppIdleEnabled;
- boolean mAppIdleTempParoled;
- boolean mCharging;
- private long mLastAppIdleParoledTime;
-
- private volatile boolean mPendingOneTimeCheckIdleStates;
- private boolean mSystemServicesReady = false;
-
- private final Object mAppIdleLock = new Object();
- @GuardedBy("mAppIdleLock")
- private AppIdleHistory mAppIdleHistory;
-
- @GuardedBy("mAppIdleLock")
- private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
- mPackageAccessListeners = new ArrayList<>();
-
- @GuardedBy("mAppIdleLock")
- private boolean mHaveCarrierPrivilegedApps;
- @GuardedBy("mAppIdleLock")
- private List<String> mCarrierPrivilegedApps;
+ AppStandbyController mAppStandby;
public UsageStatsService(Context context) {
super(context);
@@ -185,6 +123,8 @@
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mHandler = new H(BackgroundThread.get().getLooper());
+ mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper());
+
File systemDataDir = new File(Environment.getDataDirectory(), "system");
mUsageStatsDir = new File(systemDataDir, "usagestats");
mUsageStatsDir.mkdirs();
@@ -198,30 +138,9 @@
getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter,
null, mHandler);
- IntentFilter packageFilter = new IntentFilter();
- packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- packageFilter.addDataScheme("package");
-
- getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
- null, mHandler);
-
- mAppIdleEnabled = getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_enableAutoPowerModes);
- if (mAppIdleEnabled) {
- IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
- deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
- deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
- getContext().registerReceiver(new DeviceStateReceiver(), deviceStates);
- }
-
synchronized (mLock) {
cleanUpRemovedUsersLocked();
}
- synchronized (mAppIdleLock) {
- mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
- }
mRealTimeSnapshot = SystemClock.elapsedRealtime();
mSystemTimeSnapshot = System.currentTimeMillis();
@@ -233,28 +152,10 @@
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
- // Observe changes to the threshold
- SettingsObserver settingsObserver = new SettingsObserver(mHandler);
- settingsObserver.registerObserver();
- settingsObserver.updateSettings();
+ mAppStandby.onBootPhase(phase);
- mAppWidgetManager = getContext().getSystemService(AppWidgetManager.class);
mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
- mBatteryStats = IBatteryStats.Stub.asInterface(
- ServiceManager.getService(BatteryStats.SERVICE_NAME));
- mDisplayManager = (DisplayManager) getContext().getSystemService(
- Context.DISPLAY_SERVICE);
- mPowerManager = getContext().getSystemService(PowerManager.class);
-
- mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
- synchronized (mAppIdleLock) {
- mAppIdleHistory.updateDisplay(isDisplayOn(), SystemClock.elapsedRealtime());
- }
-
- if (mPendingOneTimeCheckIdleStates) {
- postOneTimeCheckIdleStates();
- }
if (ENABLE_KERNEL_UPDATES && KERNEL_COUNTER_FILE.exists()) {
try {
@@ -268,18 +169,9 @@
} else {
Slog.w(TAG, "Missing procfs interface: " + KERNEL_COUNTER_FILE);
}
-
- mSystemServicesReady = true;
- } else if (phase == PHASE_BOOT_COMPLETED) {
- setChargingState(getContext().getSystemService(BatteryManager.class).isCharging());
}
}
- private boolean isDisplayOn() {
- return mDisplayManager
- .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
- }
-
private class UserActionsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -291,60 +183,12 @@
}
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
if (userId >=0) {
- postCheckIdleStates(userId);
+ mAppStandby.postCheckIdleStates(userId);
}
}
}
}
- private class PackageReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_PACKAGE_ADDED.equals(action)
- || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
- clearCarrierPrivilegedApps();
- }
- if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
- Intent.ACTION_PACKAGE_ADDED.equals(action))
- && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
- getSendingUserId());
- }
- }
- }
-
- private class DeviceStateReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
- setChargingState(intent.getIntExtra("plugged", 0) != 0);
- } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
- onDeviceIdleModeChanged();
- }
- }
- }
-
- private final DisplayManager.DisplayListener mDisplayListener
- = new DisplayManager.DisplayListener() {
-
- @Override public void onDisplayAdded(int displayId) {
- }
-
- @Override public void onDisplayRemoved(int displayId) {
- }
-
- @Override public void onDisplayChanged(int displayId) {
- if (displayId == Display.DEFAULT_DISPLAY) {
- final boolean displayOn = isDisplayOn();
- synchronized (UsageStatsService.this.mAppIdleLock) {
- mAppIdleHistory.updateDisplay(displayOn, SystemClock.elapsedRealtime());
- }
- }
- }
- };
-
private final IUidObserver mUidObserver = new IUidObserver.Stub() {
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq) {
@@ -388,42 +232,18 @@
@Override
public void onStatsReloaded() {
- postOneTimeCheckIdleStates();
+ mAppStandby.postOneTimeCheckIdleStates();
}
@Override
public void onNewUpdate(int userId) {
- initializeDefaultsForSystemApps(userId);
- }
-
- private void initializeDefaultsForSystemApps(int userId) {
- Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
- PackageManager.MATCH_DISABLED_COMPONENTS,
- userId);
- final int packageCount = packages.size();
- synchronized (mAppIdleLock) {
- for (int i = 0; i < packageCount; i++) {
- final PackageInfo pi = packages.get(i);
- String packageName = pi.packageName;
- if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
- mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime);
- }
- }
- }
+ mAppStandby.initializeDefaultsForSystemApps(userId);
}
private boolean shouldObfuscateInstantAppsForCaller(int callingUid, int userId) {
return !mPackageManagerInternal.canAccessInstantApps(callingUid, userId);
}
- void clearAppIdleForPackage(String packageName, int userId) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.clearUsage(packageName, userId);
- }
- }
-
private void cleanUpRemovedUsersLocked() {
final List<UserInfo> users = mUserManager.getUsers(true);
if (users == null || users.size() == 0) {
@@ -451,195 +271,6 @@
}
}
- void setChargingState(boolean charging) {
- synchronized (mAppIdleLock) {
- if (mCharging != charging) {
- mCharging = charging;
- postParoleStateChanged();
- }
- }
- }
-
- /** Paroled here means temporary pardon from being inactive */
- void setAppIdleParoled(boolean paroled) {
- synchronized (mAppIdleLock) {
- final long now = System.currentTimeMillis();
- if (mAppIdleTempParoled != paroled) {
- mAppIdleTempParoled = paroled;
- if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled);
- if (paroled) {
- postParoleEndTimeout();
- } else {
- mLastAppIdleParoledTime = now;
- postNextParoleTimeout(now);
- }
- postParoleStateChanged();
- }
- }
- }
-
- boolean isParoledOrCharging() {
- synchronized (mAppIdleLock) {
- return mAppIdleTempParoled || mCharging;
- }
- }
-
- private void postNextParoleTimeout(long now) {
- if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
- mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
- // Compute when the next parole needs to happen. We check more frequently than necessary
- // since the message handler delays are based on elapsedRealTime and not wallclock time.
- // The comparison is done in wallclock time.
- long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now;
- if (timeLeft < 0) {
- timeLeft = 0;
- }
- mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
- }
-
- private void postParoleEndTimeout() {
- if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT");
- mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT);
- mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis);
- }
-
- private void postParoleStateChanged() {
- if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
- mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
- mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
- }
-
- void postCheckIdleStates(int userId) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
- }
-
- /**
- * We send a different message to check idle states once, otherwise we would end up
- * scheduling a series of repeating checkIdleStates each time we fired off one.
- */
- void postOneTimeCheckIdleStates() {
- if (mDeviceIdleController == null) {
- // Not booted yet; wait for it!
- mPendingOneTimeCheckIdleStates = true;
- } else {
- mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
- mPendingOneTimeCheckIdleStates = false;
- }
- }
-
- /**
- * Check all running users' or specified user's apps to see if they enter an idle state.
- * @return Returns whether checking should continue periodically.
- */
- boolean checkIdleStates(int checkUserId) {
- if (!mAppIdleEnabled) {
- return false;
- }
-
- final int[] runningUserIds;
- try {
- runningUserIds = ActivityManager.getService().getRunningUserIds();
- if (checkUserId != UserHandle.USER_ALL
- && !ArrayUtils.contains(runningUserIds, checkUserId)) {
- return false;
- }
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
-
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- for (int i = 0; i < runningUserIds.length; i++) {
- final int userId = runningUserIds[i];
- if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
- continue;
- }
- if (DEBUG) {
- Slog.d(TAG, "Checking idle state for user " + userId);
- }
- List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
- PackageManager.MATCH_DISABLED_COMPONENTS,
- userId);
- final int packageCount = packages.size();
- for (int p = 0; p < packageCount; p++) {
- final PackageInfo pi = packages.get(p);
- final String packageName = pi.packageName;
- final boolean isIdle = isAppIdleFiltered(packageName,
- UserHandle.getAppId(pi.applicationInfo.uid),
- userId, elapsedRealtime);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
- userId, isIdle ? 1 : 0, packageName));
- if (isIdle) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
- }
- }
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "checkIdleStates took "
- + (SystemClock.elapsedRealtime() - elapsedRealtime));
- }
- return true;
- }
-
- /** Check if it's been a while since last parole and let idle apps do some work */
- void checkParoleTimeout() {
- boolean setParoled = false;
- synchronized (mAppIdleLock) {
- final long now = System.currentTimeMillis();
- if (!mAppIdleTempParoled) {
- final long timeSinceLastParole = now - mLastAppIdleParoledTime;
- if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
- if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
- setParoled = true;
- } else {
- if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
- postNextParoleTimeout(now);
- }
- }
- }
- if (setParoled) {
- setAppIdleParoled(true);
- }
- }
-
- private void notifyBatteryStats(String packageName, int userId, boolean idle) {
- try {
- final int uid = mPackageManager.getPackageUidAsUser(packageName,
- PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
- if (idle) {
- mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
- packageName, uid);
- } else {
- mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
- packageName, uid);
- }
- } catch (NameNotFoundException | RemoteException e) {
- }
- }
-
- void onDeviceIdleModeChanged() {
- final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
- if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
- boolean paroled = false;
- synchronized (mAppIdleLock) {
- final long timeSinceLastParole = System.currentTimeMillis() - mLastAppIdleParoledTime;
- if (!deviceIdle
- && timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
- if (DEBUG) {
- Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
- }
- paroled = true;
- } else if (deviceIdle) {
- if (DEBUG) Slog.i(TAG, "Device idle, back to prison");
- paroled = false;
- } else {
- return;
- }
- }
- setAppIdleParoled(paroled);
- }
-
private static void deleteRecursively(File f) {
File[] files = f.listFiles();
if (files != null) {
@@ -724,76 +355,7 @@
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
service.reportEvent(event);
- synchronized (mAppIdleLock) {
- // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
- // about apps that are on some kind of whitelist anyway.
- final boolean previouslyIdle = mAppIdleHistory.isIdle(
- event.mPackage, userId, elapsedRealtime);
- // Inform listeners if necessary
- if ((event.mEventType == Event.MOVE_TO_FOREGROUND
- || event.mEventType == Event.MOVE_TO_BACKGROUND
- || event.mEventType == Event.SYSTEM_INTERACTION
- || event.mEventType == Event.USER_INTERACTION)) {
- mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
- if (previouslyIdle) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
- /* idle = */ 0, event.mPackage));
- notifyBatteryStats(event.mPackage, userId, false);
- }
- }
- }
- }
- }
-
- void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
- // Get sync adapters for the authority
- String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
- authority, userId);
- for (String packageName: packages) {
- // Only force the sync adapters to active if the provider is not in the same package and
- // the sync adapter is a system package.
- try {
- PackageInfo pi = mPackageManager.getPackageInfoAsUser(
- packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
- if (pi == null || pi.applicationInfo == null) {
- continue;
- }
- if (!packageName.equals(providerPkgName)) {
- setAppIdleAsync(packageName, false, userId);
- }
- } catch (NameNotFoundException e) {
- // Shouldn't happen
- }
- }
- }
-
- /**
- * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
- * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
- * the threshold for idle.
- *
- * This method is always called from the handler thread, so not much synchronization is
- * required.
- */
- void forceIdleState(String packageName, int userId, boolean idle) {
- final int appId = getAppId(packageName);
- if (appId < 0) return;
- final long elapsedRealtime = SystemClock.elapsedRealtime();
-
- final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
- userId, elapsedRealtime);
- synchronized (mAppIdleLock) {
- mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
- }
- final boolean stillIdle = isAppIdleFiltered(packageName, appId,
- userId, elapsedRealtime);
- // Inform listeners if necessary
- if (previouslyIdle != stillIdle) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
- /* idle = */ stillIdle ? 1 : 0, packageName));
- if (!stillIdle) {
- notifyBatteryStats(packageName, userId, idle);
- }
+ mAppStandby.reportEvent(event, elapsedRealtime, userId);
}
}
@@ -813,9 +375,7 @@
synchronized (mLock) {
Slog.i(TAG, "Removing user " + userId + " and all data.");
mUserState.remove(userId);
- synchronized (mAppIdleLock) {
- mAppIdleHistory.onUserRemoved(userId);
- }
+ mAppStandby.onUserRemoved(userId);
cleanUpRemovedUsersLocked();
}
}
@@ -887,253 +447,6 @@
}
}
- private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
- synchronized (mAppIdleLock) {
- return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
- }
- }
-
- void addListener(AppIdleStateChangeListener listener) {
- synchronized (mAppIdleLock) {
- if (!mPackageAccessListeners.contains(listener)) {
- mPackageAccessListeners.add(listener);
- }
- }
- }
-
- void removeListener(AppIdleStateChangeListener listener) {
- synchronized (mAppIdleLock) {
- mPackageAccessListeners.remove(listener);
- }
- }
-
- int getAppId(String packageName) {
- try {
- ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
- PackageManager.MATCH_ANY_USER
- | PackageManager.MATCH_DISABLED_COMPONENTS);
- return ai.uid;
- } catch (NameNotFoundException re) {
- return -1;
- }
- }
-
- boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime,
- boolean shouldObfuscateInstantApps) {
- if (isParoledOrCharging()) {
- return false;
- }
- if (shouldObfuscateInstantApps &&
- mPackageManagerInternal.isPackageEphemeral(userId, packageName)) {
- return false;
- }
- return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
- }
-
- /**
- * Checks if an app has been idle for a while and filters out apps that are excluded.
- * It returns false if the current system state allows all apps to be considered active.
- * This happens if the device is plugged in or temporarily allowed to make exceptions.
- * Called by interface impls.
- */
- private boolean isAppIdleFiltered(String packageName, int appId, int userId,
- long elapsedRealtime) {
- if (packageName == null) return false;
- // If not enabled at all, of course nobody is ever idle.
- if (!mAppIdleEnabled) {
- return false;
- }
- if (appId < Process.FIRST_APPLICATION_UID) {
- // System uids never go idle.
- return false;
- }
- if (packageName.equals("android")) {
- // Nor does the framework (which should be redundant with the above, but for MR1 we will
- // retain this for safety).
- return false;
- }
- if (mSystemServicesReady) {
- try {
- // We allow all whitelisted apps, including those that don't want to be whitelisted
- // for idle mode, because app idle (aka app standby) is really not as big an issue
- // for controlling who participates vs. doze mode.
- if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
- return false;
- }
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
-
- if (isActiveDeviceAdmin(packageName, userId)) {
- return false;
- }
-
- if (isActiveNetworkScorer(packageName)) {
- return false;
- }
-
- if (mAppWidgetManager != null
- && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
- return false;
- }
-
- if (isDeviceProvisioningPackage(packageName)) {
- return false;
- }
- }
-
- if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
- return false;
- }
-
- // Check this last, as it is the most expensive check
- // TODO: Optimize this by fetching the carrier privileged apps ahead of time
- if (isCarrierApp(packageName)) {
- return false;
- }
-
- return true;
- }
-
- int[] getIdleUidsForUser(int userId) {
- if (!mAppIdleEnabled) {
- return new int[0];
- }
-
- final long elapsedRealtime = SystemClock.elapsedRealtime();
-
- List<ApplicationInfo> apps;
- try {
- ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
- .getInstalledApplications(/* flags= */ 0, userId);
- if (slice == null) {
- return new int[0];
- }
- apps = slice.getList();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- // State of each uid. Key is the uid. Value lower 16 bits is the number of apps
- // associated with that uid, upper 16 bits is the number of those apps that is idle.
- SparseIntArray uidStates = new SparseIntArray();
-
- // Now resolve all app state. Iterating over all apps, keeping track of how many
- // we find for each uid and how many of those are idle.
- for (int i = apps.size() - 1; i >= 0; i--) {
- ApplicationInfo ai = apps.get(i);
-
- // Check whether this app is idle.
- boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
- userId, elapsedRealtime);
-
- int index = uidStates.indexOfKey(ai.uid);
- if (index < 0) {
- uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
- } else {
- int value = uidStates.valueAt(index);
- uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "getIdleUids took " + (SystemClock.elapsedRealtime() - elapsedRealtime));
- }
- int numIdle = 0;
- for (int i = uidStates.size() - 1; i >= 0; i--) {
- int value = uidStates.valueAt(i);
- if ((value&0x7fff) == (value>>16)) {
- numIdle++;
- }
- }
-
- int[] res = new int[numIdle];
- numIdle = 0;
- for (int i = uidStates.size() - 1; i >= 0; i--) {
- int value = uidStates.valueAt(i);
- if ((value&0x7fff) == (value>>16)) {
- res[numIdle] = uidStates.keyAt(i);
- numIdle++;
- }
- }
-
- return res;
- }
-
- void setAppIdleAsync(String packageName, boolean idle, int userId) {
- if (packageName == null) return;
-
- mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
- .sendToTarget();
- }
-
- private boolean isActiveDeviceAdmin(String packageName, int userId) {
- DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
- if (dpm == null) return false;
- return dpm.packageHasActiveAdmins(packageName, userId);
- }
-
- /**
- * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
- * returns {@code false}.
- */
- private boolean isDeviceProvisioningPackage(String packageName) {
- String deviceProvisioningPackage = getContext().getResources().getString(
- com.android.internal.R.string.config_deviceProvisioningPackage);
- return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
- }
-
- private boolean isCarrierApp(String packageName) {
- synchronized (mAppIdleLock) {
- if (!mHaveCarrierPrivilegedApps) {
- fetchCarrierPrivilegedAppsLA();
- }
- if (mCarrierPrivilegedApps != null) {
- return mCarrierPrivilegedApps.contains(packageName);
- }
- return false;
- }
- }
-
- void clearCarrierPrivilegedApps() {
- if (DEBUG) {
- Slog.i(TAG, "Clearing carrier privileged apps list");
- }
- synchronized (mAppIdleLock) {
- mHaveCarrierPrivilegedApps = false;
- mCarrierPrivilegedApps = null; // Need to be refetched.
- }
- }
-
- @GuardedBy("mAppIdleLock")
- private void fetchCarrierPrivilegedAppsLA() {
- TelephonyManager telephonyManager =
- getContext().getSystemService(TelephonyManager.class);
- mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
- mHaveCarrierPrivilegedApps = true;
- if (DEBUG) {
- Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
- }
- }
-
- private boolean isActiveNetworkScorer(String packageName) {
- NetworkScoreManager nsm = (NetworkScoreManager) getContext().getSystemService(
- Context.NETWORK_SCORE_SERVICE);
- return packageName != null && packageName.equals(nsm.getActiveScorerPackage());
- }
-
- void informListeners(String packageName, int userId, boolean isIdle) {
- for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onAppIdleStateChanged(packageName, userId, isIdle);
- }
- }
-
- void informParoleStateChanged() {
- final boolean paroled = isParoledOrCharging();
- for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onParoleStateChanged(paroled);
- }
- }
-
private static boolean validRange(long currentTime, long beginTime, long endTime) {
return beginTime <= currentTime && beginTime < endTime;
}
@@ -1143,15 +456,10 @@
for (int i = 0; i < userCount; i++) {
UserUsageStatsService service = mUserState.valueAt(i);
service.persistActiveStats();
- synchronized (mAppIdleLock) {
- mAppIdleHistory.writeAppIdleTimes(mUserState.keyAt(i));
- }
+ mAppStandby.flushToDisk(mUserState.keyAt(i));
}
- // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
- // considered not-idle, which is the safest outcome in such an event.
- synchronized (mAppIdleLock) {
- mAppIdleHistory.writeAppIdleDurations();
- }
+ mAppStandby.flushDurationsToDisk();
+
mHandler.removeMessages(MSG_FLUSH_TO_DISK);
}
@@ -1166,7 +474,8 @@
final int userCount = mUserState.size();
for (int i = 0; i < userCount; i++) {
- idpw.printPair("user", mUserState.keyAt(i));
+ int userId = mUserState.keyAt(i);
+ idpw.printPair("user", userId);
idpw.println();
idpw.increaseIndent();
if (argSet.contains("--checkin")) {
@@ -1176,57 +485,19 @@
idpw.println();
if (args.length > 0) {
if ("history".equals(args[0])) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.dumpHistory(idpw, mUserState.keyAt(i));
- }
+ mAppStandby.dumpHistory(idpw, userId);
} else if ("flush".equals(args[0])) {
- UsageStatsService.this.flushToDiskLocked();
+ flushToDiskLocked();
pw.println("Flushed stats to disk");
}
}
}
- synchronized (mAppIdleLock) {
- mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
- }
+ mAppStandby.dumpUser(idpw, userId);
idpw.decreaseIndent();
}
pw.println();
- synchronized (mAppIdleLock) {
- pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
- + "): " + mCarrierPrivilegedApps);
- }
-
- pw.println();
- pw.println("Settings:");
-
- pw.print(" mAppIdleDurationMillis=");
- TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
- pw.println();
-
- pw.print(" mAppIdleWallclockThresholdMillis=");
- TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
- pw.println();
-
- pw.print(" mCheckIdleIntervalMillis=");
- TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
- pw.println();
-
- pw.print(" mAppIdleParoleIntervalMillis=");
- TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw);
- pw.println();
-
- pw.print(" mAppIdleParoleDurationMillis=");
- TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw);
- pw.println();
-
- pw.println();
- pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
- pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
- pw.print(" mCharging="); pw.print(mCharging);
- pw.print(" mLastAppIdleParoledTime=");
- TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
- pw.println();
+ mAppStandby.dumpState(args, pw);
}
}
@@ -1250,50 +521,6 @@
onUserRemoved(msg.arg1);
break;
- case MSG_INFORM_LISTENERS:
- informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
- break;
-
- case MSG_FORCE_IDLE_STATE:
- forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
- break;
-
- case MSG_CHECK_IDLE_STATES:
- if (checkIdleStates(msg.arg1)) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- MSG_CHECK_IDLE_STATES, msg.arg1, 0),
- mCheckIdleIntervalMillis);
- }
- break;
-
- case MSG_ONE_TIME_CHECK_IDLE_STATES:
- mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
- checkIdleStates(UserHandle.USER_ALL);
- break;
-
- case MSG_CHECK_PAROLE_TIMEOUT:
- checkParoleTimeout();
- break;
-
- case MSG_PAROLE_END_TIMEOUT:
- if (DEBUG) Slog.d(TAG, "Ending parole");
- setAppIdleParoled(false);
- break;
-
- case MSG_REPORT_CONTENT_PROVIDER_USAGE:
- SomeArgs args = (SomeArgs) msg.obj;
- reportContentProviderUsage((String) args.arg1, // authority name
- (String) args.arg2, // package name
- (int) args.arg3); // userId
- args.recycle();
- break;
-
- case MSG_PAROLE_STATE_CHANGED:
- if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
- + ", Charging state:" + mCharging);
- informParoleStateChanged();
- break;
-
default:
super.handleMessage(msg);
break;
@@ -1301,72 +528,6 @@
}
}
- /**
- * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}.
- */
- private class SettingsObserver extends ContentObserver {
- /**
- * This flag has been used to disable app idle on older builds with bug b/26355386.
- */
- @Deprecated
- private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
-
- private static final String KEY_IDLE_DURATION = "idle_duration2";
- private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
- private static final String KEY_PAROLE_INTERVAL = "parole_interval";
- private static final String KEY_PAROLE_DURATION = "parole_duration";
-
- private final KeyValueListParser mParser = new KeyValueListParser(',');
-
- SettingsObserver(Handler handler) {
- super(handler);
- }
-
- void registerObserver() {
- getContext().getContentResolver().registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.APP_IDLE_CONSTANTS), false, this);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- updateSettings();
- postOneTimeCheckIdleStates();
- }
-
- void updateSettings() {
- synchronized (mAppIdleLock) {
- // Look at global settings for this.
- // TODO: Maybe apply different thresholds for different users.
- try {
- mParser.setString(Settings.Global.getString(getContext().getContentResolver(),
- Settings.Global.APP_IDLE_CONSTANTS));
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
- // fallthrough, mParser is empty and all defaults will be returned.
- }
-
- // Default: 12 hours of screen-on time sans dream-time
- mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
- COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
-
- mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
- COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
-
- mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
- COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
-
- // Default: 24 hours between paroles
- mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
- COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
-
- mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
- COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
- mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
- mAppIdleScreenThresholdMillis);
- }
- }
- }
-
private final class BinderService extends IUsageStatsManager.Stub {
private boolean hasPermission(String callingPackage) {
@@ -1462,7 +623,8 @@
Binder.getCallingUid(), userId);
final long token = Binder.clearCallingIdentity();
try {
- return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId,
+ return mAppStandby.isAppIdleFilteredOrParoled(
+ packageName, userId,
SystemClock.elapsedRealtime(), obfuscateInstantApps);
} finally {
Binder.restoreCallingIdentity(token);
@@ -1483,9 +645,9 @@
"No permission to change app idle state");
final long token = Binder.clearCallingIdentity();
try {
- final int appId = getAppId(packageName);
+ final int appId = mAppStandby.getAppId(packageName);
if (appId < 0) return;
- UsageStatsService.this.setAppIdleAsync(packageName, idle, userId);
+ mAppStandby.setAppIdleAsync(packageName, idle, userId);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1509,7 +671,7 @@
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_CARRIER_SERVICES,
"onCarrierPrivilegedAppsChanged can only be called by privileged apps.");
- UsageStatsService.this.clearCarrierPrivilegedApps();
+ mAppStandby.clearCarrierPrivilegedApps();
}
@Override
@@ -1624,28 +786,23 @@
@Override
public void reportContentProviderUsage(String name, String packageName, int userId) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = name;
- args.arg2 = packageName;
- args.arg3 = userId;
- mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args)
- .sendToTarget();
+ mAppStandby.postReportContentProviderUsage(name, packageName, userId);
}
@Override
public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
- return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId,
- SystemClock.elapsedRealtime());
+ return mAppStandby.isAppIdleFiltered(packageName, uidForAppId,
+ userId, SystemClock.elapsedRealtime());
}
@Override
public int[] getIdleUidsForUser(int userId) {
- return UsageStatsService.this.getIdleUidsForUser(userId);
+ return mAppStandby.getIdleUidsForUser(userId);
}
@Override
public boolean isAppIdleParoleOn() {
- return isParoledOrCharging();
+ return mAppStandby.isParoledOrCharging();
}
@Override
@@ -1658,20 +815,20 @@
@Override
public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) {
- UsageStatsService.this.addListener(listener);
+ mAppStandby.addListener(listener);
listener.onParoleStateChanged(isAppIdleParoleOn());
}
@Override
public void removeAppIdleStateChangeListener(
AppIdleStateChangeListener listener) {
- UsageStatsService.this.removeListener(listener);
+ mAppStandby.removeListener(listener);
}
@Override
public byte[] getBackupPayload(int user, String key) {
// Check to ensure that only user 0's data is b/r for now
- synchronized (UsageStatsService.this.mLock) {
+ synchronized (mLock) {
if (user == UserHandle.USER_SYSTEM) {
final UserUsageStatsService userStats =
getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
@@ -1684,7 +841,7 @@
@Override
public void applyRestoredPayload(int user, String key, byte[] payload) {
- synchronized (UsageStatsService.this.mLock) {
+ synchronized (mLock) {
if (user == UserHandle.USER_SYSTEM) {
final UserUsageStatsService userStats =
getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 68c1d5f..acc27be 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -314,7 +314,11 @@
return null;
}
- mDevicesParser.scan();
+ if (!mDevicesParser.scan()) {
+ Slog.e(TAG, "Error parsing ALSA devices file.");
+ return null;
+ }
+
int device = mDevicesParser.getDefaultDeviceNum(card);
boolean hasPlayback = mDevicesParser.hasPlaybackDevices(card);
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index ebac041..764b7b2 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -522,8 +522,7 @@
* @param handler The {@link Handler} on which calls to {@code callback} should be enqueued on.
*/
public void registerStateCallback(@NonNull DownloadRequest request,
- @NonNull DownloadStateCallback callback,
- @NonNull Handler handler) {
+ @NonNull DownloadStateCallback callback, @NonNull Handler handler) {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound");
@@ -533,7 +532,8 @@
new InternalDownloadStateCallback(callback, handler);
try {
- int result = downloadService.registerStateCallback(request, internalCallback);
+ int result = downloadService.registerStateCallback(request, internalCallback,
+ callback.getCallbackFilterFlags());
if (result != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index cde0bdf..c0564c5 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -107,8 +107,6 @@
public static final String MODEM_ACTIVITY_RESULT_KEY =
BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY;
- private static ITelephonyRegistry sRegistry;
-
/**
* The allowed states of Wi-Fi calling.
*
@@ -179,11 +177,6 @@
mContext = context;
}
mSubscriptionManager = SubscriptionManager.from(mContext);
-
- if (sRegistry == null) {
- sRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
- "telephony.registry"));
- }
}
/** @hide */
@@ -3513,6 +3506,10 @@
return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
}
+ private ITelephonyRegistry getTelephonyRegistry() {
+ return ITelephonyRegistry.Stub.asInterface(ServiceManager.getService("telephony.registry"));
+ }
+
//
//
// PhoneStateListener
@@ -3552,12 +3549,16 @@
if (listener.mSubId == null) {
listener.mSubId = mSubId;
}
- sRegistry.listenForSubscriber(listener.mSubId, getOpPackageName(),
- listener.callback, events, notifyNow);
+
+ ITelephonyRegistry registry = getTelephonyRegistry();
+ if (registry != null) {
+ registry.listenForSubscriber(listener.mSubId, getOpPackageName(),
+ listener.callback, events, notifyNow);
+ } else {
+ Rlog.w(TAG, "telephony registry not ready.");
+ }
} catch (RemoteException ex) {
// system process dead
- } catch (NullPointerException ex) {
- // system process dead
}
}
diff --git a/telephony/java/android/telephony/mbms/DownloadStateCallback.java b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
index 86920bd..892fbf0 100644
--- a/telephony/java/android/telephony/mbms/DownloadStateCallback.java
+++ b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
@@ -16,8 +16,12 @@
package android.telephony.mbms;
+import android.annotation.IntDef;
import android.telephony.MbmsDownloadSession;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A optional listener class used by download clients to track progress. Apps should extend this
* class and pass an instance into
@@ -29,6 +33,71 @@
public class DownloadStateCallback {
/**
+ * Bitmask flags used for filtering out callback methods. Used when constructing the
+ * DownloadStateCallback as an optional parameter.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ALL_UPDATES, PROGRESS_UPDATES, STATE_UPDATES})
+ public @interface FilterFlag {}
+
+ /**
+ * Receive all callbacks.
+ * Default value.
+ */
+ public static final int ALL_UPDATES = 0x00;
+ /**
+ * Receive callbacks for {@link #onProgressUpdated}.
+ */
+ public static final int PROGRESS_UPDATES = 0x01;
+ /**
+ * Receive callbacks for {@link #onStateUpdated}.
+ */
+ public static final int STATE_UPDATES = 0x02;
+
+ private final int mCallbackFilterFlags;
+
+ /**
+ * Creates a DownloadStateCallback that will receive all callbacks.
+ */
+ public DownloadStateCallback() {
+ mCallbackFilterFlags = ALL_UPDATES;
+ }
+
+ /**
+ * Creates a DownloadStateCallback that will only receive callbacks for the methods specified
+ * via the filterFlags parameter.
+ * @param filterFlags A bitmask of filter flags that will specify which callback this instance
+ * is interested in.
+ */
+ public DownloadStateCallback(int filterFlags) {
+ mCallbackFilterFlags = filterFlags;
+ }
+
+ /**
+ * Return the currently set filter flags.
+ * @return An integer containing the bitmask of flags that this instance is interested in.
+ * @hide
+ */
+ public int getCallbackFilterFlags() {
+ return mCallbackFilterFlags;
+ }
+
+ /**
+ * Returns true if a filter flag is set for a particular callback method. If the flag is set,
+ * the callback will be delivered to the listening process.
+ * @param flag A filter flag specifying whether or not a callback method is registered to
+ * receive callbacks.
+ * @return true if registered to receive callbacks in the listening process, false if not.
+ */
+ public final boolean isFilterFlagSet(@FilterFlag int flag) {
+ if (mCallbackFilterFlags == ALL_UPDATES) {
+ return true;
+ }
+ return (mCallbackFilterFlags & flag) > 0;
+ }
+
+ /**
* Called when the middleware wants to report progress for a file in a {@link DownloadRequest}.
*
* @param request a {@link DownloadRequest}, indicating which download is being referenced.
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 61415b5..fe27537 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -165,6 +165,12 @@
Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
return false;
}
+ // We do not need to verify below extras if the result is not success.
+ if (MbmsDownloadSession.RESULT_SUCCESSFUL !=
+ intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
+ MbmsDownloadSession.RESULT_CANCELLED)) {
+ return true;
+ }
if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST)) {
Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
return false;
diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.java b/telephony/java/android/telephony/mbms/ServiceInfo.java
index 9a01ed0..8529f52 100644
--- a/telephony/java/android/telephony/mbms/ServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/ServiceInfo.java
@@ -23,6 +23,7 @@
import android.text.TextUtils;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -62,12 +63,6 @@
throw new RuntimeException("bad locales length " + newLocales.size());
}
- for (Locale l : newLocales) {
- if (!newNames.containsKey(l)) {
- throw new IllegalArgumentException("A name must be provided for each locale");
- }
- }
-
names = new HashMap(newNames.size());
names.putAll(newNames);
className = newClassName;
@@ -127,7 +122,7 @@
* Get the user-displayable name for this cell-broadcast service corresponding to the
* provided {@link Locale}.
* @param locale The {@link Locale} in which you want the name of the service. This must be a
- * value from the list returned by {@link #getLocales()} -- an
+ * value from the set returned by {@link #getNamedContentLocales()} -- an
* {@link java.util.NoSuchElementException} may be thrown otherwise.
* @return The {@link CharSequence} providing the name of the service in the given
* {@link Locale}
@@ -140,6 +135,17 @@
}
/**
+ * Return an unmodifiable set of the current {@link Locale}s that have a user-displayable name
+ * associated with them. The user-displayable name associated with any {@link Locale} in this
+ * set can be retrieved with {@link #getNameForLocale(Locale)}.
+ * @return An unmodifiable set of {@link Locale} objects corresponding to a user-displayable
+ * content name in that locale.
+ */
+ public @NonNull Set<Locale> getNamedContentLocales() {
+ return Collections.unmodifiableSet(names.keySet());
+ }
+
+ /**
* The class name for this service - used to categorize and filter
*/
public String getServiceClassName() {
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
index ed5e826..cb93542 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
@@ -36,7 +36,8 @@
int download(in DownloadRequest downloadRequest);
- int registerStateCallback(in DownloadRequest downloadRequest, IDownloadStateCallback listener);
+ int registerStateCallback(in DownloadRequest downloadRequest, IDownloadStateCallback listener,
+ int flags);
int unregisterStateCallback(in DownloadRequest downloadRequest,
IDownloadStateCallback listener);
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index d845a57..2f85a1d 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -46,6 +46,47 @@
private final Map<IBinder, DownloadStateCallback> mDownloadCallbackBinderMap = new HashMap<>();
private final Map<IBinder, DeathRecipient> mDownloadCallbackDeathRecipients = new HashMap<>();
+
+ // Filters the DownloadStateCallbacks by its configuration from the app.
+ private abstract static class FilteredDownloadStateCallback extends DownloadStateCallback {
+
+ private final IDownloadStateCallback mCallback;
+ public FilteredDownloadStateCallback(IDownloadStateCallback callback, int callbackFlags) {
+ super(callbackFlags);
+ mCallback = callback;
+ }
+
+ @Override
+ public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
+ int currentDownloadSize, int fullDownloadSize, int currentDecodedSize,
+ int fullDecodedSize) {
+ if (!isFilterFlagSet(PROGRESS_UPDATES)) {
+ return;
+ }
+ try {
+ mCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+ fullDownloadSize, currentDecodedSize, fullDecodedSize);
+ } catch (RemoteException e) {
+ onRemoteException(e);
+ }
+ }
+
+ @Override
+ public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
+ @MbmsDownloadSession.DownloadStatus int state) {
+ if (!isFilterFlagSet(STATE_UPDATES)) {
+ return;
+ }
+ try {
+ mCallback.onStateUpdated(request, fileInfo, state);
+ } catch (RemoteException e) {
+ onRemoteException(e);
+ }
+ }
+
+ protected abstract void onRemoteException(RemoteException e);
+ }
+
/**
* Initialize the download service for this app and subId, registering the listener.
*
@@ -196,9 +237,8 @@
* @hide
*/
@Override
- public final int registerStateCallback(
- final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
- throws RemoteException {
+ public final int registerStateCallback(final DownloadRequest downloadRequest,
+ final IDownloadStateCallback callback, int flags) throws RemoteException {
final int uid = Binder.getCallingUid();
DeathRecipient deathRecipient = new DeathRecipient() {
@Override
@@ -211,28 +251,10 @@
mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient);
callback.asBinder().linkToDeath(deathRecipient, 0);
- DownloadStateCallback exposedCallback = new DownloadStateCallback() {
+ DownloadStateCallback exposedCallback = new FilteredDownloadStateCallback(callback, flags) {
@Override
- public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo, int
- currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int
- fullDecodedSize) {
- try {
- callback.onProgressUpdated(request, fileInfo, currentDownloadSize,
- fullDownloadSize,
- currentDecodedSize, fullDecodedSize);
- } catch (RemoteException e) {
- onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
- }
- }
-
- @Override
- public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
- @MbmsDownloadSession.DownloadStatus int state) {
- try {
- callback.onStateUpdated(request, fileInfo, state);
- } catch (RemoteException e) {
- onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
- }
+ protected void onRemoteException(RemoteException e) {
+ onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
}
};
diff --git a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
index 8fb27b2..a43f122 100644
--- a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
+++ b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
@@ -38,8 +38,9 @@
/**
* The MBMS middleware should send this when a download of single file has completed or
- * failed. Mandatory extras are
+ * failed. The only mandatory extra is
* {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_RESULT}
+ * and the following are required when the download has completed:
* {@link MbmsDownloadSession#EXTRA_MBMS_FILE_INFO}
* {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_REQUEST}
* {@link #EXTRA_TEMP_LIST}
diff --git a/tests/Compatibility/Android.mk b/tests/Compatibility/Android.mk
index feeae02..82e2126 100644
--- a/tests/Compatibility/Android.mk
+++ b/tests/Compatibility/Android.mk
@@ -17,9 +17,7 @@
# We only want this apk build for tests.
LOCAL_MODULE_TAGS := tests
-
-LOCAL_JAVA_LIBRARIES := legacy-android-test
-LOCAL_STATIC_JAVA_LIBRARIES := junit
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
# Include all test java files.
LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
diff --git a/tests/Compatibility/AndroidManifest.xml b/tests/Compatibility/AndroidManifest.xml
index 7017431..5d1317e 100644
--- a/tests/Compatibility/AndroidManifest.xml
+++ b/tests/Compatibility/AndroidManifest.xml
@@ -15,12 +15,12 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.compatibilitytest" >
+ package="com.android.compatibilitytest"
+ android:sharedUserId="android.uid.system">
<uses-sdk android:minSdkVersion="21"
android:targetSdkVersion="21" />
- <application >
- <uses-library android:name="android.test.runner" />
- </application>
+ <application />
+ <uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.REAL_GET_TASKS" />
<instrumentation
android:name=".AppCompatibilityRunner"
diff --git a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
index f81b001..a5261d0 100644
--- a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
+++ b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
@@ -17,62 +17,91 @@
package com.android.compatibilitytest;
import android.app.ActivityManager;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.IActivityController;
+import android.app.IActivityManager;
+import android.app.Instrumentation;
import android.app.UiAutomation;
import android.app.UiModeManager;
-import android.app.ActivityManager.ProcessErrorStateInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.os.Bundle;
-import android.test.InstrumentationTestCase;
+import android.os.DropBoxManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
-import junit.framework.Assert;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* Application Compatibility Test that launches an application and detects
* crashes.
*/
-public class AppCompatibility extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AppCompatibility {
private static final String TAG = AppCompatibility.class.getSimpleName();
private static final String PACKAGE_TO_LAUNCH = "package_to_launch";
private static final String APP_LAUNCH_TIMEOUT_MSECS = "app_launch_timeout_ms";
private static final String WORKSPACE_LAUNCH_TIMEOUT_MSECS = "workspace_launch_timeout_ms";
+ private static final Set<String> DROPBOX_TAGS = new HashSet<>();
+ static {
+ DROPBOX_TAGS.add("SYSTEM_TOMBSTONE");
+ DROPBOX_TAGS.add("system_app_anr");
+ DROPBOX_TAGS.add("system_app_native_crash");
+ DROPBOX_TAGS.add("system_app_crash");
+ DROPBOX_TAGS.add("data_app_anr");
+ DROPBOX_TAGS.add("data_app_native_crash");
+ DROPBOX_TAGS.add("data_app_crash");
+ }
+ // time waiting for app to launch
private int mAppLaunchTimeout = 7000;
+ // time waiting for launcher home screen to show up
private int mWorkspaceLaunchTimeout = 2000;
private Context mContext;
private ActivityManager mActivityManager;
private PackageManager mPackageManager;
- private AppCompatibilityRunner mRunner;
private Bundle mArgs;
+ private Instrumentation mInstrumentation;
+ private String mLauncherPackageName;
+ private IActivityController mCrashSupressor = new CrashSuppressor();
+ private Map<String, List<String>> mAppErrors = new HashMap<>();
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
- mRunner = (AppCompatibilityRunner) getInstrumentation();
- assertNotNull("Could not fetch InstrumentationTestRunner.", mRunner);
-
- mContext = mRunner.getTargetContext();
- Assert.assertNotNull("Could not get the Context", mContext);
-
- mActivityManager = (ActivityManager)
- mContext.getSystemService(Context.ACTIVITY_SERVICE);
- Assert.assertNotNull("Could not get Activity Manager", mActivityManager);
-
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = InstrumentationRegistry.getTargetContext();
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mPackageManager = mContext.getPackageManager();
- Assert.assertNotNull("Missing Package Manager", mPackageManager);
+ mArgs = InstrumentationRegistry.getArguments();
- mArgs = mRunner.getBundle();
+ // resolve launcher package name
+ Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME);
+ ResolveInfo resolveInfo = mPackageManager.resolveActivity(
+ intent, PackageManager.MATCH_DEFAULT_ONLY);
+ mLauncherPackageName = resolveInfo.activityInfo.packageName;
+ Assert.assertNotNull("failed to resolve package name for launcher", mLauncherPackageName);
+ Log.v(TAG, "Using launcher package name: " + mLauncherPackageName);
// Parse optional inputs.
String appLaunchTimeoutMsecs = mArgs.getString(APP_LAUNCH_TIMEOUT_MSECS);
@@ -83,13 +112,20 @@
if (workspaceLaunchTimeoutMsecs != null) {
mWorkspaceLaunchTimeout = Integer.parseInt(workspaceLaunchTimeoutMsecs);
}
- getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_0);
+ mInstrumentation.getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_0);
+
+ // set activity controller to suppress crash dialogs and collects them by process name
+ mAppErrors.clear();
+ IActivityManager.Stub.asInterface(ServiceManager.checkService(Context.ACTIVITY_SERVICE))
+ .setActivityController(mCrashSupressor, false);
}
- @Override
- protected void tearDown() throws Exception {
- getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_UNFREEZE);
- super.tearDown();
+ @After
+ public void tearDown() throws Exception {
+ // unset activity controller
+ IActivityManager.Stub.asInterface(ServiceManager.checkService(Context.ACTIVITY_SERVICE))
+ .setActivityController(null, false);
+ mInstrumentation.getUiAutomation().setRotation(UiAutomation.ROTATION_UNFREEZE);
}
/**
@@ -98,6 +134,7 @@
*
* @throws Exception
*/
+ @Test
public void testAppStability() throws Exception {
String packageName = mArgs.getString(PACKAGE_TO_LAUNCH);
if (packageName != null) {
@@ -107,13 +144,23 @@
Log.w(TAG, String.format("Skipping %s; no launch intent", packageName));
return;
}
- ProcessErrorStateInfo err = launchActivity(packageName, intent);
- // Make sure there are no errors when launching the application,
- // otherwise raise an
- // exception with the first error encountered.
- assertNull(getStackTrace(err), err);
+ long startTime = System.currentTimeMillis();
+ launchActivity(packageName, intent);
try {
- assertTrue("App crashed after launch.", processStillUp(packageName));
+ checkDropbox(startTime, packageName);
+ if (mAppErrors.containsKey(packageName)) {
+ StringBuilder message = new StringBuilder("Error detected for package: ")
+ .append(packageName);
+ for (String err : mAppErrors.get(packageName)) {
+ message.append("\n\n");
+ message.append(err);
+ }
+ Assert.fail(message.toString());
+ }
+ // last check: see if app process is still running
+ Assert.assertTrue("app package \"" + packageName + "\" no longer found in running "
+ + "tasks, but no explicit crashes were detected; check logcat for details",
+ processStillUp(packageName));
} finally {
returnHome();
}
@@ -124,31 +171,30 @@
}
/**
- * Gets the stack trace for the error.
- *
- * @param in {@link ProcessErrorStateInfo} to parse.
- * @return {@link String} the long message of the error.
+ * Check dropbox for entries of interest regarding the specified process
+ * @param startTime if not 0, only check entries with timestamp later than the start time
+ * @param processName the process name to check for
*/
- private String getStackTrace(ProcessErrorStateInfo in) {
- if (in == null) {
- return null;
- } else {
- return in.stackTrace;
- }
- }
-
- /**
- * Returns the process name that the package is going to use.
- *
- * @param packageName name of the package
- * @return process name of the package
- */
- private String getProcessName(String packageName) {
- try {
- PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
- return pi.applicationInfo.processName;
- } catch (NameNotFoundException e) {
- return packageName;
+ private void checkDropbox(long startTime, String processName) {
+ DropBoxManager dropbox = (DropBoxManager) mContext
+ .getSystemService(Context.DROPBOX_SERVICE);
+ DropBoxManager.Entry entry = null;
+ while (null != (entry = dropbox.getNextEntry(null, startTime))) {
+ try {
+ // only check entries with tag that's of interest
+ String tag = entry.getTag();
+ if (DROPBOX_TAGS.contains(tag)) {
+ String content = entry.getText(4096);
+ if (content != null) {
+ if (content.contains(processName)) {
+ addProcessError(processName, "dropbox:" + tag, content);
+ }
+ }
+ }
+ startTime = entry.getTimeMillis();
+ } finally {
+ entry.close();
+ }
}
}
@@ -166,8 +212,7 @@
}
private Intent getLaunchIntentForPackage(String packageName) {
- UiModeManager umm = (UiModeManager)
- getInstrumentation().getContext().getSystemService(Context.UI_MODE_SERVICE);
+ UiModeManager umm = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
boolean isLeanback = umm.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION;
Intent intent = null;
if (isLeanback) {
@@ -186,35 +231,32 @@
* @return {@link Collection} of {@link ProcessErrorStateInfo} detected
* during the app launch.
*/
- private ProcessErrorStateInfo launchActivity(String packageName, Intent intent) {
+ private void launchActivity(String packageName, Intent intent) {
Log.d(TAG, String.format("launching package \"%s\" with intent: %s",
packageName, intent.toString()));
- String processName = getProcessName(packageName);
-
// Launch Activity
mContext.startActivity(intent);
try {
+ // artificial delay: in case app crashes after doing some work during launch
Thread.sleep(mAppLaunchTimeout);
} catch (InterruptedException e) {
// ignore
}
+ }
- // See if there are any errors. We wait until down here to give ANRs as much time as
- // possible to occur.
- final Collection<ProcessErrorStateInfo> postErr =
- mActivityManager.getProcessesInErrorState();
-
- if (postErr == null) {
- return null;
+ private void addProcessError(String processName, String errorType, String errorInfo) {
+ // parse out the package name if necessary, for apps with multiple proceses
+ String pkgName = processName.split(":", 2)[0];
+ List<String> errors;
+ if (mAppErrors.containsKey(pkgName)) {
+ errors = mAppErrors.get(pkgName);
+ } else {
+ errors = new ArrayList<>();
}
- for (ProcessErrorStateInfo error : postErr) {
- if (error.processName.equals(processName)) {
- return error;
- }
- }
- return null;
+ errors.add(String.format("type: %s details:\n%s", errorType, errorInfo));
+ mAppErrors.put(pkgName, errors);
}
/**
@@ -233,4 +275,55 @@
}
return false;
}
+
+ /**
+ * An {@link IActivityController} that instructs framework to kill processes hitting crashes
+ * directly without showing crash dialogs
+ *
+ */
+ private class CrashSuppressor extends IActivityController.Stub {
+
+ @Override
+ public boolean activityStarting(Intent intent, String pkg) throws RemoteException {
+ Log.d(TAG, "activity starting: " + intent.getComponent().toShortString());
+ return true;
+ }
+
+ @Override
+ public boolean activityResuming(String pkg) throws RemoteException {
+ Log.d(TAG, "activity resuming: " + pkg);
+ return true;
+ }
+
+ @Override
+ public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
+ long timeMillis, String stackTrace) throws RemoteException {
+ Log.d(TAG, "app crash: " + processName);
+ addProcessError(processName, "crash", stackTrace);
+ // don't show dialog
+ return false;
+ }
+
+ @Override
+ public int appEarlyNotResponding(String processName, int pid, String annotation)
+ throws RemoteException {
+ // ignore
+ return 0;
+ }
+
+ @Override
+ public int appNotResponding(String processName, int pid, String processStats)
+ throws RemoteException {
+ Log.d(TAG, "app ANR: " + processName);
+ addProcessError(processName, "ANR", processStats);
+ // don't show dialog
+ return -1;
+ }
+
+ @Override
+ public int systemNotResponding(String msg) throws RemoteException {
+ // ignore
+ return -1;
+ }
+ }
}
diff --git a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibilityRunner.java b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibilityRunner.java
index 258937f..b61ec34 100644
--- a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibilityRunner.java
+++ b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibilityRunner.java
@@ -16,20 +16,7 @@
package com.android.compatibilitytest;
-import android.os.Bundle;
-import android.test.InstrumentationTestRunner;
+import android.support.test.runner.AndroidJUnitRunner;
-public class AppCompatibilityRunner extends InstrumentationTestRunner {
-
- private Bundle mArgs;
-
- @Override
- public void onCreate(Bundle args) {
- super.onCreate(args);
- mArgs = args;
- }
-
- public Bundle getBundle() {
- return mArgs;
- }
-}
+// empty subclass to maintain backwards compatibility on host-side harness
+public class AppCompatibilityRunner extends AndroidJUnitRunner {}
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
new file mode 100644
index 0000000..1b4bef5
--- /dev/null
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link IpSecConfig}. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class IpSecConfigTest {
+
+ @Test
+ public void testDefaults() throws Exception {
+ IpSecConfig c = new IpSecConfig();
+ assertEquals(IpSecTransform.MODE_TRANSPORT, c.getMode());
+ assertEquals("", c.getLocalAddress());
+ assertEquals("", c.getRemoteAddress());
+ assertNull(c.getNetwork());
+ assertEquals(IpSecTransform.ENCAP_NONE, c.getEncapType());
+ assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getEncapSocketResourceId());
+ assertEquals(0, c.getEncapRemotePort());
+ assertEquals(0, c.getNattKeepaliveInterval());
+ for (int direction :
+ new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN}) {
+ assertNull(c.getEncryption(direction));
+ assertNull(c.getAuthentication(direction));
+ assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId(direction));
+ }
+ }
+
+ @Test
+ public void testParcelUnparcel() throws Exception {
+ assertParcelingIsLossless(new IpSecConfig());
+
+ IpSecConfig c = new IpSecConfig();
+ c.setMode(IpSecTransform.MODE_TUNNEL);
+ c.setLocalAddress("0.0.0.0");
+ c.setRemoteAddress("1.2.3.4");
+ c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP);
+ c.setEncapSocketResourceId(7);
+ c.setEncapRemotePort(22);
+ c.setNattKeepaliveInterval(42);
+ c.setEncryption(
+ IpSecTransform.DIRECTION_OUT,
+ new IpSecAlgorithm(
+ IpSecAlgorithm.CRYPT_AES_CBC,
+ new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
+ c.setAuthentication(
+ IpSecTransform.DIRECTION_OUT,
+ new IpSecAlgorithm(
+ IpSecAlgorithm.AUTH_HMAC_SHA1,
+ new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}));
+ c.setSpiResourceId(IpSecTransform.DIRECTION_OUT, 1984);
+ c.setEncryption(
+ IpSecTransform.DIRECTION_IN,
+ new IpSecAlgorithm(
+ IpSecAlgorithm.CRYPT_AES_CBC,
+ new byte[] {2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
+ c.setAuthentication(
+ IpSecTransform.DIRECTION_IN,
+ new IpSecAlgorithm(
+ IpSecAlgorithm.AUTH_HMAC_SHA1,
+ new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 1}));
+ c.setSpiResourceId(IpSecTransform.DIRECTION_IN, 99);
+ assertParcelingIsLossless(c);
+ }
+
+ private void assertParcelingIsLossless(IpSecConfig ci) throws Exception {
+ Parcel p = Parcel.obtain();
+ ci.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ IpSecConfig co = IpSecConfig.CREATOR.createFromParcel(p);
+ assertTrue(IpSecConfig.equals(co, ci));
+ }
+}
diff --git a/tests/net/java/android/net/util/BlockingSocketReaderTest.java b/tests/net/java/android/net/util/BlockingSocketReaderTest.java
index e03350f..1aad453 100644
--- a/tests/net/java/android/net/util/BlockingSocketReaderTest.java
+++ b/tests/net/java/android/net/util/BlockingSocketReaderTest.java
@@ -16,8 +16,11 @@
package android.net.util;
+import static android.net.util.BlockingSocketReader.DEFAULT_RECV_BUF_SIZE;
import static android.system.OsConstants.*;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructTimeval;
@@ -27,6 +30,7 @@
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet6Address;
@@ -53,61 +57,83 @@
protected FileDescriptor mLocalSocket;
protected InetSocketAddress mLocalSockName;
protected byte[] mLastRecvBuf;
- protected boolean mExited;
+ protected boolean mStopped;
+ protected HandlerThread mHandlerThread;
protected BlockingSocketReader mReceiver;
+ class UdpLoopbackReader extends BlockingSocketReader {
+ public UdpLoopbackReader(Handler h) {
+ super(h);
+ }
+
+ @Override
+ protected FileDescriptor createFd() {
+ FileDescriptor s = null;
+ try {
+ s = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ Os.bind(s, LOOPBACK6, 0);
+ mLocalSockName = (InetSocketAddress) Os.getsockname(s);
+ Os.setsockoptTimeval(s, SOL_SOCKET, SO_SNDTIMEO, TIMEO);
+ } catch (ErrnoException|SocketException e) {
+ closeFd(s);
+ fail();
+ return null;
+ }
+
+ mLocalSocket = s;
+ return s;
+ }
+
+ @Override
+ protected void handlePacket(byte[] recvbuf, int length) {
+ mLastRecvBuf = Arrays.copyOf(recvbuf, length);
+ mLatch.countDown();
+ }
+
+ @Override
+ protected void onStart() {
+ mStopped = false;
+ mLatch.countDown();
+ }
+
+ @Override
+ protected void onStop() {
+ mStopped = true;
+ mLatch.countDown();
+ }
+ };
+
@Override
public void setUp() {
resetLatch();
mLocalSocket = null;
mLocalSockName = null;
mLastRecvBuf = null;
- mExited = false;
+ mStopped = false;
- mReceiver = new BlockingSocketReader() {
- @Override
- protected FileDescriptor createSocket() {
- FileDescriptor s = null;
- try {
- s = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
- Os.bind(s, LOOPBACK6, 0);
- mLocalSockName = (InetSocketAddress) Os.getsockname(s);
- Os.setsockoptTimeval(s, SOL_SOCKET, SO_SNDTIMEO, TIMEO);
- } catch (ErrnoException|SocketException e) {
- closeSocket(s);
- fail();
- return null;
- }
-
- mLocalSocket = s;
- return s;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- mLastRecvBuf = Arrays.copyOf(recvbuf, length);
- mLatch.countDown();
- }
-
- @Override
- protected void onExit() {
- mExited = true;
- mLatch.countDown();
- }
- };
+ mHandlerThread = new HandlerThread(BlockingSocketReaderTest.class.getSimpleName());
+ mHandlerThread.start();
}
@Override
- public void tearDown() {
- if (mReceiver != null) mReceiver.stop();
+ public void tearDown() throws Exception {
+ if (mReceiver != null) {
+ mHandlerThread.getThreadHandler().post(() -> { mReceiver.stop(); });
+ waitForActivity();
+ }
mReceiver = null;
+ mHandlerThread.quit();
+ mHandlerThread = null;
}
void resetLatch() { mLatch = new CountDownLatch(1); }
void waitForActivity() throws Exception {
- assertTrue(mLatch.await(500, TimeUnit.MILLISECONDS));
- resetLatch();
+ try {
+ mLatch.await(1000, TimeUnit.MILLISECONDS);
+ } finally {
+ resetLatch();
+ }
}
void sendPacket(byte[] contents) throws Exception {
@@ -118,31 +144,54 @@
}
public void testBasicWorking() throws Exception {
- assertTrue(mReceiver.start());
+ final Handler h = mHandlerThread.getThreadHandler();
+ mReceiver = new UdpLoopbackReader(h);
+
+ h.post(() -> { mReceiver.start(); });
+ waitForActivity();
assertTrue(mLocalSockName != null);
assertEquals(LOOPBACK6, mLocalSockName.getAddress());
assertTrue(0 < mLocalSockName.getPort());
assertTrue(mLocalSocket != null);
- assertFalse(mExited);
+ assertFalse(mStopped);
final byte[] one = "one 1".getBytes("UTF-8");
sendPacket(one);
waitForActivity();
assertEquals(1, mReceiver.numPacketsReceived());
assertTrue(Arrays.equals(one, mLastRecvBuf));
- assertFalse(mExited);
+ assertFalse(mStopped);
final byte[] two = "two 2".getBytes("UTF-8");
sendPacket(two);
waitForActivity();
assertEquals(2, mReceiver.numPacketsReceived());
assertTrue(Arrays.equals(two, mLastRecvBuf));
- assertFalse(mExited);
+ assertFalse(mStopped);
mReceiver.stop();
waitForActivity();
assertEquals(2, mReceiver.numPacketsReceived());
assertTrue(Arrays.equals(two, mLastRecvBuf));
- assertTrue(mExited);
+ assertTrue(mStopped);
+ mReceiver = null;
+ }
+
+ class NullBlockingSocketReader extends BlockingSocketReader {
+ public NullBlockingSocketReader(Handler h, int recvbufsize) {
+ super(h, recvbufsize);
+ }
+
+ @Override
+ public FileDescriptor createFd() { return null; }
+ }
+
+ public void testMinimalRecvBufSize() throws Exception {
+ final Handler h = mHandlerThread.getThreadHandler();
+
+ for (int i : new int[]{-1, 0, 1, DEFAULT_RECV_BUF_SIZE-1}) {
+ final BlockingSocketReader b = new NullBlockingSocketReader(h, i);
+ assertEquals(DEFAULT_RECV_BUF_SIZE, b.recvBufSize());
+ }
}
}
diff --git a/tests/net/java/com/android/internal/util/BitUtilsTest.java b/tests/net/java/com/android/internal/util/BitUtilsTest.java
index 0ad8a21..f4dc12a 100644
--- a/tests/net/java/com/android/internal/util/BitUtilsTest.java
+++ b/tests/net/java/com/android/internal/util/BitUtilsTest.java
@@ -56,6 +56,25 @@
}
@Test
+ public void testUnsignedShortComposition() {
+ byte b0 = 0;
+ byte b1 = 1;
+ byte b2 = 2;
+ byte b10 = 10;
+ byte b16 = 16;
+ byte b128 = -128;
+ byte b224 = -32;
+ byte b255 = -1;
+ assertEquals(0x0000, uint16(b0, b0));
+ assertEquals(0xffff, uint16(b255, b255));
+ assertEquals(0x0a01, uint16(b10, b1));
+ assertEquals(0x8002, uint16(b128, b2));
+ assertEquals(0x01ff, uint16(b1, b255));
+ assertEquals(0x80ff, uint16(b128, b255));
+ assertEquals(0xe010, uint16(b224, b16));
+ }
+
+ @Test
public void testUnsignedIntWideningConversions() {
assertEquals(0, uint32(0));
assertEquals(1, uint32(1));
diff --git a/tests/net/java/com/android/internal/util/RingBufferTest.java b/tests/net/java/com/android/internal/util/RingBufferTest.java
new file mode 100644
index 0000000..7a2344317
--- /dev/null
+++ b/tests/net/java/com/android/internal/util/RingBufferTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2017 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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import java.util.Arrays;
+import java.util.Objects;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RingBufferTest {
+
+ @Test
+ public void testEmptyRingBuffer() {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, 100);
+
+ assertArraysEqual(new String[0], buffer.toArray());
+ }
+
+ @Test
+ public void testIncorrectConstructorArguments() {
+ try {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, -10);
+ fail("Should not be able to create a negative capacity RingBuffer");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, 0);
+ fail("Should not be able to create a 0 capacity RingBuffer");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testRingBufferWithNoWrapping() {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, 100);
+
+ buffer.append("a");
+ buffer.append("b");
+ buffer.append("c");
+ buffer.append("d");
+ buffer.append("e");
+
+ String[] expected = {"a", "b", "c", "d", "e"};
+ assertArraysEqual(expected, buffer.toArray());
+ }
+
+ @Test
+ public void testRingBufferWithCapacity1() {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, 1);
+
+ buffer.append("a");
+ assertArraysEqual(new String[]{"a"}, buffer.toArray());
+
+ buffer.append("b");
+ assertArraysEqual(new String[]{"b"}, buffer.toArray());
+
+ buffer.append("c");
+ assertArraysEqual(new String[]{"c"}, buffer.toArray());
+
+ buffer.append("d");
+ assertArraysEqual(new String[]{"d"}, buffer.toArray());
+
+ buffer.append("e");
+ assertArraysEqual(new String[]{"e"}, buffer.toArray());
+ }
+
+ @Test
+ public void testRingBufferWithWrapping() {
+ int capacity = 100;
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, capacity);
+
+ buffer.append("a");
+ buffer.append("b");
+ buffer.append("c");
+ buffer.append("d");
+ buffer.append("e");
+
+ String[] expected1 = {"a", "b", "c", "d", "e"};
+ assertArraysEqual(expected1, buffer.toArray());
+
+ String[] expected2 = new String[capacity];
+ int firstIndex = 0;
+ int lastIndex = capacity - 1;
+
+ expected2[firstIndex] = "e";
+ for (int i = 1; i < capacity; i++) {
+ buffer.append("x");
+ expected2[i] = "x";
+ }
+ assertArraysEqual(expected2, buffer.toArray());
+
+ buffer.append("x");
+ expected2[firstIndex] = "x";
+ assertArraysEqual(expected2, buffer.toArray());
+
+ for (int i = 0; i < 10; i++) {
+ for (String s : expected2) {
+ buffer.append(s);
+ }
+ }
+ assertArraysEqual(expected2, buffer.toArray());
+
+ buffer.append("a");
+ expected2[lastIndex] = "a";
+ assertArraysEqual(expected2, buffer.toArray());
+ }
+
+ static <T> void assertArraysEqual(T[] expected, T[] got) {
+ if (expected.length != got.length) {
+ fail(Arrays.toString(expected) + " and " + Arrays.toString(got)
+ + " did not have the same length");
+ }
+
+ for (int i = 0; i < expected.length; i++) {
+ if (!Objects.equals(expected[i], got[i])) {
+ fail(Arrays.toString(expected) + " and " + Arrays.toString(got)
+ + " were not equal");
+ }
+ }
+ }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index a814738..335e6240 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -19,6 +19,8 @@
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.getNetworkTypeName;
@@ -119,7 +121,6 @@
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.BooleanSupplier;
import java.util.function.Predicate;
/**
@@ -782,6 +783,13 @@
return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
}
+ @Override
+ public boolean hasService(String name) {
+ // Currenty, the only relevant service that ConnectivityService checks for is
+ // ETHERNET_SERVICE.
+ return Context.ETHERNET_SERVICE.equals(name);
+ }
+
public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
return mLastCreatedNetworkMonitor;
}
@@ -929,6 +937,13 @@
// will fail. Failing here is much easier to debug.
assertTrue(mCm.isNetworkSupported(TYPE_WIFI));
assertTrue(mCm.isNetworkSupported(TYPE_MOBILE));
+ assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_MMS));
+ assertFalse(mCm.isNetworkSupported(TYPE_MOBILE_FOTA));
+
+ // Check that TYPE_ETHERNET is supported. Unlike the asserts above, which only validate our
+ // mocks, this assert exercises the ConnectivityService code path that ensures that
+ // TYPE_ETHERNET is supported if the ethernet service is running.
+ assertTrue(mCm.isNetworkSupported(TYPE_ETHERNET));
}
@SmallTest
@@ -3196,68 +3211,68 @@
}
@SmallTest
- public void testNetworkRequestMaximum() {
+ public void testNetworkCallbackMaximum() {
final int MAX_REQUESTS = 100;
- // Test that the limit is enforced when MAX_REQUESTS simultaneous requests are added.
+ final int CALLBACKS = 90;
+ final int INTENTS = 10;
+ assertEquals(MAX_REQUESTS, CALLBACKS + INTENTS);
+
NetworkRequest networkRequest = new NetworkRequest.Builder().build();
- ArrayList<NetworkCallback> networkCallbacks = new ArrayList<NetworkCallback>();
- try {
- for (int i = 0; i < MAX_REQUESTS; i++) {
- NetworkCallback networkCallback = new NetworkCallback();
- mCm.requestNetwork(networkRequest, networkCallback);
- networkCallbacks.add(networkCallback);
- }
- fail("Registering " + MAX_REQUESTS + " NetworkRequests did not throw exception");
- } catch (TooManyRequestsException expected) {}
- for (NetworkCallback networkCallback : networkCallbacks) {
- mCm.unregisterNetworkCallback(networkCallback);
- }
- networkCallbacks.clear();
+ ArrayList<Object> registered = new ArrayList<>();
- try {
- for (int i = 0; i < MAX_REQUESTS; i++) {
- NetworkCallback networkCallback = new NetworkCallback();
- mCm.registerNetworkCallback(networkRequest, networkCallback);
- networkCallbacks.add(networkCallback);
- }
- fail("Registering " + MAX_REQUESTS + " NetworkCallbacks did not throw exception");
- } catch (TooManyRequestsException expected) {}
- for (NetworkCallback networkCallback : networkCallbacks) {
- mCm.unregisterNetworkCallback(networkCallback);
+ int j = 0;
+ while (j++ < CALLBACKS / 2) {
+ NetworkCallback cb = new NetworkCallback();
+ mCm.requestNetwork(networkRequest, cb);
+ registered.add(cb);
}
- networkCallbacks.clear();
+ while (j++ < CALLBACKS) {
+ NetworkCallback cb = new NetworkCallback();
+ mCm.registerNetworkCallback(networkRequest, cb);
+ registered.add(cb);
+ }
+ j = 0;
+ while (j++ < INTENTS / 2) {
+ PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, new Intent("a" + j), 0);
+ mCm.requestNetwork(networkRequest, pi);
+ registered.add(pi);
+ }
+ while (j++ < INTENTS) {
+ PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, new Intent("b" + j), 0);
+ mCm.registerNetworkCallback(networkRequest, pi);
+ registered.add(pi);
+ }
- ArrayList<PendingIntent> pendingIntents = new ArrayList<PendingIntent>();
+ // Test that the limit is enforced when MAX_REQUESTS simultaneous requests are added.
try {
- for (int i = 0; i < MAX_REQUESTS + 1; i++) {
- PendingIntent pendingIntent =
- PendingIntent.getBroadcast(mContext, 0, new Intent("a" + i), 0);
- mCm.requestNetwork(networkRequest, pendingIntent);
- pendingIntents.add(pendingIntent);
- }
- fail("Registering " + MAX_REQUESTS +
- " PendingIntent NetworkRequests did not throw exception");
+ mCm.requestNetwork(networkRequest, new NetworkCallback());
+ fail("Registering " + MAX_REQUESTS + " network requests did not throw exception");
} catch (TooManyRequestsException expected) {}
- for (PendingIntent pendingIntent : pendingIntents) {
- mCm.unregisterNetworkCallback(pendingIntent);
- }
- pendingIntents.clear();
+ try {
+ mCm.registerNetworkCallback(networkRequest, new NetworkCallback());
+ fail("Registering " + MAX_REQUESTS + " network callbacks did not throw exception");
+ } catch (TooManyRequestsException expected) {}
+ try {
+ mCm.requestNetwork(networkRequest,
+ PendingIntent.getBroadcast(mContext, 0, new Intent("c"), 0));
+ fail("Registering " + MAX_REQUESTS + " PendingIntent requests did not throw exception");
+ } catch (TooManyRequestsException expected) {}
+ try {
+ mCm.registerNetworkCallback(networkRequest,
+ PendingIntent.getBroadcast(mContext, 0, new Intent("d"), 0));
+ fail("Registering " + MAX_REQUESTS
+ + " PendingIntent callbacks did not throw exception");
+ } catch (TooManyRequestsException expected) {}
- try {
- for (int i = 0; i < MAX_REQUESTS + 1; i++) {
- PendingIntent pendingIntent =
- PendingIntent.getBroadcast(mContext, 0, new Intent("a" + i), 0);
- mCm.registerNetworkCallback(networkRequest, pendingIntent);
- pendingIntents.add(pendingIntent);
+ for (Object o : registered) {
+ if (o instanceof NetworkCallback) {
+ mCm.unregisterNetworkCallback((NetworkCallback)o);
}
- fail("Registering " + MAX_REQUESTS +
- " PendingIntent NetworkCallbacks did not throw exception");
- } catch (TooManyRequestsException expected) {}
- for (PendingIntent pendingIntent : pendingIntents) {
- mCm.unregisterNetworkCallback(pendingIntent);
+ if (o instanceof PendingIntent) {
+ mCm.unregisterNetworkCallback((PendingIntent)o);
+ }
}
- pendingIntents.clear();
- waitForIdle(5000);
+ waitForIdle();
// Test that the limit is not hit when MAX_REQUESTS requests are added and removed.
for (int i = 0; i < MAX_REQUESTS; i++) {
@@ -3265,23 +3280,23 @@
mCm.requestNetwork(networkRequest, networkCallback);
mCm.unregisterNetworkCallback(networkCallback);
}
- waitForIdle();
+
for (int i = 0; i < MAX_REQUESTS; i++) {
NetworkCallback networkCallback = new NetworkCallback();
mCm.registerNetworkCallback(networkRequest, networkCallback);
mCm.unregisterNetworkCallback(networkCallback);
}
- waitForIdle();
+
for (int i = 0; i < MAX_REQUESTS; i++) {
PendingIntent pendingIntent =
- PendingIntent.getBroadcast(mContext, 0, new Intent("b" + i), 0);
+ PendingIntent.getBroadcast(mContext, 0, new Intent("e" + i), 0);
mCm.requestNetwork(networkRequest, pendingIntent);
mCm.unregisterNetworkCallback(pendingIntent);
}
- waitForIdle();
+
for (int i = 0; i < MAX_REQUESTS; i++) {
PendingIntent pendingIntent =
- PendingIntent.getBroadcast(mContext, 0, new Intent("c" + i), 0);
+ PendingIntent.getBroadcast(mContext, 0, new Intent("f" + i), 0);
mCm.registerNetworkCallback(networkRequest, pendingIntent);
mCm.unregisterNetworkCallback(pendingIntent);
}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
new file mode 100644
index 0000000..9057a10
--- /dev/null
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.INetd;
+import android.net.IpSecAlgorithm;
+import android.net.IpSecConfig;
+import android.net.IpSecManager;
+import android.net.IpSecSpiResponse;
+import android.net.IpSecTransform;
+import android.net.IpSecTransformResponse;
+import android.net.NetworkUtils;
+import android.os.Binder;
+import android.os.ParcelFileDescriptor;
+import android.support.test.filters.SmallTest;
+import android.system.OsConstants;
+
+import java.net.Socket;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/** Unit tests for {@link IpSecService}. */
+@SmallTest
+@RunWith(Parameterized.class)
+public class IpSecServiceParameterizedTest {
+
+ private static final int DROID_SPI = 0xD1201D;
+ private static final int DROID_SPI2 = DROID_SPI + 1;
+
+ private final String mRemoteAddr;
+
+ @Parameterized.Parameters
+ public static Collection ipSecConfigs() {
+ return Arrays.asList(new Object[][] {{"8.8.4.4"}, {"2601::10"}});
+ }
+
+ private static final byte[] CRYPT_KEY = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+ };
+ private static final byte[] AUTH_KEY = {
+ 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
+ 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
+ };
+
+ Context mMockContext;
+ INetd mMockNetd;
+ IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
+ IpSecService mIpSecService;
+
+ public IpSecServiceParameterizedTest(String remoteAddr) {
+ mRemoteAddr = remoteAddr;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mMockContext = mock(Context.class);
+ mMockNetd = mock(INetd.class);
+ mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
+ mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
+
+ // Injecting mock netd
+ when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
+ }
+
+ @Test
+ public void testIpSecServiceReserveSpi() throws Exception {
+ when(mMockNetd.ipSecAllocateSpi(
+ anyInt(),
+ eq(IpSecTransform.DIRECTION_OUT),
+ anyString(),
+ eq(mRemoteAddr),
+ eq(DROID_SPI)))
+ .thenReturn(DROID_SPI);
+
+ IpSecSpiResponse spiResp =
+ mIpSecService.reserveSecurityParameterIndex(
+ IpSecTransform.DIRECTION_OUT, mRemoteAddr, DROID_SPI, new Binder());
+ assertEquals(IpSecManager.Status.OK, spiResp.status);
+ assertEquals(DROID_SPI, spiResp.spi);
+ }
+
+ @Test
+ public void testReleaseSecurityParameterIndex() throws Exception {
+ when(mMockNetd.ipSecAllocateSpi(
+ anyInt(),
+ eq(IpSecTransform.DIRECTION_OUT),
+ anyString(),
+ eq(mRemoteAddr),
+ eq(DROID_SPI)))
+ .thenReturn(DROID_SPI);
+
+ IpSecSpiResponse spiResp =
+ mIpSecService.reserveSecurityParameterIndex(
+ IpSecTransform.DIRECTION_OUT, mRemoteAddr, DROID_SPI, new Binder());
+
+ mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
+
+ verify(mMockNetd)
+ .ipSecDeleteSecurityAssociation(
+ eq(spiResp.resourceId), anyInt(), anyString(), anyString(), eq(DROID_SPI));
+ }
+
+ IpSecConfig buildIpSecConfig() throws Exception {
+ IpSecManager ipSecManager = new IpSecManager(mIpSecService);
+
+ // Mocking the netd to allocate SPI
+ when(mMockNetd.ipSecAllocateSpi(anyInt(), anyInt(), anyString(), anyString(), anyInt()))
+ .thenReturn(DROID_SPI)
+ .thenReturn(DROID_SPI2);
+
+ IpSecAlgorithm encryptAlgo = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm authAlgo =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 8);
+
+ /** Allocate and add SPI records in the IpSecService through IpSecManager interface. */
+ IpSecManager.SecurityParameterIndex outSpi =
+ ipSecManager.reserveSecurityParameterIndex(
+ IpSecTransform.DIRECTION_OUT,
+ NetworkUtils.numericToInetAddress(mRemoteAddr));
+ IpSecManager.SecurityParameterIndex inSpi =
+ ipSecManager.reserveSecurityParameterIndex(
+ IpSecTransform.DIRECTION_IN,
+ NetworkUtils.numericToInetAddress(mRemoteAddr));
+
+ IpSecConfig config = new IpSecConfig();
+ config.setSpiResourceId(IpSecTransform.DIRECTION_IN, inSpi.getResourceId());
+ config.setSpiResourceId(IpSecTransform.DIRECTION_OUT, outSpi.getResourceId());
+ config.setEncryption(IpSecTransform.DIRECTION_OUT, encryptAlgo);
+ config.setAuthentication(IpSecTransform.DIRECTION_OUT, authAlgo);
+ config.setEncryption(IpSecTransform.DIRECTION_IN, encryptAlgo);
+ config.setAuthentication(IpSecTransform.DIRECTION_IN, authAlgo);
+ config.setRemoteAddress(mRemoteAddr);
+ return config;
+ }
+
+ @Test
+ public void testCreateTransportModeTransform() throws Exception {
+ IpSecConfig ipSecConfig = buildIpSecConfig();
+
+ IpSecTransformResponse createTransformResp =
+ mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+ assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+ verify(mMockNetd)
+ .ipSecAddSecurityAssociation(
+ eq(createTransformResp.resourceId),
+ anyInt(),
+ eq(IpSecTransform.DIRECTION_OUT),
+ anyString(),
+ anyString(),
+ anyLong(),
+ eq(DROID_SPI),
+ eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
+ eq(AUTH_KEY),
+ anyInt(),
+ eq(IpSecAlgorithm.CRYPT_AES_CBC),
+ eq(CRYPT_KEY),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyInt());
+ verify(mMockNetd)
+ .ipSecAddSecurityAssociation(
+ eq(createTransformResp.resourceId),
+ anyInt(),
+ eq(IpSecTransform.DIRECTION_IN),
+ anyString(),
+ anyString(),
+ anyLong(),
+ eq(DROID_SPI2),
+ eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
+ eq(AUTH_KEY),
+ anyInt(),
+ eq(IpSecAlgorithm.CRYPT_AES_CBC),
+ eq(CRYPT_KEY),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyInt());
+ }
+
+ @Test
+ public void testDeleteTransportModeTransform() throws Exception {
+ IpSecConfig ipSecConfig = buildIpSecConfig();
+
+ IpSecTransformResponse createTransformResp =
+ mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+ mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId);
+
+ verify(mMockNetd)
+ .ipSecDeleteSecurityAssociation(
+ eq(createTransformResp.resourceId),
+ eq(IpSecTransform.DIRECTION_OUT),
+ anyString(),
+ anyString(),
+ eq(DROID_SPI));
+ verify(mMockNetd)
+ .ipSecDeleteSecurityAssociation(
+ eq(createTransformResp.resourceId),
+ eq(IpSecTransform.DIRECTION_IN),
+ anyString(),
+ anyString(),
+ eq(DROID_SPI2));
+ }
+
+ @Test
+ public void testApplyTransportModeTransform() throws Exception {
+ IpSecConfig ipSecConfig = buildIpSecConfig();
+
+ IpSecTransformResponse createTransformResp =
+ mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
+
+ int resourceId = createTransformResp.resourceId;
+ mIpSecService.applyTransportModeTransform(pfd, resourceId);
+
+ verify(mMockNetd)
+ .ipSecApplyTransportModeTransform(
+ eq(pfd.getFileDescriptor()),
+ eq(resourceId),
+ eq(IpSecTransform.DIRECTION_OUT),
+ anyString(),
+ anyString(),
+ eq(DROID_SPI));
+ verify(mMockNetd)
+ .ipSecApplyTransportModeTransform(
+ eq(pfd.getFileDescriptor()),
+ eq(resourceId),
+ eq(IpSecTransform.DIRECTION_IN),
+ anyString(),
+ anyString(),
+ eq(DROID_SPI2));
+ }
+
+ @Test
+ public void testRemoveTransportModeTransform() throws Exception {
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
+ mIpSecService.removeTransportModeTransform(pfd, 1);
+
+ verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
+ }
+}
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 23fee28..efc58cc 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -23,34 +23,28 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.INetd;
-import android.net.IpSecAlgorithm;
-import android.net.IpSecConfig;
import android.net.IpSecManager;
import android.net.IpSecSpiResponse;
import android.net.IpSecTransform;
-import android.net.IpSecTransformResponse;
import android.net.IpSecUdpEncapResponse;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.support.test.filters.SmallTest;
import android.system.ErrnoException;
import android.system.Os;
+
import java.io.FileDescriptor;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -62,13 +56,8 @@
public class IpSecServiceTest {
private static final int DROID_SPI = 0xD1201D;
- private static final int DROID_SPI2 = DROID_SPI + 1;
private static final int TEST_UDP_ENCAP_INVALID_PORT = 100;
private static final int TEST_UDP_ENCAP_PORT_OUT_RANGE = 100000;
- private static final int TEST_UDP_ENCAP_PORT = 34567;
-
- private static final String IPV4_LOOPBACK = "127.0.0.1";
- private static final String IPV4_ADDR = "192.168.0.2";
private static final InetAddress INADDR_ANY;
@@ -80,21 +69,6 @@
}
}
- private static final int[] DIRECTIONS =
- new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN};
- private static final byte[] CRYPT_KEY = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
- 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
- };
- private static final byte[] AUTH_KEY = {
- 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
- 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
- };
-
Context mMockContext;
INetd mMockNetd;
IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
@@ -118,44 +92,6 @@
}
@Test
- public void testIpSecServiceReserveSpi() throws Exception {
- when(mMockNetd.ipSecAllocateSpi(
- anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- eq(IPV4_LOOPBACK),
- eq(DROID_SPI)))
- .thenReturn(DROID_SPI);
-
- IpSecSpiResponse spiResp =
- mIpSecService.reserveSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, IPV4_LOOPBACK, DROID_SPI, new Binder());
- assertEquals(IpSecManager.Status.OK, spiResp.status);
- assertEquals(DROID_SPI, spiResp.spi);
- }
-
- @Test
- public void testReleaseSecurityParameterIndex() throws Exception {
- when(mMockNetd.ipSecAllocateSpi(
- anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- eq(IPV4_LOOPBACK),
- eq(DROID_SPI)))
- .thenReturn(DROID_SPI);
-
- IpSecSpiResponse spiResp =
- mIpSecService.reserveSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, IPV4_LOOPBACK, DROID_SPI, new Binder());
-
- mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
-
- verify(mMockNetd)
- .ipSecDeleteSecurityAssociation(
- eq(spiResp.resourceId), anyInt(), anyString(), anyString(), eq(DROID_SPI));
- }
-
- @Test
public void testReleaseInvalidSecurityParameterIndex() throws Exception {
try {
mIpSecService.releaseSecurityParameterIndex(1);
@@ -285,108 +221,6 @@
}
}
- IpSecConfig buildIpSecConfig() throws Exception {
- IpSecManager ipSecManager = new IpSecManager(mIpSecService);
-
- // Mocking the netd to allocate SPI
- when(mMockNetd.ipSecAllocateSpi(anyInt(), anyInt(), anyString(), anyString(), anyInt()))
- .thenReturn(DROID_SPI)
- .thenReturn(DROID_SPI2);
-
- IpSecAlgorithm encryptAlgo = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
- IpSecAlgorithm authAlgo =
- new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 8);
-
- InetAddress localAddr = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
-
- /** Allocate and add SPI records in the IpSecService through IpSecManager interface. */
- IpSecManager.SecurityParameterIndex outSpi =
- ipSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, localAddr);
- IpSecManager.SecurityParameterIndex inSpi =
- ipSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_IN, localAddr);
-
- IpSecConfig ipSecConfig =
- new IpSecTransform.Builder(mMockContext)
- .setSpi(IpSecTransform.DIRECTION_OUT, outSpi)
- .setSpi(IpSecTransform.DIRECTION_IN, inSpi)
- .setEncryption(IpSecTransform.DIRECTION_OUT, encryptAlgo)
- .setAuthentication(IpSecTransform.DIRECTION_OUT, authAlgo)
- .setEncryption(IpSecTransform.DIRECTION_IN, encryptAlgo)
- .setAuthentication(IpSecTransform.DIRECTION_IN, authAlgo)
- .getIpSecConfig();
- return ipSecConfig;
- }
-
- @Test
- public void testCreateTransportModeTransform() throws Exception {
- IpSecConfig ipSecConfig = buildIpSecConfig();
-
- IpSecTransformResponse createTransformResp =
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
- assertEquals(IpSecManager.Status.OK, createTransformResp.status);
-
- verify(mMockNetd)
- .ipSecAddSecurityAssociation(
- eq(createTransformResp.resourceId),
- anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- anyString(),
- anyLong(),
- eq(DROID_SPI),
- eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
- eq(AUTH_KEY),
- anyInt(),
- eq(IpSecAlgorithm.CRYPT_AES_CBC),
- eq(CRYPT_KEY),
- anyInt(),
- anyInt(),
- anyInt(),
- anyInt());
- verify(mMockNetd)
- .ipSecAddSecurityAssociation(
- eq(createTransformResp.resourceId),
- anyInt(),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- anyLong(),
- eq(DROID_SPI2),
- eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
- eq(AUTH_KEY),
- anyInt(),
- eq(IpSecAlgorithm.CRYPT_AES_CBC),
- eq(CRYPT_KEY),
- anyInt(),
- anyInt(),
- anyInt(),
- anyInt());
- }
-
- @Test
- public void testDeleteTransportModeTransform() throws Exception {
- IpSecConfig ipSecConfig = buildIpSecConfig();
-
- IpSecTransformResponse createTransformResp =
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
- mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId);
-
- verify(mMockNetd)
- .ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- anyString(),
- eq(DROID_SPI));
- verify(mMockNetd)
- .ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- eq(DROID_SPI2));
- }
-
@Test
public void testDeleteInvalidTransportModeTransform() throws Exception {
try {
@@ -397,39 +231,31 @@
}
@Test
- public void testApplyTransportModeTransform() throws Exception {
- IpSecConfig ipSecConfig = buildIpSecConfig();
-
- IpSecTransformResponse createTransformResp =
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
- ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
-
- int resourceId = createTransformResp.resourceId;
- mIpSecService.applyTransportModeTransform(pfd, resourceId);
-
- verify(mMockNetd)
- .ipSecApplyTransportModeTransform(
- eq(pfd.getFileDescriptor()),
- eq(resourceId),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- anyString(),
- eq(DROID_SPI));
- verify(mMockNetd)
- .ipSecApplyTransportModeTransform(
- eq(pfd.getFileDescriptor()),
- eq(resourceId),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- eq(DROID_SPI2));
- }
-
- @Test
public void testRemoveTransportModeTransform() throws Exception {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
mIpSecService.removeTransportModeTransform(pfd, 1);
verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
}
+
+ @Test
+ public void testValidateIpAddresses() throws Exception {
+ String[] invalidAddresses =
+ new String[] {"www.google.com", "::", "2001::/64", "0.0.0.0", ""};
+ for (String address : invalidAddresses) {
+ try {
+ IpSecSpiResponse spiResp =
+ mIpSecService.reserveSecurityParameterIndex(
+ IpSecTransform.DIRECTION_OUT, address, DROID_SPI, new Binder());
+ fail("Invalid address was passed through IpSecService validation: " + address);
+ } catch (IllegalArgumentException e) {
+ } catch (Exception e) {
+ fail(
+ "Invalid InetAddress was not caught in validation: "
+ + address
+ + ", Exception: "
+ + e);
+ }
+ }
+ }
}
diff --git a/tests/net/java/com/android/server/NsdServiceTest.java b/tests/net/java/com/android/server/NsdServiceTest.java
index 2e49c98..b88c784 100644
--- a/tests/net/java/com/android/server/NsdServiceTest.java
+++ b/tests/net/java/com/android/server/NsdServiceTest.java
@@ -77,7 +77,10 @@
@After
public void tearDown() throws Exception {
- mThread.quit();
+ if (mThread != null) {
+ mThread.quit();
+ mThread = null;
+ }
}
@Test
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index e3f46a4..dfe31bd 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -85,6 +85,32 @@
}
@Test
+ public void testRequiresClat() throws Exception {
+ final int[] supportedTypes = {
+ ConnectivityManager.TYPE_MOBILE,
+ ConnectivityManager.TYPE_WIFI,
+ ConnectivityManager.TYPE_ETHERNET,
+ };
+
+ // NetworkInfo doesn't allow setting the State directly, but rather
+ // requires setting DetailedState in order set State as a side-effect.
+ final NetworkInfo.DetailedState[] supportedDetailedStates = {
+ NetworkInfo.DetailedState.CONNECTED,
+ NetworkInfo.DetailedState.SUSPENDED,
+ };
+
+ for (int type : supportedTypes) {
+ mNai.networkInfo.setType(type);
+ for (NetworkInfo.DetailedState state : supportedDetailedStates) {
+ mNai.networkInfo.setDetailedState(state, "reason", "extraInfo");
+ assertTrue(
+ String.format("requiresClat expected for type=%d state=%s", type, state),
+ Nat464Xlat.requiresClat(mNai));
+ }
+ }
+ }
+
+ @Test
public void testNormalStartAndStop() throws Exception {
Nat464Xlat nat = makeNat464Xlat();
ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
@@ -100,7 +126,6 @@
mLooper.dispatchNext();
verify(mNms).getInterfaceConfig(eq(STACKED_IFACE));
- verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(false));
verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
assertFalse(c.getValue().getStackedLinks().isEmpty());
assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
@@ -110,7 +135,6 @@
nat.stop();
verify(mNms).stopClatd(eq(BASE_IFACE));
- verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(true));
// Stacked interface removed notification arrives.
nat.interfaceRemoved(STACKED_IFACE);
@@ -141,7 +165,6 @@
mLooper.dispatchNext();
verify(mNms).getInterfaceConfig(eq(STACKED_IFACE));
- verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(false));
verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
assertFalse(c.getValue().getStackedLinks().isEmpty());
assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
@@ -153,7 +176,6 @@
verify(mNms).unregisterObserver(eq(nat));
verify(mNms).stopClatd(eq(BASE_IFACE));
- verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(true));
verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
assertTrue(c.getValue().getStackedLinks().isEmpty());
assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 7a2ff89..b98f63b 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -671,6 +671,35 @@
offload.setUpstreamLinkProperties(upstreamLp);
}
+ // Pretend that some local prefixes and downstreams have been added
+ // (and removed, for good measure).
+ final Set<IpPrefix> minimumLocalPrefixes = new HashSet<>();
+ for (String s : new String[]{
+ "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"}) {
+ minimumLocalPrefixes.add(new IpPrefix(s));
+ }
+ offload.setLocalPrefixes(minimumLocalPrefixes);
+
+ final LinkProperties usbLinkProperties = new LinkProperties();
+ usbLinkProperties.setInterfaceName(RNDIS0);
+ usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24"));
+ usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(USB_PREFIX)));
+ offload.notifyDownstreamLinkProperties(usbLinkProperties);
+
+ final LinkProperties wifiLinkProperties = new LinkProperties();
+ wifiLinkProperties.setInterfaceName(WLAN0);
+ wifiLinkProperties.addLinkAddress(new LinkAddress("192.168.43.1/24"));
+ wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix(WIFI_PREFIX)));
+ wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix(IPV6_LINKLOCAL)));
+ // Use a benchmark prefix (RFC 5180 + erratum), since the documentation
+ // prefix is included in the excluded prefix list.
+ wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::1/64"));
+ wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::2/64"));
+ wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix("2001:2::/64")));
+ offload.notifyDownstreamLinkProperties(wifiLinkProperties);
+
+ offload.removeDownstreamInterface(RNDIS0);
+
// Clear invocation history, especially the getForwardedStats() calls
// that happen with setUpstreamParameters().
clearInvocations(mHardware);
@@ -685,6 +714,17 @@
verifyNoMoreInteractions(mNMService);
// TODO: verify local prefixes and downstreams are also pushed to the HAL.
+ verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
+ ArrayList<String> localPrefixes = mStringArrayCaptor.getValue();
+ assertEquals(4, localPrefixes.size());
+ assertArrayListContains(localPrefixes,
+ // TODO: The logic to find and exclude downstream IP prefixes
+ // is currently in Tethering's OffloadWrapper but must be moved
+ // into OffloadController proper. After this, also check for:
+ // "192.168.43.1/32", "2001:2::1/128", "2001:2::2/128"
+ "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64");
+ verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "192.168.43.0/24");
+ verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "2001:2::/64");
verify(mHardware, times(1)).setUpstreamParameters(eq(RMNET0), any(), any(), any());
verify(mHardware, times(1)).setDataLimit(eq(RMNET0), anyLong());
verifyNoMoreInteractions(mHardware);
@@ -692,7 +732,7 @@
private static void assertArrayListContains(ArrayList<String> list, String... elems) {
for (String element : elems) {
- assertTrue(list.contains(element));
+ assertTrue(element + " not in list", list.contains(element));
}
}
}
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 5f586a1..627a231 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -1246,7 +1246,7 @@
if (kIsDebug) {
printf("Adding 9-patch info...\n");
}
- strcpy((char*)unknowns[p_index].name, "npTc");
+ memcpy((char*)unknowns[p_index].name, "npTc", 5);
unknowns[p_index].data = (png_byte*)imageInfo.serialize9patch();
unknowns[p_index].size = imageInfo.info9Patch.serializedSize();
// TODO: remove the check below when everything works
@@ -1254,7 +1254,7 @@
// automatically generated 9 patch outline data
int chunk_size = sizeof(png_uint_32) * 6;
- strcpy((char*)unknowns[o_index].name, "npOl");
+ memcpy((char*)unknowns[o_index].name, "npOl", 5);
unknowns[o_index].data = (png_byte*) calloc(chunk_size, 1);
png_byte outputData[chunk_size];
memcpy(&outputData, &imageInfo.outlineInsetsLeft, 4 * sizeof(png_uint_32));
@@ -1266,7 +1266,7 @@
// optional optical inset / layout bounds data
if (imageInfo.haveLayoutBounds) {
int chunk_size = sizeof(png_uint_32) * 4;
- strcpy((char*)unknowns[b_index].name, "npLb");
+ memcpy((char*)unknowns[b_index].name, "npLb", 5);
unknowns[b_index].data = (png_byte*) calloc(chunk_size, 1);
memcpy(unknowns[b_index].data, &imageInfo.layoutBoundsLeft, chunk_size);
unknowns[b_index].size = chunk_size;
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 43918da..ff51d51 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -83,9 +83,13 @@
"configuration/ConfigurationParser.cpp",
"filter/AbiFilter.cpp",
"filter/ConfigFilter.cpp",
- "flatten/Archive.cpp",
- "flatten/TableFlattener.cpp",
- "flatten/XmlFlattener.cpp",
+ "format/Archive.cpp",
+ "format/binary/BinaryResourceParser.cpp",
+ "format/binary/ResChunkPullParser.cpp",
+ "format/binary/TableFlattener.cpp",
+ "format/binary/XmlFlattener.cpp",
+ "format/proto/ProtoDeserialize.cpp",
+ "format/proto/ProtoSerialize.cpp",
"io/BigBufferStreams.cpp",
"io/File.cpp",
"io/FileInputStream.cpp",
@@ -106,14 +110,9 @@
"optimize/ResourceDeduper.cpp",
"optimize/VersionCollapser.cpp",
"process/SymbolTable.cpp",
- "proto/ProtoHelpers.cpp",
- "proto/TableProtoDeserializer.cpp",
- "proto/TableProtoSerializer.cpp",
"split/TableSplitter.cpp",
"text/Unicode.cpp",
"text/Utf8Iterator.cpp",
- "unflatten/BinaryResourceParser.cpp",
- "unflatten/ResChunkPullParser.cpp",
"util/BigBuffer.cpp",
"util/Files.cpp",
"util/Util.cpp",
@@ -139,6 +138,7 @@
"xml/XmlDom.cpp",
"xml/XmlPullParser.cpp",
"xml/XmlUtil.cpp",
+ "Configuration.proto",
"Resources.proto",
"ResourcesInternal.proto",
],
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index a9278c1..59a6e12 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -876,6 +876,12 @@
return copy;
}
+std::string ConfigDescription::GetBcp47LanguageTag(bool canonicalize) const {
+ char locale[RESTABLE_MAX_LOCALE_LEN];
+ getBcp47Locale(locale, canonicalize);
+ return std::string(locale);
+}
+
bool ConfigDescription::Dominates(const ConfigDescription& o) const {
if (*this == o) {
return true;
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index 65c9617..c1d0e10 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -61,6 +61,9 @@
ConfigDescription CopyWithoutSdkVersion() const;
+ // Returns the BCP-47 language tag of this configuration's locale.
+ std::string GetBcp47LanguageTag(bool canonicalize = false) const;
+
/**
* A configuration X dominates another configuration Y, if X has at least the
* precedence of Y and X is strictly more general than Y: for any type defined
diff --git a/tools/aapt2/Configuration.proto b/tools/aapt2/Configuration.proto
new file mode 100644
index 0000000..fc636a4
--- /dev/null
+++ b/tools/aapt2/Configuration.proto
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+package aapt.pb;
+
+option java_package = "com.android.aapt";
+option optimize_for = LITE_RUNTIME;
+
+// A description of the requirements a device must have in order for a
+// resource to be matched and selected.
+message Configuration {
+ enum LayoutDirection {
+ LAYOUT_DIRECTION_UNSET = 0;
+ LAYOUT_DIRECTION_LTR = 1;
+ LAYOUT_DIRECTION_RTL = 2;
+ }
+
+ enum ScreenLayoutSize {
+ SCREEN_LAYOUT_SIZE_UNSET = 0;
+ SCREEN_LAYOUT_SIZE_SMALL = 1;
+ SCREEN_LAYOUT_SIZE_NORMAL = 2;
+ SCREEN_LAYOUT_SIZE_LARGE = 3;
+ SCREEN_LAYOUT_SIZE_XLARGE = 4;
+ }
+
+ enum ScreenLayoutLong {
+ SCREEN_LAYOUT_LONG_UNSET = 0;
+ SCREEN_LAYOUT_LONG_LONG = 1;
+ SCREEN_LAYOUT_LONG_NOTLONG = 2;
+ }
+
+ enum ScreenRound {
+ SCREEN_ROUND_UNSET = 0;
+ SCREEN_ROUND_ROUND = 1;
+ SCREEN_ROUND_NOTROUND = 2;
+ }
+
+ enum WideColorGamut {
+ WIDE_COLOR_GAMUT_UNSET = 0;
+ WIDE_COLOR_GAMUT_WIDECG = 1;
+ WIDE_COLOR_GAMUT_NOWIDECG = 2;
+ }
+
+ enum Hdr {
+ HDR_UNSET = 0;
+ HDR_HIGHDR = 1;
+ HDR_LOWDR = 2;
+ }
+
+ enum Orientation {
+ ORIENTATION_UNSET = 0;
+ ORIENTATION_PORT = 1;
+ ORIENTATION_LAND = 2;
+ ORIENTATION_SQUARE = 3;
+ }
+
+ enum UiModeType {
+ UI_MODE_TYPE_UNSET = 0;
+ UI_MODE_TYPE_NORMAL = 1;
+ UI_MODE_TYPE_DESK = 2;
+ UI_MODE_TYPE_CAR = 3;
+ UI_MODE_TYPE_TELEVISION = 4;
+ UI_MODE_TYPE_APPLIANCE = 5;
+ UI_MODE_TYPE_WATCH = 6;
+ UI_MODE_TYPE_VRHEADSET = 7;
+ }
+
+ enum UiModeNight {
+ UI_MODE_NIGHT_UNSET = 0;
+ UI_MODE_NIGHT_NIGHT = 1;
+ UI_MODE_NIGHT_NOTNIGHT = 2;
+ }
+
+ enum Touchscreen {
+ TOUCHSCREEN_UNSET = 0;
+ TOUCHSCREEN_NOTOUCH = 1;
+ TOUCHSCREEN_STYLUS = 2;
+ TOUCHSCREEN_FINGER = 3;
+ }
+
+ enum KeysHidden {
+ KEYS_HIDDEN_UNSET = 0;
+ KEYS_HIDDEN_KEYSEXPOSED = 1;
+ KEYS_HIDDEN_KEYSHIDDEN = 2;
+ KEYS_HIDDEN_KEYSSOFT = 3;
+ }
+
+ enum Keyboard {
+ KEYBOARD_UNSET = 0;
+ KEYBOARD_NOKEYS = 1;
+ KEYBOARD_QWERTY = 2;
+ KEYBOARD_TWELVEKEY = 3;
+ }
+
+ enum NavHidden {
+ NAV_HIDDEN_UNSET = 0;
+ NAV_HIDDEN_NAVEXPOSED = 1;
+ NAV_HIDDEN_NAVHIDDEN = 2;
+ }
+
+ enum Navigation {
+ NAVIGATION_UNSET = 0;
+ NAVIGATION_NONAV = 1;
+ NAVIGATION_DPAD = 2;
+ NAVIGATION_TRACKBALL = 3;
+ NAVIGATION_WHEEL = 4;
+ }
+
+ //
+ // Axis/dimensions that are understood by the runtime.
+ //
+
+ // Mobile country code.
+ uint32 mcc = 1;
+
+ // Mobile network code.
+ uint32 mnc = 2;
+
+ // BCP-47 locale tag.
+ string locale = 3;
+
+ // Left-to-right, right-to-left...
+ LayoutDirection layout_direction = 4;
+
+ // Screen width in pixels. Prefer screen_width_dp.
+ uint32 screen_width = 5;
+
+ // Screen height in pixels. Prefer screen_height_dp.
+ uint32 screen_height = 6;
+
+ // Screen width in density independent pixels (dp).
+ uint32 screen_width_dp = 7;
+
+ // Screen height in density independent pixels (dp).
+ uint32 screen_height_dp = 8;
+
+ // The smallest screen dimension, regardless of orientation, in dp.
+ uint32 smallest_screen_width_dp = 9;
+
+ // Whether the device screen is classified as small, normal, large, xlarge.
+ ScreenLayoutSize screen_layout_size = 10;
+
+ // Whether the device screen is long.
+ ScreenLayoutLong screen_layout_long = 11;
+
+ // Whether the screen is round (Android Wear).
+ ScreenRound screen_round = 12;
+
+ // Whether the screen supports wide color gamut.
+ WideColorGamut wide_color_gamut = 13;
+
+ // Whether the screen has high dynamic range.
+ Hdr hdr = 14;
+
+ // Which orientation the device is in (portrait, landscape).
+ Orientation orientation = 15;
+
+ // Which type of UI mode the device is in (television, car, etc.).
+ UiModeType ui_mode_type = 16;
+
+ // Whether the device is in night mode.
+ UiModeNight ui_mode_night = 17;
+
+ // The device's screen density in dots-per-inch (dpi).
+ uint32 density = 18;
+
+ // Whether a touchscreen exists, supports a stylus, or finger.
+ Touchscreen touchscreen = 19;
+
+ // Whether the keyboard hardware keys are currently hidden, exposed, or
+ // if the keyboard is a software keyboard.
+ KeysHidden keys_hidden = 20;
+
+ // The type of keyboard present (none, QWERTY, 12-key).
+ Keyboard keyboard = 21;
+
+ // Whether the navigation is exposed or hidden.
+ NavHidden nav_hidden = 22;
+
+ // The type of navigation present on the device
+ // (trackball, wheel, dpad, etc.).
+ Navigation navigation = 23;
+
+ // The minimum SDK version of the device.
+ uint32 sdk_version = 24;
+
+ //
+ // Build-time only dimensions.
+ //
+
+ string product = 25;
+}
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index dba9cb5..1555d6126 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -35,9 +35,9 @@
namespace {
-class PrintVisitor : public ValueVisitor {
+class PrintVisitor : public DescendingValueVisitor {
public:
- using ValueVisitor::Visit;
+ using DescendingValueVisitor::Visit;
void Visit(Attribute* attr) override {
std::cout << "(attr) type=";
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index b80780e..c1815c8 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -18,13 +18,17 @@
#include "ResourceValues.h"
#include "ValueVisitor.h"
-#include "flatten/Archive.h"
-#include "flatten/TableFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/TableFlattener.h"
+#include "format/binary/XmlFlattener.h"
#include "io/BigBufferInputStream.h"
#include "io/Util.h"
+#include "xml/XmlDom.h"
namespace aapt {
+using xml::XmlResource;
+
std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context,
const android::StringPiece& path) {
Source source(path);
@@ -52,6 +56,7 @@
if (!parser.Parse()) {
return {};
}
+
return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
}
@@ -63,7 +68,7 @@
bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table,
const TableFlattenerOptions& options, FilterChain* filters,
- IArchiveWriter* writer) {
+ IArchiveWriter* writer, XmlResource* manifest) {
std::set<std::string> referenced_resources;
// List the files being referenced in the resource table.
for (auto& pkg : split_table->packages) {
@@ -119,6 +124,20 @@
return false;
}
+ } else if (manifest != nullptr && path == "AndroidManifest.xml") {
+ BigBuffer buffer(8192);
+ XmlFlattener xml_flattener(&buffer, {});
+ if (!xml_flattener.Consume(context, manifest)) {
+ context->GetDiagnostics()->Error(DiagMessage(path) << "flattening failed");
+ return false;
+ }
+
+ uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
+ io::BigBufferInputStream manifest_buffer_in(&buffer);
+ if (!io::CopyInputStreamToArchive(context, &manifest_buffer_in, path, compression_flags,
+ writer)) {
+ return false;
+ }
} else {
uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
if (!io::CopyFileToArchive(context, file, path, compression_flags, writer)) {
@@ -129,4 +148,26 @@
return true;
}
+std::unique_ptr<xml::XmlResource> LoadedApk::InflateManifest(IAaptContext* context) {
+ IDiagnostics* diag = context->GetDiagnostics();
+
+ io::IFile* manifest_file = GetFileCollection()->FindFile("AndroidManifest.xml");
+ if (manifest_file == nullptr) {
+ diag->Error(DiagMessage(source_) << "no AndroidManifest.xml found");
+ return {};
+ }
+
+ std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
+ if (manifest_data == nullptr) {
+ diag->Error(DiagMessage(manifest_file->GetSource()) << "could not open AndroidManifest.xml");
+ return {};
+ }
+
+ std::unique_ptr<xml::XmlResource> manifest =
+ xml::Inflate(manifest_data->data(), manifest_data->size(), diag, manifest_file->GetSource());
+ if (manifest == nullptr) {
+ diag->Error(DiagMessage() << "failed to read binary AndroidManifest.xml");
+ }
+ return manifest;
+}
} // namespace aapt
diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h
index dacd0c2..d2dd5cf 100644
--- a/tools/aapt2/LoadedApk.h
+++ b/tools/aapt2/LoadedApk.h
@@ -21,14 +21,15 @@
#include "ResourceTable.h"
#include "filter/Filter.h"
-#include "flatten/Archive.h"
-#include "flatten/TableFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/BinaryResourceParser.h"
+#include "format/binary/TableFlattener.h"
#include "io/ZipArchive.h"
-#include "unflatten/BinaryResourceParser.h"
+#include "xml/XmlDom.h"
namespace aapt {
-/** Info about an APK loaded in memory. */
+// Info about an APK loaded in memory.
class LoadedApk {
public:
LoadedApk(
@@ -36,12 +37,19 @@
std::unique_ptr<io::IFileCollection> apk,
std::unique_ptr<ResourceTable> table)
: source_(source), apk_(std::move(apk)), table_(std::move(table)) {}
+ virtual ~LoadedApk() = default;
- io::IFileCollection* GetFileCollection() { return apk_.get(); }
+ io::IFileCollection* GetFileCollection() {
+ return apk_.get();
+ }
- ResourceTable* GetResourceTable() { return table_.get(); }
+ ResourceTable* GetResourceTable() {
+ return table_.get();
+ }
- const Source& GetSource() { return source_; }
+ const Source& GetSource() {
+ return source_;
+ }
/**
* Writes the APK on disk at the given path, while also removing the resource
@@ -51,23 +59,30 @@
IArchiveWriter* writer);
/**
- * Writes the APK on disk at the given path, while also removing the resource
- * files that are not referenced in the resource table. The provided filter
- * chain is applied to each entry in the APK file.
+ * Writes the APK on disk at the given path, while also removing the resource files that are not
+ * referenced in the resource table. The provided filter chain is applied to each entry in the APK
+ * file.
+ *
+ * If the manifest is also provided, it will be written to the new APK file, otherwise the
+ * original manifest will be written. The manifest is only required if the contents of the new APK
+ * have been modified in a way that require the AndroidManifest.xml to also be modified.
*/
virtual bool WriteToArchive(IAaptContext* context, ResourceTable* split_table,
const TableFlattenerOptions& options, FilterChain* filters,
- IArchiveWriter* writer);
+ IArchiveWriter* writer, xml::XmlResource* manifest = nullptr);
+
+ /** Inflates the AndroidManifest.xml file from the APK. */
+ std::unique_ptr<xml::XmlResource> InflateManifest(IAaptContext* context);
static std::unique_ptr<LoadedApk> LoadApkFromPath(IAaptContext* context,
const android::StringPiece& path);
private:
+ DISALLOW_COPY_AND_ASSIGN(LoadedApk);
+
Source source_;
std::unique_ptr<io::IFileCollection> apk_;
std::unique_ptr<ResourceTable> table_;
-
- DISALLOW_COPY_AND_ASSIGN(LoadedApk);
};
} // namespace aapt
diff --git a/tools/aapt2/Locale.cpp b/tools/aapt2/Locale.cpp
index 7664fac..d81921f 100644
--- a/tools/aapt2/Locale.cpp
+++ b/tools/aapt2/Locale.cpp
@@ -24,9 +24,10 @@
#include "util/Util.h"
-namespace aapt {
+using ::android::ResTable_config;
+using ::android::StringPiece;
-using android::ResTable_config;
+namespace aapt {
void LocaleValue::set_language(const char* language_chars) {
size_t i = 0;
@@ -72,7 +73,7 @@
return std::all_of(std::begin(str), std::end(str), ::isdigit);
}
-bool LocaleValue::InitFromFilterString(const android::StringPiece& str) {
+bool LocaleValue::InitFromFilterString(const StringPiece& str) {
// A locale (as specified in the filter) is an underscore separated name such
// as "en_US", "en_Latn_US", or "en_US_POSIX".
std::vector<std::string> parts = util::SplitAndLowercase(str, '_');
@@ -138,6 +139,71 @@
return true;
}
+bool LocaleValue::InitFromBcp47Tag(const StringPiece& bcp47tag) {
+ return InitFromBcp47TagImpl(bcp47tag, '-');
+}
+
+bool LocaleValue::InitFromBcp47TagImpl(const StringPiece& bcp47tag, const char separator) {
+ std::vector<std::string> subtags = util::SplitAndLowercase(bcp47tag, separator);
+ if (subtags.size() == 1) {
+ set_language(subtags[0].c_str());
+ } else if (subtags.size() == 2) {
+ set_language(subtags[0].c_str());
+
+ // The second tag can either be a region, a variant or a script.
+ switch (subtags[1].size()) {
+ case 2:
+ case 3:
+ set_region(subtags[1].c_str());
+ break;
+ case 4:
+ if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
+ // This is a variant: fall through
+ } else {
+ set_script(subtags[1].c_str());
+ break;
+ }
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ set_variant(subtags[1].c_str());
+ break;
+ default:
+ return false;
+ }
+ } else if (subtags.size() == 3) {
+ // The language is always the first subtag.
+ set_language(subtags[0].c_str());
+
+ // The second subtag can either be a script or a region code.
+ // If its size is 4, it's a script code, else it's a region code.
+ if (subtags[1].size() == 4) {
+ set_script(subtags[1].c_str());
+ } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
+ set_region(subtags[1].c_str());
+ } else {
+ return false;
+ }
+
+ // The third tag can either be a region code (if the second tag was
+ // a script), else a variant code.
+ if (subtags[2].size() >= 4) {
+ set_variant(subtags[2].c_str());
+ } else {
+ set_region(subtags[2].c_str());
+ }
+ } else if (subtags.size() == 4) {
+ set_language(subtags[0].c_str());
+ set_script(subtags[1].c_str());
+ set_region(subtags[2].c_str());
+ set_variant(subtags[3].c_str());
+ } else {
+ return false;
+ }
+ return true;
+}
+
ssize_t LocaleValue::InitFromParts(std::vector<std::string>::iterator iter,
std::vector<std::string>::iterator end) {
const std::vector<std::string>::iterator start_iter = iter;
@@ -145,71 +211,13 @@
std::string& part = *iter;
if (part[0] == 'b' && part[1] == '+') {
// This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags,
- // except that the separator is "+" and not "-".
- std::vector<std::string> subtags = util::SplitAndLowercase(part, '+');
- subtags.erase(subtags.begin());
- if (subtags.size() == 1) {
- set_language(subtags[0].c_str());
- } else if (subtags.size() == 2) {
- set_language(subtags[0].c_str());
-
- // The second tag can either be a region, a variant or a script.
- switch (subtags[1].size()) {
- case 2:
- case 3:
- set_region(subtags[1].c_str());
- break;
- case 4:
- if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
- // This is a variant: fall through
- } else {
- set_script(subtags[1].c_str());
- break;
- }
- case 5:
- case 6:
- case 7:
- case 8:
- set_variant(subtags[1].c_str());
- break;
- default:
- return -1;
- }
- } else if (subtags.size() == 3) {
- // The language is always the first subtag.
- set_language(subtags[0].c_str());
-
- // The second subtag can either be a script or a region code.
- // If its size is 4, it's a script code, else it's a region code.
- if (subtags[1].size() == 4) {
- set_script(subtags[1].c_str());
- } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
- set_region(subtags[1].c_str());
- } else {
- return -1;
- }
-
- // The third tag can either be a region code (if the second tag was
- // a script), else a variant code.
- if (subtags[2].size() >= 4) {
- set_variant(subtags[2].c_str());
- } else {
- set_region(subtags[2].c_str());
- }
- } else if (subtags.size() == 4) {
- set_language(subtags[0].c_str());
- set_script(subtags[1].c_str());
- set_region(subtags[2].c_str());
- set_variant(subtags[3].c_str());
- } else {
+ // except that the separator is "+" and not "-". Skip the prefix 'b+'.
+ if (!InitFromBcp47TagImpl(StringPiece(part).substr(2), '+')) {
return -1;
}
-
++iter;
-
} else {
- if ((part.length() == 2 || part.length() == 3) && is_alpha(part) &&
- part != "car") {
+ if ((part.length() == 2 || part.length() == 3) && is_alpha(part) && part != "car") {
set_language(part.c_str());
++iter;
@@ -222,7 +230,6 @@
}
}
}
-
return static_cast<ssize_t>(iter - start_iter);
}
diff --git a/tools/aapt2/Locale.h b/tools/aapt2/Locale.h
index 3d73b2e..6d8b598 100644
--- a/tools/aapt2/Locale.h
+++ b/tools/aapt2/Locale.h
@@ -41,6 +41,9 @@
*/
bool InitFromFilterString(const android::StringPiece& config);
+ // Initializes this LocaleValue from a BCP-47 locale tag.
+ bool InitFromBcp47Tag(const android::StringPiece& bcp47tag);
+
/**
* Initialize this LocaleValue from parts of a vector.
*/
@@ -67,6 +70,8 @@
inline bool operator>(const LocaleValue& o) const;
private:
+ bool InitFromBcp47TagImpl(const android::StringPiece& bcp47tag, const char separator);
+
void set_language(const char* language);
void set_region(const char* language);
void set_script(const char* script);
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index f193fe0..6fac6e9 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -23,12 +23,12 @@
#include "NameMangler.h"
#include "SdkConstants.h"
-#include "flatten/ResourceTypeExtensions.h"
+#include "format/binary/ResourceTypeExtensions.h"
#include "util/Files.h"
#include "util/Util.h"
-using android::StringPiece;
-using android::StringPiece16;
+using ::android::StringPiece;
+using ::android::StringPiece16;
namespace aapt {
namespace ResourceUtils {
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 1cba194..e013729 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -35,15 +35,25 @@
}
template <typename Derived>
-void BaseValue<Derived>::Accept(RawValueVisitor* visitor) {
+void BaseValue<Derived>::Accept(ValueVisitor* visitor) {
visitor->Visit(static_cast<Derived*>(this));
}
template <typename Derived>
-void BaseItem<Derived>::Accept(RawValueVisitor* visitor) {
+void BaseValue<Derived>::Accept(ConstValueVisitor* visitor) const {
+ visitor->Visit(static_cast<const Derived*>(this));
+}
+
+template <typename Derived>
+void BaseItem<Derived>::Accept(ValueVisitor* visitor) {
visitor->Visit(static_cast<Derived*>(this));
}
+template <typename Derived>
+void BaseItem<Derived>::Accept(ConstValueVisitor* visitor) const {
+ visitor->Visit(static_cast<const Derived*>(this));
+}
+
RawString::RawString(const StringPool::Ref& ref) : value(ref) {}
bool RawString::Equals(const Value* value) const {
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 275864b..742765d 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -33,7 +33,8 @@
namespace aapt {
-struct RawValueVisitor;
+class ValueVisitor;
+class ConstValueVisitor;
// A resource value. This is an all-encompassing representation
// of Item and Map and their subclasses. The way to do
@@ -45,36 +46,58 @@
virtual ~Value() = default;
// Whether this value is weak and can be overridden without warning or error. Default is false.
- bool IsWeak() const { return weak_; }
+ bool IsWeak() const {
+ return weak_;
+ }
- void SetWeak(bool val) { weak_ = val; }
+ void SetWeak(bool val) {
+ weak_ = val;
+ }
- // Whether the value is marked as translatable.
- // This does not persist when flattened.
+ // Whether the value is marked as translatable. This does not persist when flattened to binary.
// It is only used during compilation phase.
- void SetTranslatable(bool val) { translatable_ = val; }
+ void SetTranslatable(bool val) {
+ translatable_ = val;
+ }
// Default true.
- bool IsTranslatable() const { return translatable_; }
+ bool IsTranslatable() const {
+ return translatable_;
+ }
// Returns the source where this value was defined.
- const Source& GetSource() const { return source_; }
+ const Source& GetSource() const {
+ return source_;
+ }
- void SetSource(const Source& source) { source_ = source; }
+ void SetSource(const Source& source) {
+ source_ = source;
+ }
- void SetSource(Source&& source) { source_ = std::move(source); }
+ void SetSource(Source&& source) {
+ source_ = std::move(source);
+ }
// Returns the comment that was associated with this resource.
- const std::string& GetComment() const { return comment_; }
+ const std::string& GetComment() const {
+ return comment_;
+ }
- void SetComment(const android::StringPiece& str) { comment_ = str.to_string(); }
+ void SetComment(const android::StringPiece& str) {
+ comment_ = str.to_string();
+ }
- void SetComment(std::string&& str) { comment_ = std::move(str); }
+ void SetComment(std::string&& str) {
+ comment_ = std::move(str);
+ }
virtual bool Equals(const Value* value) const = 0;
// Calls the appropriate overload of ValueVisitor.
- virtual void Accept(RawValueVisitor* visitor) = 0;
+ virtual void Accept(ValueVisitor* visitor) = 0;
+
+ // Calls the appropriate overload of ConstValueVisitor.
+ virtual void Accept(ConstValueVisitor* visitor) const = 0;
// Clone the value. `new_pool` is the new StringPool that
// any resources with strings should use when copying their string.
@@ -95,7 +118,8 @@
// Inherit from this to get visitor accepting implementations for free.
template <typename Derived>
struct BaseValue : public Value {
- void Accept(RawValueVisitor* visitor) override;
+ void Accept(ValueVisitor* visitor) override;
+ void Accept(ConstValueVisitor* visitor) const override;
};
// A resource item with a single value. This maps to android::ResTable_entry.
@@ -111,7 +135,8 @@
// Inherit from this to get visitor accepting implementations for free.
template <typename Derived>
struct BaseItem : public Item {
- void Accept(RawValueVisitor* visitor) override;
+ void Accept(ValueVisitor* visitor) override;
+ void Accept(ConstValueVisitor* visitor) const override;
};
// A reference to another resource. This maps to android::Res_value::TYPE_REFERENCE.
@@ -144,7 +169,10 @@
// An ID resource. Has no real value, just a place holder.
struct Id : public BaseItem<Id> {
- Id() { weak_ = true; }
+ Id() {
+ weak_ = true;
+ }
+
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out) const override;
Id* Clone(StringPool* new_pool) const override;
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 71f33b0..174b7f6 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -14,77 +14,80 @@
* limitations under the License.
*/
-// Keep proto2 syntax because we require the distinction between fields that
-// are set and unset.
-syntax = "proto2";
+syntax = "proto3";
+
+import "frameworks/base/tools/aapt2/Configuration.proto";
+
+package aapt.pb;
option java_package = "com.android.aapt";
option optimize_for = LITE_RUNTIME;
-package aapt.pb;
-
-// A configuration description that wraps the binary form of the C++ class
-// aapt::ConfigDescription, with an added product definition.
-// TODO(adamlesinski): Flesh this out to be represented in proto.
-message ConfigDescription {
- optional bytes data = 1;
- optional string product = 2;
-}
-
// A string pool that wraps the binary form of the C++ class android::ResStringPool.
message StringPool {
- optional bytes data = 1;
+ bytes data = 1;
}
// The position of a declared entity within a file.
message SourcePosition {
- optional uint32 line_number = 1;
- optional uint32 column_number = 2;
+ uint32 line_number = 1;
+ uint32 column_number = 2;
}
// Developer friendly source file information for an entity in the resource table.
message Source {
// The index of the string path within the source string pool of a ResourceTable.
- optional uint32 path_idx = 1;
- optional SourcePosition position = 2;
+ uint32 path_idx = 1;
+ SourcePosition position = 2;
}
// Top level message representing a resource table.
message ResourceTable {
// The string pool containing source paths referenced throughout the resource table. This does
// not end up in the final binary ARSC file.
- optional StringPool source_pool = 1;
+ StringPool source_pool = 1;
// Resource definitions corresponding to an Android package.
repeated Package package = 2;
}
+// A package ID in the range [0x00, 0xff].
+message PackageId {
+ uint32 id = 1;
+}
+
// Defines resources for an Android package.
message Package {
// The package ID of this package, in the range [0x00, 0xff].
- // The ID 0x00 is reserved for shared libraries, or when the ID is assigned at run-time.
- // The ID 0x01 is reserved for the 'android' package (framework).
- // The ID range [0x02, 0x7f) is reserved for auto-assignment to shared libraries at run-time.
- // The ID 0x7f is reserved for the application package.
- // IDs > 0x7f are reserved for the application as well and are treated as feature splits.
- optional uint32 package_id = 1;
+ // - ID 0x00 is reserved for shared libraries, or when the ID is assigned at run-time.
+ // - ID 0x01 is reserved for the 'android' package (framework).
+ // - ID range [0x02, 0x7f) is reserved for auto-assignment to shared libraries at run-time.
+ // - ID 0x7f is reserved for the application package.
+ // - IDs > 0x7f are reserved for the application as well and are treated as feature splits.
+ // This may not be set if no ID was assigned.
+ PackageId package_id = 1;
// The Java compatible Android package name of the app.
- optional string package_name = 2;
+ string package_name = 2;
// The series of types defined by the package.
repeated Type type = 3;
}
+// A type ID in the range [0x01, 0xff].
+message TypeId {
+ uint32 id = 1;
+}
+
// A set of resources grouped under a common type. Such types include string, layout, xml, dimen,
// attr, etc. This maps to the second part of a resource identifier in Java (R.type.entry).
message Type {
- // The ID of the type. This may be 0, which indicates no ID is set.
- optional uint32 id = 1;
+ // The ID of the type. This may not be set if no ID was assigned.
+ TypeId type_id = 1;
// The name of the type. This corresponds to the 'type' part of a full resource name of the form
// package:type/entry. The set of legal type names is listed in Resource.cpp.
- optional string name = 2;
+ string name = 2;
// The entries defined for this type.
repeated Entry entry = 3;
@@ -112,17 +115,22 @@
PUBLIC = 2;
}
- optional Visibility visibility = 1;
+ Visibility visibility = 1;
// The path at which this entry's visibility was defined (eg. public.xml).
- optional Source source = 2;
+ Source source = 2;
// The comment associated with the <public> tag.
- optional string comment = 3;
+ string comment = 3;
// Whether the symbol can be merged into another resource table without there being an existing
// definition to override. Used for overlays and set to true when <add-resource> is specified.
- optional bool allow_new = 4;
+ bool allow_new = 4;
+}
+
+// An entry ID in the range [0x0000, 0xffff].
+message EntryId {
+ uint32 id = 1;
}
// An entry declaration. An entry has a full resource ID that is the combination of package ID,
@@ -132,14 +140,15 @@
// The ID of this entry. Together with the package ID and type ID, this forms a full resource ID
// of the form 0xPPTTEEEE, where PP is the package ID, TT is the type ID, and EEEE is the entry
// ID.
- optional uint32 id = 1;
+ // This may not be set if no ID was assigned.
+ EntryId entry_id = 1;
// The name of this entry. This corresponds to the 'entry' part of a full resource name of the
// form package:type/entry.
- optional string name = 2;
+ string name = 2;
// The symbol status of this entry, which includes visibility information.
- optional SymbolStatus symbol_status = 3;
+ SymbolStatus symbol_status = 3;
// The set of values defined for this entry, each corresponding to a different
// configuration/variant.
@@ -148,50 +157,54 @@
// A Configuration/Value pair.
message ConfigValue {
- optional ConfigDescription config = 1;
- optional Value value = 2;
+ Configuration config = 1;
+ Value value = 2;
}
// The generic meta-data for every value in a resource table.
message Value {
// Where the value was defined.
- optional Source source = 1;
+ Source source = 1;
// Any comment associated with the value.
- optional string comment = 2;
+ string comment = 2;
// Whether the value can be overridden.
- optional bool weak = 3;
+ bool weak = 3;
- // If the value is an Item, this is set.
- optional Item item = 4;
-
- // If the value is a CompoundValue, this is set.
- optional CompoundValue compound_value = 5;
+ // The value is either an Item or a CompoundValue.
+ oneof value {
+ Item item = 4;
+ CompoundValue compound_value = 5;
+ }
}
// An Item is an abstract type. It represents a value that can appear inline in many places, such
// as XML attribute values or on the right hand side of style attribute definitions. The concrete
// type is one of the types below. Only one can be set.
message Item {
- optional Reference ref = 1;
- optional String str = 2;
- optional RawString raw_str = 3;
- optional StyledString styled_str = 4;
- optional FileReference file = 5;
- optional Id id = 6;
- optional Primitive prim = 7;
+ oneof value {
+ Reference ref = 1;
+ String str = 2;
+ RawString raw_str = 3;
+ StyledString styled_str = 4;
+ FileReference file = 5;
+ Id id = 6;
+ Primitive prim = 7;
+ }
}
// A CompoundValue is an abstract type. It represents a value that is a made of other values.
// These can only usually appear as top-level resources. The concrete type is one of the types
// below. Only one can be set.
message CompoundValue {
- optional Attribute attr = 1;
- optional Style style = 2;
- optional Styleable styleable = 3;
- optional Array array = 4;
- optional Plural plural = 5;
+ oneof value {
+ Attribute attr = 1;
+ Style style = 2;
+ Styleable styleable = 3;
+ Array array = 4;
+ Plural plural = 5;
+ }
}
// A value that is a reference to another resource. This reference can be by name or resource ID.
@@ -204,16 +217,16 @@
ATTRIBUTE = 1;
}
- optional Type type = 1;
+ Type type = 1;
- // The resource ID (0xPPTTEEEE) of the resource being referred.
- optional uint32 id = 2;
+ // The resource ID (0xPPTTEEEE) of the resource being referred. This is optional.
+ uint32 id = 2;
- // The optional resource name.
- optional string name = 3;
+ // The name of the resource being referred. This is optional if the resource ID is set.
+ string name = 3;
// Whether this reference is referencing a private resource (@*package:type/entry).
- optional bool private = 4;
+ bool private = 4;
}
// A value that represents an ID. This is just a placeholder, as ID values are used to occupy a
@@ -223,32 +236,32 @@
// A value that is a string.
message String {
- optional string value = 1;
+ string value = 1;
}
// A value that is a raw string, which is unescaped/uninterpreted. This is typically used to
// represent the value of a style attribute before the attribute is compiled and the set of
// allowed values is known.
message RawString {
- optional string value = 1;
+ string value = 1;
}
// A string with styling information, like html tags that specify boldness, italics, etc.
message StyledString {
// The raw text of the string.
- optional string value = 1;
+ string value = 1;
// A Span marks a region of the string text that is styled.
message Span {
// The name of the tag, and its attributes, encoded as follows:
// tag_name;attr1=value1;attr2=value2;[...]
- optional string tag = 1;
+ string tag = 1;
// The first character position this span applies to, in UTF-16 offset.
- optional uint32 first_char = 2;
+ uint32 first_char = 2;
// The last character position this span applies to, in UTF-16 offset.
- optional uint32 last_char = 3;
+ uint32 last_char = 3;
}
repeated Span span = 2;
@@ -257,14 +270,14 @@
// A value that is a reference to an external entity, like an XML file or a PNG.
message FileReference {
// Path to a file within the APK (typically res/type-config/entry.ext).
- optional string path = 1;
+ string path = 1;
}
// A value that represents a primitive data type (float, int, boolean, etc.).
// Corresponds to the fields (type/data) of the C struct android::Res_value.
message Primitive {
- optional uint32 type = 1;
- optional uint32 data = 2;
+ uint32 type = 1;
+ uint32 data = 2;
}
// A value that represents an XML attribute and what values it accepts.
@@ -272,21 +285,22 @@
// A Symbol used to represent an enum or a flag.
message Symbol {
// Where the enum/flag item was defined.
- optional Source source = 1;
+ Source source = 1;
// Any comments associated with the enum or flag.
- optional string comment = 2;
+ string comment = 2;
// The name of the enum/flag as a reference. Enums/flag items are generated as ID resource
// values.
- optional Reference name = 3;
+ Reference name = 3;
// The value of the enum/flag.
- optional uint32 value = 4;
+ uint32 value = 4;
}
// Bitmask of formats allowed for an attribute.
enum FormatFlags {
+ NONE = 0x0; // Proto3 requires a default of 0.
ANY = 0x0000ffff; // Allows any type except ENUM and FLAGS.
REFERENCE = 0x01; // Allows Reference values.
STRING = 0x02; // Allows String/StyledString values.
@@ -304,15 +318,15 @@
// A bitmask of types that this XML attribute accepts. Corresponds to the flags in the
// enum FormatFlags.
- optional uint32 format_flags = 1;
+ uint32 format_flags = 1;
// The smallest integer allowed for this XML attribute. Only makes sense if the format includes
// FormatFlags::INTEGER.
- optional int32 min_int = 2;
+ int32 min_int = 2;
// The largest integer allowed for this XML attribute. Only makes sense if the format includes
// FormatFlags::INTEGER.
- optional int32 max_int = 3;
+ int32 max_int = 3;
// The set of enums/flags defined in this attribute. Only makes sense if the format includes
// either FormatFlags::ENUM or FormatFlags::FLAGS. Having both is an error.
@@ -324,23 +338,23 @@
// An XML attribute/value pair defined in the style.
message Entry {
// Where the entry was defined.
- optional Source source = 1;
+ Source source = 1;
// Any comments associated with the entry.
- optional string comment = 2;
+ string comment = 2;
// A reference to the XML attribute.
- optional Reference key = 3;
+ Reference key = 3;
// The Item defined for this XML attribute.
- optional Item item = 4;
+ Item item = 4;
}
// The optinal style from which this style inherits attributes.
- optional Reference parent = 1;
+ Reference parent = 1;
// The source file information of the parent inheritance declaration.
- optional Source parent_source = 2;
+ Source parent_source = 2;
// The set of XML attribute/value pairs for this style.
repeated Entry entry = 3;
@@ -352,13 +366,13 @@
// An attribute defined for this styleable.
message Entry {
// Where the attribute was defined within the <declare-styleable> block.
- optional Source source = 1;
+ Source source = 1;
// Any comments associated with the declaration.
- optional string comment = 2;
+ string comment = 2;
// The reference to the attribute.
- optional Reference attr = 3;
+ Reference attr = 3;
}
// The set of attribute declarations.
@@ -370,13 +384,13 @@
// A single element of the array.
message Element {
// Where the element was defined.
- optional Source source = 1;
+ Source source = 1;
// Any comments associated with the element.
- optional string comment = 2;
+ string comment = 2;
// The value assigned to this element.
- optional Item item = 3;
+ Item item = 3;
}
// The list of array elements.
@@ -398,16 +412,16 @@
// The plural value for a given arity.
message Entry {
// Where the plural was defined.
- optional Source source = 1;
+ Source source = 1;
// Any comments associated with the plural.
- optional string comment = 2;
+ string comment = 2;
// The arity of the plural.
- optional Arity arity = 3;
+ Arity arity = 3;
// The value assigned to this plural.
- optional Item item = 4;
+ Item item = 4;
}
// The set of arity/plural mappings.
@@ -417,14 +431,13 @@
// Defines an abstract XmlNode that must be either an XmlElement, or
// a text node represented by a string.
message XmlNode {
- // If set, this node is an element/tag.
- optional XmlElement element = 1;
-
- // If set, this node is a chunk of text.
- optional string text = 2;
+ oneof node {
+ XmlElement element = 1;
+ string text = 2;
+ }
// Source line and column info.
- optional SourcePosition source = 3;
+ SourcePosition source = 3;
}
// An <element> in an XML document.
@@ -433,10 +446,10 @@
repeated XmlNamespace namespace_declaration = 1;
// The namespace URI of this element.
- optional string namespace_uri = 2;
+ string namespace_uri = 2;
// The name of this element.
- optional string name = 3;
+ string name = 3;
// The attributes of this element.
repeated XmlAttribute attribute = 4;
@@ -447,25 +460,25 @@
// A namespace declaration on an XmlElement (xmlns:android="http://...").
message XmlNamespace {
- optional string prefix = 1;
- optional string uri = 2;
+ string prefix = 1;
+ string uri = 2;
// Source line and column info.
- optional SourcePosition source = 3;
+ SourcePosition source = 3;
}
// An attribute defined on an XmlElement (android:text="...").
message XmlAttribute {
- optional string namespace_uri = 1;
- optional string name = 2;
- optional string value = 3;
+ string namespace_uri = 1;
+ string name = 2;
+ string value = 3;
// Source line and column info.
- optional SourcePosition source = 4;
+ SourcePosition source = 4;
- // The resource ID (0xPPTTEEEE) of the attribute.
- optional uint32 resource_id = 5;
+ // The optional resource ID (0xPPTTEEEE) of the attribute.
+ uint32 resource_id = 5;
- // The interpreted/compiled version of the `value` string.
- optional Item compiled_item = 6;
+ // The optional interpreted/compiled version of the `value` string.
+ Item compiled_item = 6;
}
diff --git a/tools/aapt2/ResourcesInternal.proto b/tools/aapt2/ResourcesInternal.proto
index 3117917..0b0a252 100644
--- a/tools/aapt2/ResourcesInternal.proto
+++ b/tools/aapt2/ResourcesInternal.proto
@@ -14,39 +14,40 @@
* limitations under the License.
*/
-syntax = "proto2";
+syntax = "proto3";
-option java_package = "android.aapt.pb.internal";
-option optimize_for = LITE_RUNTIME;
-
+import "frameworks/base/tools/aapt2/Configuration.proto";
import "frameworks/base/tools/aapt2/Resources.proto";
package aapt.pb.internal;
+option java_package = "android.aapt.pb.internal";
+option optimize_for = LITE_RUNTIME;
+
// The top level message representing an external resource file (layout XML, PNG, etc).
// This is used to represent a compiled file before it is linked. Only useful to aapt2.
message CompiledFile {
message Symbol {
// The name of the symbol (in the form package:type/name).
- optional string resource_name = 1;
+ string resource_name = 1;
// The position in the file at which this symbol is defined. For debug use.
- optional aapt.pb.SourcePosition source = 2;
+ aapt.pb.SourcePosition source = 2;
}
// The name of the resource (in the form package:type/name).
- optional string resource_name = 1;
+ string resource_name = 1;
// The configuration for which the resource is defined.
- optional aapt.pb.ConfigDescription config = 2;
+ aapt.pb.Configuration config = 2;
// The filesystem path to where the source file originated.
// Mainly used to display helpful error messages.
- optional string source_path = 3;
+ string source_path = 3;
// Any symbols this file auto-generates/exports (eg. @+id/foo in an XML file).
repeated Symbol exported_symbol = 4;
// If this is a compiled XML file, this is the root node.
- optional aapt.pb.XmlNode xml_root = 5;
+ aapt.pb.XmlNode xml_root = 5;
}
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index eb4fa49..4e74ec3 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -22,12 +22,11 @@
namespace aapt {
-/**
- * Visits a value and invokes the appropriate method based on its type. Does not
- * traverse into compound types. Use ValueVisitor for that.
- */
-struct RawValueVisitor {
- virtual ~RawValueVisitor() = default;
+// Visits a value and invokes the appropriate method based on its type.
+// Does not traverse into compound types. Use ValueVisitor for that.
+class ValueVisitor {
+ public:
+ virtual ~ValueVisitor() = default;
virtual void VisitAny(Value* value) {}
virtual void VisitItem(Item* value) { VisitAny(value); }
@@ -46,21 +45,67 @@
virtual void Visit(Styleable* value) { VisitAny(value); }
};
+// Const version of ValueVisitor.
+class ConstValueVisitor {
+ public:
+ virtual ~ConstValueVisitor() = default;
+
+ virtual void VisitAny(const Value* value) {
+ }
+ virtual void VisitItem(const Item* value) {
+ VisitAny(value);
+ }
+ virtual void Visit(const Reference* value) {
+ VisitItem(value);
+ }
+ virtual void Visit(const RawString* value) {
+ VisitItem(value);
+ }
+ virtual void Visit(const String* value) {
+ VisitItem(value);
+ }
+ virtual void Visit(const StyledString* value) {
+ VisitItem(value);
+ }
+ virtual void Visit(const FileReference* value) {
+ VisitItem(value);
+ }
+ virtual void Visit(const Id* value) {
+ VisitItem(value);
+ }
+ virtual void Visit(const BinaryPrimitive* value) {
+ VisitItem(value);
+ }
+
+ virtual void Visit(const Attribute* value) {
+ VisitAny(value);
+ }
+ virtual void Visit(const Style* value) {
+ VisitAny(value);
+ }
+ virtual void Visit(const Array* value) {
+ VisitAny(value);
+ }
+ virtual void Visit(const Plural* value) {
+ VisitAny(value);
+ }
+ virtual void Visit(const Styleable* value) {
+ VisitAny(value);
+ }
+};
+
// NOLINT, do not add parentheses around T.
#define DECL_VISIT_COMPOUND_VALUE(T) \
virtual void Visit(T* value) override { /* NOLINT */ \
VisitSubValues(value); \
}
-/**
- * Visits values, and if they are compound values, visits the components as
- * well.
- */
-struct ValueVisitor : public RawValueVisitor {
+// Visits values, and if they are compound values, descends into their components as well.
+struct DescendingValueVisitor : public ValueVisitor {
// The compiler will think we're hiding an overload, when we actually intend
// to call into RawValueVisitor. This will expose the visit methods in the
// super class so the compiler knows we are trying to call them.
- using RawValueVisitor::Visit;
+ using ValueVisitor::Visit;
void VisitSubValues(Attribute* attribute) {
for (Attribute::Symbol& symbol : attribute->symbols) {
@@ -106,37 +151,30 @@
DECL_VISIT_COMPOUND_VALUE(Styleable);
};
-/**
- * Do not use directly. Helper struct for dyn_cast.
- */
+// Do not use directly. Helper struct for dyn_cast.
template <typename T>
-struct DynCastVisitor : public RawValueVisitor {
- T* value = nullptr;
+struct DynCastVisitor : public ConstValueVisitor {
+ const T* value = nullptr;
- void Visit(T* v) override { value = v; }
+ void Visit(const T* v) override {
+ value = v;
+ }
};
-/**
- * Specialization that checks if the value is an Item.
- */
+// Specialization that checks if the value is an Item.
template <>
-struct DynCastVisitor<Item> : public RawValueVisitor {
- Item* value = nullptr;
+struct DynCastVisitor<Item> : public ConstValueVisitor {
+ const Item* value = nullptr;
- void VisitItem(Item* item) override { value = item; }
+ void VisitItem(const Item* item) override {
+ value = item;
+ }
};
+// Returns a valid pointer to T if the value is an instance of T. Returns nullptr if value is
+// nullptr of if value is not an instance of T.
template <typename T>
const T* ValueCast(const Value* value) {
- return ValueCast<T>(const_cast<Value*>(value));
-}
-
-/**
- * Returns a valid pointer to T if the Value is of subtype T.
- * Otherwise, returns nullptr.
- */
-template <typename T>
-T* ValueCast(Value* value) {
if (!value) {
return nullptr;
}
@@ -145,8 +183,13 @@
return visitor.value;
}
-inline void VisitAllValuesInPackage(ResourceTablePackage* pkg,
- RawValueVisitor* visitor) {
+// Non-const version of ValueCast.
+template <typename T>
+T* ValueCast(Value* value) {
+ return const_cast<T*>(ValueCast<T>(static_cast<const Value*>(value)));
+}
+
+inline void VisitAllValuesInPackage(ResourceTablePackage* pkg, ValueVisitor* visitor) {
for (auto& type : pkg->types) {
for (auto& entry : type->entries) {
for (auto& config_value : entry->values) {
@@ -156,8 +199,7 @@
}
}
-inline void VisitAllValuesInTable(ResourceTable* table,
- RawValueVisitor* visitor) {
+inline void VisitAllValuesInTable(ResourceTable* table, ValueVisitor* visitor) {
for (auto& pkg : table->packages) {
VisitAllValuesInPackage(pkg.get(), visitor);
}
diff --git a/tools/aapt2/ValueVisitor_test.cpp b/tools/aapt2/ValueVisitor_test.cpp
index eb75b10..5aea77d 100644
--- a/tools/aapt2/ValueVisitor_test.cpp
+++ b/tools/aapt2/ValueVisitor_test.cpp
@@ -24,16 +24,16 @@
namespace aapt {
-struct SingleReferenceVisitor : public ValueVisitor {
- using ValueVisitor::Visit;
+struct SingleReferenceVisitor : public DescendingValueVisitor {
+ using DescendingValueVisitor::Visit;
Reference* visited = nullptr;
void Visit(Reference* ref) override { visited = ref; }
};
-struct StyleVisitor : public ValueVisitor {
- using ValueVisitor::Visit;
+struct StyleVisitor : public DescendingValueVisitor {
+ using DescendingValueVisitor::Visit;
std::list<Reference*> visited_refs;
Style* visited_style = nullptr;
@@ -42,7 +42,7 @@
void Visit(Style* style) override {
visited_style = style;
- ValueVisitor::Visit(style);
+ DescendingValueVisitor::Visit(style);
}
};
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 7f5bbf0..a5e6aefd1 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -35,12 +35,12 @@
#include "compile/Png.h"
#include "compile/PseudolocaleGenerator.h"
#include "compile/XmlIdCollector.h"
-#include "flatten/Archive.h"
-#include "flatten/XmlFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/XmlFlattener.h"
+#include "format/proto/ProtoSerialize.h"
#include "io/BigBufferOutputStream.h"
#include "io/FileInputStream.h"
#include "io/Util.h"
-#include "proto/ProtoSerialize.h"
#include "util/Files.h"
#include "util/Maybe.h"
#include "util/Util.h"
@@ -248,8 +248,9 @@
// ZeroCopyOutputStream interface.
CopyingOutputStreamAdaptor copying_adaptor(writer);
- std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(&table);
- if (!pb_table->SerializeToZeroCopyStream(©ing_adaptor)) {
+ pb::ResourceTable pb_table;
+ SerializeTableToPb(table, &pb_table);
+ if (!pb_table.SerializeToZeroCopyStream(©ing_adaptor)) {
context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write");
return false;
}
@@ -282,9 +283,10 @@
// Number of CompiledFiles.
output_stream.WriteLittleEndian32(1);
- std::unique_ptr<pb::internal::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
- output_stream.WriteCompiledFile(compiled_file.get());
- output_stream.WriteData(&buffer);
+ pb::internal::CompiledFile pb_compiled_file;
+ SerializeCompiledFileToPb(file, &pb_compiled_file);
+ output_stream.WriteCompiledFile(pb_compiled_file);
+ output_stream.WriteData(buffer);
if (output_stream.HadError()) {
diag->Error(DiagMessage(output_path) << "failed to write data");
@@ -319,8 +321,9 @@
// Number of CompiledFiles.
output_stream.WriteLittleEndian32(1);
- std::unique_ptr<pb::internal::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
- output_stream.WriteCompiledFile(compiled_file.get());
+ pb::internal::CompiledFile pb_compiled_file;
+ SerializeCompiledFileToPb(file, &pb_compiled_file);
+ output_stream.WriteCompiledFile(pb_compiled_file);
output_stream.WriteData(map.getDataPtr(), map.getDataLength());
if (output_stream.HadError()) {
@@ -346,13 +349,13 @@
return false;
}
- std::unique_ptr<pb::internal::CompiledFile> pb_compiled_file =
- SerializeCompiledFileToPb(xmlres->file);
- out->WriteCompiledFile(pb_compiled_file.get());
- out->WriteData(&buffer);
+ pb::internal::CompiledFile pb_compiled_file;
+ SerializeCompiledFileToPb(xmlres->file, &pb_compiled_file);
+ out->WriteCompiledFile(pb_compiled_file);
+ out->WriteData(buffer);
if (out->HadError()) {
- context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write data");
+ context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write XML data");
return false;
}
return true;
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 1a6f348..625c47c 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -22,7 +22,7 @@
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
-using android::StringPiece;
+using ::android::StringPiece;
namespace aapt {
@@ -325,9 +325,9 @@
return diff;
}
-class ZeroingReferenceVisitor : public ValueVisitor {
+class ZeroingReferenceVisitor : public DescendingValueVisitor {
public:
- using ValueVisitor::Visit;
+ using DescendingValueVisitor::Visit;
void Visit(Reference* ref) override {
if (ref->name && ref->id) {
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 0965910..44032f6 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -21,10 +21,10 @@
#include "Debug.h"
#include "Diagnostics.h"
#include "Flags.h"
+#include "format/binary/BinaryResourceParser.h"
+#include "format/proto/ProtoDeserialize.h"
#include "io/ZipArchive.h"
#include "process/IResourceTableConsumer.h"
-#include "proto/ProtoSerialize.h"
-#include "unflatten/BinaryResourceParser.h"
#include "util/Files.h"
using ::android::StringPiece;
@@ -33,29 +33,28 @@
bool DumpCompiledFile(const pb::internal::CompiledFile& pb_file, const void* data, size_t len,
const Source& source, IAaptContext* context) {
- std::unique_ptr<ResourceFile> file =
- DeserializeCompiledFileFromPb(pb_file, source, context->GetDiagnostics());
- if (!file) {
- context->GetDiagnostics()->Warn(DiagMessage() << "failed to read compiled file");
+ ResourceFile file;
+ std::string error;
+ if (!DeserializeCompiledFileFromPb(pb_file, &file, &error)) {
+ context->GetDiagnostics()->Warn(DiagMessage(source)
+ << "failed to read compiled file: " << error);
return false;
}
- std::cout << "Resource: " << file->name << "\n"
- << "Config: " << file->config << "\n"
- << "Source: " << file->source << "\n";
+ std::cout << "Resource: " << file.name << "\n"
+ << "Config: " << file.config << "\n"
+ << "Source: " << file.source << "\n";
return true;
}
bool TryDumpFile(IAaptContext* context, const std::string& file_path) {
- std::unique_ptr<ResourceTable> table;
-
std::string err;
std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err);
if (zip) {
- io::IFile* file = zip->FindFile("resources.arsc.flat");
- if (file) {
+ ResourceTable table;
+ if (io::IFile* file = zip->FindFile("resources.arsc.flat")) {
std::unique_ptr<io::IData> data = file->OpenAsData();
- if (!data) {
+ if (data == nullptr) {
context->GetDiagnostics()->Error(DiagMessage(file_path)
<< "failed to open resources.arsc.flat");
return false;
@@ -67,83 +66,78 @@
return false;
}
- table = DeserializeTableFromPb(pb_table, Source(file_path), context->GetDiagnostics());
- if (!table) {
+ ResourceTable table;
+ if (!DeserializeTableFromPb(pb_table, &table, &err)) {
+ context->GetDiagnostics()->Error(DiagMessage(file_path)
+ << "failed to parse table: " << err);
+ return false;
+ }
+ } else if (io::IFile* file = zip->FindFile("resources.arsc")) {
+ std::unique_ptr<io::IData> data = file->OpenAsData();
+ if (!data) {
+ context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.arsc");
+ return false;
+ }
+
+ BinaryResourceParser parser(context, &table, Source(file_path), data->data(), data->size());
+ if (!parser.Parse()) {
return false;
}
}
- if (!table) {
- file = zip->FindFile("resources.arsc");
- if (file) {
- std::unique_ptr<io::IData> data = file->OpenAsData();
- if (!data) {
- context->GetDiagnostics()->Error(DiagMessage(file_path)
- << "failed to open resources.arsc");
- return false;
- }
+ DebugPrintTableOptions options;
+ options.show_sources = true;
+ Debug::PrintTable(&table, options);
+ return true;
+ }
- table = util::make_unique<ResourceTable>();
- BinaryResourceParser parser(context, table.get(), Source(file_path), data->data(),
- data->size());
- if (!parser.Parse()) {
- return false;
- }
- }
+ err.clear();
+
+ Maybe<android::FileMap> file = file::MmapPath(file_path, &err);
+ if (!file) {
+ context->GetDiagnostics()->Error(DiagMessage(file_path) << err);
+ return false;
+ }
+
+ android::FileMap* file_map = &file.value();
+
+ // Check to see if this is a loose ResourceTable.
+ pb::ResourceTable pb_table;
+ if (pb_table.ParseFromArray(file_map->getDataPtr(), file_map->getDataLength())) {
+ ResourceTable table;
+ if (DeserializeTableFromPb(pb_table, &table, &err)) {
+ DebugPrintTableOptions options;
+ options.show_sources = true;
+ Debug::PrintTable(&table, options);
+ return true;
}
}
- if (!table) {
- Maybe<android::FileMap> file = file::MmapPath(file_path, &err);
- if (!file) {
- context->GetDiagnostics()->Error(DiagMessage(file_path) << err);
+ // Try as a compiled file.
+ CompiledFileInputStream input(file_map->getDataPtr(), file_map->getDataLength());
+ uint32_t num_files = 0;
+ if (!input.ReadLittleEndian32(&num_files)) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < num_files; i++) {
+ pb::internal::CompiledFile compiled_file;
+ if (!input.ReadCompiledFile(&compiled_file)) {
+ context->GetDiagnostics()->Warn(DiagMessage() << "failed to read compiled file");
return false;
}
- android::FileMap* file_map = &file.value();
-
- // Try as a compiled table.
- pb::ResourceTable pb_table;
- if (pb_table.ParseFromArray(file_map->getDataPtr(), file_map->getDataLength())) {
- table = DeserializeTableFromPb(pb_table, Source(file_path), context->GetDiagnostics());
+ uint64_t offset, len;
+ if (!input.ReadDataMetaData(&offset, &len)) {
+ context->GetDiagnostics()->Warn(DiagMessage() << "failed to read meta data");
+ return false;
}
- if (!table) {
- // Try as a compiled file.
- CompiledFileInputStream input(file_map->getDataPtr(), file_map->getDataLength());
-
- uint32_t num_files = 0;
- if (!input.ReadLittleEndian32(&num_files)) {
- return false;
- }
-
- for (uint32_t i = 0; i < num_files; i++) {
- pb::internal::CompiledFile compiled_file;
- if (!input.ReadCompiledFile(&compiled_file)) {
- context->GetDiagnostics()->Warn(DiagMessage() << "failed to read compiled file");
- return false;
- }
-
- uint64_t offset, len;
- if (!input.ReadDataMetaData(&offset, &len)) {
- context->GetDiagnostics()->Warn(DiagMessage() << "failed to read meta data");
- return false;
- }
-
- const void* data = static_cast<const uint8_t*>(file_map->getDataPtr()) + offset;
- if (!DumpCompiledFile(compiled_file, data, len, Source(file_path), context)) {
- return false;
- }
- }
+ const void* data = static_cast<const uint8_t*>(file_map->getDataPtr()) + offset;
+ if (!DumpCompiledFile(compiled_file, data, len, Source(file_path), context)) {
+ return false;
}
}
-
- if (table) {
- DebugPrintTableOptions options;
- options.show_sources = true;
- Debug::PrintTable(table.get(), options);
- }
-
return true;
}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 3a2faa9..88e0f69 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -33,12 +33,17 @@
#include "Locale.h"
#include "NameMangler.h"
#include "ResourceUtils.h"
+#include "ResourceValues.h"
+#include "ValueVisitor.h"
#include "cmd/Util.h"
#include "compile/IdAssigner.h"
#include "filter/ConfigFilter.h"
-#include "flatten/Archive.h"
-#include "flatten/TableFlattener.h"
-#include "flatten/XmlFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/BinaryResourceParser.h"
+#include "format/binary/TableFlattener.h"
+#include "format/binary/XmlFlattener.h"
+#include "format/proto/ProtoDeserialize.h"
+#include "format/proto/ProtoSerialize.h"
#include "io/BigBufferInputStream.h"
#include "io/FileInputStream.h"
#include "io/FileSystem.h"
@@ -56,9 +61,7 @@
#include "optimize/VersionCollapser.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
-#include "proto/ProtoSerialize.h"
#include "split/TableSplitter.h"
-#include "unflatten/BinaryResourceParser.h"
#include "util/Files.h"
#include "xml/XmlDom.h"
@@ -279,8 +282,10 @@
return {};
}
- std::unique_ptr<ResourceTable> table = DeserializeTableFromPb(pb_table, source, diag);
- if (!table) {
+ std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
+ std::string error;
+ if (!DeserializeTableFromPb(pb_table, table.get(), &error)) {
+ diag->Error(DiagMessage(source) << "invalid compiled table: " << error);
return {};
}
return table;
@@ -915,8 +920,9 @@
}
bool FlattenTableToPb(ResourceTable* table, IArchiveWriter* writer) {
- std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(table);
- return io::CopyProtoToArchive(context_, pb_table.get(), "resources.arsc.flat", 0, writer);
+ pb::ResourceTable pb_table;
+ SerializeTableToPb(*table, &pb_table);
+ return io::CopyProtoToArchive(context_, &pb_table, "resources.arsc.flat", 0, writer);
}
bool WriteJavaFile(ResourceTable* table, const StringPiece& package_name_to_generate,
@@ -1395,14 +1401,15 @@
return false;
}
- std::unique_ptr<ResourceFile> resource_file = DeserializeCompiledFileFromPb(
- compiled_file, file->GetSource(), context_->GetDiagnostics());
- if (!resource_file) {
+ ResourceFile resource_file;
+ std::string error;
+ if (!DeserializeCompiledFileFromPb(compiled_file, &resource_file, &error)) {
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "failed to read compiled header: " << error);
return false;
}
- if (!MergeCompiledFile(file->CreateFileSegment(offset, len), resource_file.get(),
- override)) {
+ if (!MergeCompiledFile(file->CreateFileSegment(offset, len), &resource_file, override)) {
return false;
}
}
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 33a1d3a..67ac67a 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -18,6 +18,7 @@
#include <vector>
#include "android-base/stringprintf.h"
+
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
@@ -30,8 +31,8 @@
#include "cmd/Util.h"
#include "configuration/ConfigurationParser.h"
#include "filter/AbiFilter.h"
-#include "flatten/TableFlattener.h"
-#include "flatten/XmlFlattener.h"
+#include "format/binary/TableFlattener.h"
+#include "format/binary/XmlFlattener.h"
#include "io/BigBufferInputStream.h"
#include "io/Util.h"
#include "optimize/MultiApkGenerator.h"
@@ -282,24 +283,8 @@
bool ExtractAppDataFromManifest(OptimizeContext* context, LoadedApk* apk,
OptimizeOptions* out_options) {
- io::IFile* manifest_file = apk->GetFileCollection()->FindFile("AndroidManifest.xml");
- if (manifest_file == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "missing AndroidManifest.xml");
- return false;
- }
-
- std::unique_ptr<io::IData> data = manifest_file->OpenAsData();
- if (data == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage(manifest_file->GetSource())
- << "failed to open file");
- return false;
- }
-
- std::unique_ptr<xml::XmlResource> manifest = xml::Inflate(
- data->data(), data->size(), context->GetDiagnostics(), manifest_file->GetSource());
+ std::unique_ptr<xml::XmlResource> manifest = apk->InflateManifest(context);
if (manifest == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage() << "failed to read binary AndroidManifest.xml");
return false;
}
diff --git a/tools/aapt2/compile/Png.cpp b/tools/aapt2/compile/Png.cpp
index 6d6147d..33122dc 100644
--- a/tools/aapt2/compile/Png.cpp
+++ b/tools/aapt2/compile/Png.cpp
@@ -538,7 +538,7 @@
if (kDebug) {
diag->Note(DiagMessage() << "adding 9-patch info..");
}
- strcpy((char*)unknowns[pIndex].name, "npTc");
+ memcpy((char*)unknowns[pIndex].name, "npTc", 5);
unknowns[pIndex].data = (png_byte*)info->serialize9Patch();
unknowns[pIndex].size = info->info9Patch.serializedSize();
// TODO: remove the check below when everything works
@@ -546,7 +546,7 @@
// automatically generated 9 patch outline data
int chunkSize = sizeof(png_uint_32) * 6;
- strcpy((char*)unknowns[oIndex].name, "npOl");
+ memcpy((char*)unknowns[oIndex].name, "npOl", 5);
unknowns[oIndex].data = (png_byte*)calloc(chunkSize, 1);
png_byte outputData[chunkSize];
memcpy(&outputData, &info->outlineInsetsLeft, 4 * sizeof(png_uint_32));
@@ -558,7 +558,7 @@
// optional optical inset / layout bounds data
if (info->haveLayoutBounds) {
int chunkSize = sizeof(png_uint_32) * 4;
- strcpy((char*)unknowns[bIndex].name, "npLb");
+ memcpy((char*)unknowns[bIndex].name, "npLb", 5);
unknowns[bIndex].data = (png_byte*)calloc(chunkSize, 1);
memcpy(unknowns[bIndex].data, &info->layoutBoundsLeft, chunkSize);
unknowns[bIndex].size = chunkSize;
diff --git a/tools/aapt2/compile/PngChunkFilter.cpp b/tools/aapt2/compile/PngChunkFilter.cpp
index 5af91fd..bc2e699 100644
--- a/tools/aapt2/compile/PngChunkFilter.cpp
+++ b/tools/aapt2/compile/PngChunkFilter.cpp
@@ -21,8 +21,8 @@
#include "io/Io.h"
-using android::StringPiece;
-using android::base::StringPrintf;
+using ::android::StringPiece;
+using ::android::base::StringPrintf;
namespace aapt {
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index 871ed4f..36c24bc 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -24,8 +24,8 @@
#include "compile/Pseudolocalizer.h"
#include "util/Util.h"
-using android::StringPiece;
-using android::StringPiece16;
+using ::android::StringPiece;
+using ::android::StringPiece16;
namespace aapt {
@@ -215,7 +215,7 @@
namespace {
-class Visitor : public RawValueVisitor {
+class Visitor : public ValueVisitor {
public:
// Either value or item will be populated upon visiting the value.
std::unique_ptr<Value> value;
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index 9d6d328..a79a577 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -27,6 +27,7 @@
#include "ConfigDescription.h"
#include "Diagnostics.h"
+#include "ResourceUtils.h"
#include "io/File.h"
#include "io/FileSystem.h"
#include "io/StringInputStream.h"
@@ -329,15 +330,32 @@
// TODO: Validate all references in the configuration are valid. It should be safe to assume from
// this point on that any references from one section to another will be present.
+ // TODO: Automatically arrange artifacts so that they match Play Store multi-APK requirements.
+ // see: https://developer.android.com/google/play/publishing/multiple-apks.html
+ //
+ // For now, make sure the version codes are unique.
+ std::vector<Artifact>& artifacts = config.artifacts;
+ std::sort(artifacts.begin(), artifacts.end());
+ if (std::adjacent_find(artifacts.begin(), artifacts.end()) != artifacts.end()) {
+ diag_->Error(DiagMessage() << "Configuration has duplicate versions");
+ return {};
+ }
+
return {config};
}
ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ =
[](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+ // This will be incremented later so the first version will always be different to the base APK.
+ int current_version = (config->artifacts.empty()) ? 0 : config->artifacts.back().version;
+
Artifact artifact{};
+ Maybe<int> version;
for (const auto& attr : root_element->attributes) {
if (attr.name == "name") {
artifact.name = attr.value;
+ } else if (attr.name == "version") {
+ version = std::stoi(attr.value);
} else if (attr.name == "abi-group") {
artifact.abi_group = {attr.value};
} else if (attr.name == "screen-density-group") {
@@ -355,6 +373,9 @@
<< attr.value);
}
}
+
+ artifact.version = (version) ? version.value() : current_version + 1;
+
config->artifacts.push_back(artifact);
return true;
};
@@ -499,11 +520,11 @@
AndroidSdk entry;
for (const auto& attr : child->attributes) {
if (attr.name == "minSdkVersion") {
- entry.min_sdk_version = {attr.value};
+ entry.min_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
} else if (attr.name == "targetSdkVersion") {
- entry.target_sdk_version = {attr.value};
+ entry.target_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
} else if (attr.name == "maxSdkVersion") {
- entry.max_sdk_version = {attr.value};
+ entry.max_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
} else {
diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value);
}
diff --git a/tools/aapt2/configuration/ConfigurationParser.h b/tools/aapt2/configuration/ConfigurationParser.h
index 9bc9081..c5d3284 100644
--- a/tools/aapt2/configuration/ConfigurationParser.h
+++ b/tools/aapt2/configuration/ConfigurationParser.h
@@ -17,6 +17,7 @@
#ifndef AAPT2_CONFIGURATION_H
#define AAPT2_CONFIGURATION_H
+#include <set>
#include <string>
#include <unordered_map>
#include <vector>
@@ -41,6 +42,12 @@
struct Artifact {
/** Name to use for output of processing foo.apk -> foo.<name>.apk. */
Maybe<std::string> name;
+ /**
+ * Value to add to the base Android manifest versionCode. If it is not present in the
+ * configuration file, it is set to the previous artifact + 1. If the first artifact does not have
+ * a value, artifacts are a 1 based index.
+ */
+ int version;
/** If present, uses the ABI group with this name. */
Maybe<std::string> abi_group;
/** If present, uses the screen density group with this name. */
@@ -60,6 +67,15 @@
/** Convert an artifact name template into a name string based on configuration contents. */
Maybe<std::string> Name(const android::StringPiece& apk_name, IDiagnostics* diag) const;
+
+ bool operator<(const Artifact& rhs) const {
+ // TODO(safarmer): Order by play store multi-APK requirements.
+ return version < rhs.version;
+ }
+
+ bool operator==(const Artifact& rhs) const {
+ return version == rhs.version;
+ }
};
/** Enumeration of currently supported ABIs. */
@@ -103,14 +119,14 @@
};
struct AndroidSdk {
- Maybe<std::string> min_sdk_version;
- Maybe<std::string> target_sdk_version;
- Maybe<std::string> max_sdk_version;
+ Maybe<int> min_sdk_version;
+ Maybe<int> target_sdk_version;
+ Maybe<int> max_sdk_version;
Maybe<AndroidManifest> manifest;
- static AndroidSdk ForMinSdk(std::string min_sdk) {
+ static AndroidSdk ForMinSdk(int min_sdk) {
AndroidSdk sdk;
- sdk.min_sdk_version = {std::move(min_sdk)};
+ sdk.min_sdk_version = min_sdk;
return sdk;
}
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index 7ffb3d5..3654901 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -24,6 +24,15 @@
#include "xml/XmlDom.h"
namespace aapt {
+
+namespace configuration {
+void PrintTo(const AndroidSdk& sdk, std::ostream* os) {
+ *os << "SDK: min=" << sdk.min_sdk_version.value_or_default(-1)
+ << ", target=" << sdk.target_sdk_version.value_or_default(-1)
+ << ", max=" << sdk.max_sdk_version.value_or_default(-1);
+}
+} // namespace configuration
+
namespace {
using ::android::ResTable_config;
@@ -76,9 +85,9 @@
</locale-group>
<android-sdk-group label="v19">
<android-sdk
- minSdkVersion="v19"
- targetSdkVersion="v24"
- maxSdkVersion="v25">
+ minSdkVersion="19"
+ targetSdkVersion="24"
+ maxSdkVersion="25">
<manifest>
<!--- manifest additions here XSLT? TODO -->
</manifest>
@@ -156,7 +165,7 @@
EXPECT_EQ(1ul, value.android_sdk_groups.size());
EXPECT_TRUE(value.android_sdk_groups["v19"].min_sdk_version);
- EXPECT_EQ("v19", value.android_sdk_groups["v19"].min_sdk_version.value());
+ EXPECT_EQ(19, value.android_sdk_groups["v19"].min_sdk_version.value());
EXPECT_EQ(1ul, value.gl_texture_groups.size());
EXPECT_EQ(1ul, value.gl_texture_groups["dxt1"].size());
@@ -174,55 +183,117 @@
}
TEST_F(ConfigurationParserTest, ArtifactAction) {
- static constexpr const char* xml = R"xml(
+ PostProcessingConfiguration config;
+ {
+ const auto doc = test::BuildXmlDom(R"xml(
+ <artifact
+ abi-group="arm"
+ screen-density-group="large"
+ locale-group="europe"
+ android-sdk-group="v19"
+ gl-texture-group="dxt1"
+ device-feature-group="low-latency"/>)xml");
+
+ ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc->root.get()), &diag_));
+
+ EXPECT_EQ(1ul, config.artifacts.size());
+
+ auto& artifact = config.artifacts.back();
+ EXPECT_FALSE(artifact.name); // TODO: make this fail.
+ EXPECT_EQ(1, artifact.version);
+ EXPECT_EQ("arm", artifact.abi_group.value());
+ EXPECT_EQ("large", artifact.screen_density_group.value());
+ EXPECT_EQ("europe", artifact.locale_group.value());
+ EXPECT_EQ("v19", artifact.android_sdk_group.value());
+ EXPECT_EQ("dxt1", artifact.gl_texture_group.value());
+ EXPECT_EQ("low-latency", artifact.device_feature_group.value());
+ }
+
+ {
+ // Perform a second action to ensure we get 2 artifacts.
+ const auto doc = test::BuildXmlDom(R"xml(
+ <artifact
+ abi-group="other"
+ screen-density-group="large"
+ locale-group="europe"
+ android-sdk-group="v19"
+ gl-texture-group="dxt1"
+ device-feature-group="low-latency"/>)xml");
+
+ ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_));
+ EXPECT_EQ(2ul, config.artifacts.size());
+ EXPECT_EQ(2, config.artifacts.back().version);
+ }
+
+ {
+ // Perform a third action with a set version code.
+ const auto doc = test::BuildXmlDom(R"xml(
<artifact
- abi-group="arm"
+ version="5"
+ abi-group="other"
screen-density-group="large"
locale-group="europe"
android-sdk-group="v19"
gl-texture-group="dxt1"
- device-feature-group="low-latency"/>)xml";
+ device-feature-group="low-latency"/>)xml");
- auto doc = test::BuildXmlDom(xml);
+ ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_));
+ EXPECT_EQ(3ul, config.artifacts.size());
+ EXPECT_EQ(5, config.artifacts.back().version);
+ }
- PostProcessingConfiguration config;
- bool ok = artifact_handler_(&config, NodeCast<Element>(doc->root.get()), &diag_);
- ASSERT_TRUE(ok);
-
- EXPECT_EQ(1ul, config.artifacts.size());
-
- auto& artifact = config.artifacts.front();
- EXPECT_FALSE(artifact.name); // TODO: make this fail.
- EXPECT_EQ("arm", artifact.abi_group.value());
- EXPECT_EQ("large", artifact.screen_density_group.value());
- EXPECT_EQ("europe", artifact.locale_group.value());
- EXPECT_EQ("v19", artifact.android_sdk_group.value());
- EXPECT_EQ("dxt1", artifact.gl_texture_group.value());
- EXPECT_EQ("low-latency", artifact.device_feature_group.value());
-
- // Perform a second action to ensure we get 2 artifacts.
- static constexpr const char* second = R"xml(
+ {
+ // Perform a fourth action to ensure the version code still increments.
+ const auto doc = test::BuildXmlDom(R"xml(
<artifact
abi-group="other"
screen-density-group="large"
locale-group="europe"
android-sdk-group="v19"
gl-texture-group="dxt1"
- device-feature-group="low-latency"/>)xml";
- doc = test::BuildXmlDom(second);
+ device-feature-group="low-latency"/>)xml");
- ok = artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
- ASSERT_TRUE(ok);
- EXPECT_EQ(2ul, config.artifacts.size());
+ ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_));
+ EXPECT_EQ(4ul, config.artifacts.size());
+ EXPECT_EQ(6, config.artifacts.back().version);
+ }
+}
+
+TEST_F(ConfigurationParserTest, DuplicateArtifactVersion) {
+ static constexpr const char* configuration = R"xml(<?xml version="1.0" encoding="utf-8" ?>
+ <pst-process xmlns="http://schemas.android.com/tools/aapt">>
+ <artifacts>
+ <artifact-format>
+ ${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release
+ </artifact-format>
+ <artifact
+ name="art1"
+ abi-group="arm"
+ screen-density-group="large"
+ locale-group="europe"
+ android-sdk-group="v19"
+ gl-texture-group="dxt1"
+ device-feature-group="low-latency"/>
+ <artifact
+ name="art2"
+ version = "1"
+ abi-group="other"
+ screen-density-group="alldpi"
+ locale-group="north-america"
+ android-sdk-group="v19"
+ gl-texture-group="dxt1"
+ device-feature-group="low-latency"/>
+ </artifacts>
+ </post-process>)xml";
+ auto result = ConfigurationParser::ForContents(configuration).Parse();
+ ASSERT_FALSE(result);
}
TEST_F(ConfigurationParserTest, ArtifactFormatAction) {
- static constexpr const char* xml = R"xml(
+ const auto doc = test::BuildXmlDom(R"xml(
<artifact-format>
${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release
- </artifact-format>)xml";
-
- auto doc = test::BuildXmlDom(xml);
+ </artifact-format>)xml");
PostProcessingConfiguration config;
bool ok = artifact_format_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
@@ -321,9 +392,9 @@
static constexpr const char* xml = R"xml(
<android-sdk-group label="v19">
<android-sdk
- minSdkVersion="v19"
- targetSdkVersion="v24"
- maxSdkVersion="v25">
+ minSdkVersion="19"
+ targetSdkVersion="24"
+ maxSdkVersion="25">
<manifest>
<!--- manifest additions here XSLT? TODO -->
</manifest>
@@ -342,14 +413,43 @@
auto& out = config.android_sdk_groups["v19"];
AndroidSdk sdk;
- sdk.min_sdk_version = std::string("v19");
- sdk.target_sdk_version = std::string("v24");
- sdk.max_sdk_version = std::string("v25");
+ sdk.min_sdk_version = 19;
+ sdk.target_sdk_version = 24;
+ sdk.max_sdk_version = 25;
sdk.manifest = AndroidManifest();
ASSERT_EQ(sdk, out);
}
+TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) {
+ static constexpr const char* xml = R"xml(
+ <android-sdk-group label="O">
+ <android-sdk
+ minSdkVersion="M"
+ targetSdkVersion="O"
+ maxSdkVersion="O">
+ </android-sdk>
+ </android-sdk-group>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = android_sdk_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_TRUE(ok);
+
+ ASSERT_EQ(1ul, config.android_sdk_groups.size());
+ ASSERT_EQ(1u, config.android_sdk_groups.count("O"));
+
+ auto& out = config.android_sdk_groups["O"];
+
+ AndroidSdk sdk;
+ sdk.min_sdk_version = {}; // Only the latest development version is supported.
+ sdk.target_sdk_version = 26;
+ sdk.max_sdk_version = 26;
+
+ ASSERT_EQ(sdk, out);
+}
+
TEST_F(ConfigurationParserTest, GlTextureGroupAction) {
static constexpr const char* xml = R"xml(
<gl-texture-group label="dxt1">
diff --git a/tools/aapt2/configuration/aapt2.xsd b/tools/aapt2/configuration/aapt2.xsd
index 47bf99e..134153a 100644
--- a/tools/aapt2/configuration/aapt2.xsd
+++ b/tools/aapt2/configuration/aapt2.xsd
@@ -39,6 +39,7 @@
<!-- Groups output artifacts together by dimension labels. -->
<xsd:complexType name="artifact">
<xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="version" type="xsd:integer"/>
<xsd:attribute name="abi-group" type="xsd:string"/>
<xsd:attribute name="android-sdk-group" type="xsd:string"/>
<xsd:attribute name="device-feature-group" type="xsd:string"/>
diff --git a/tools/aapt2/flatten/Archive.cpp b/tools/aapt2/format/Archive.cpp
similarity index 95%
rename from tools/aapt2/flatten/Archive.cpp
rename to tools/aapt2/format/Archive.cpp
index 5f8bd06..d152a9c 100644
--- a/tools/aapt2/flatten/Archive.cpp
+++ b/tools/aapt2/format/Archive.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "flatten/Archive.h"
+#include "format/Archive.h"
#include <cstdio>
#include <memory>
@@ -106,9 +106,13 @@
return !in->HadError();
}
- bool HadError() const override { return !error_.empty(); }
+ bool HadError() const override {
+ return !error_.empty();
+ }
- std::string GetError() const override { return error_; }
+ std::string GetError() const override {
+ return error_;
+ }
private:
DISALLOW_COPY_AND_ASSIGN(DirectoryWriter);
@@ -221,9 +225,13 @@
}
}
- bool HadError() const override { return !error_.empty(); }
+ bool HadError() const override {
+ return !error_.empty();
+ }
- std::string GetError() const override { return error_; }
+ std::string GetError() const override {
+ return error_;
+ }
virtual ~ZipFileWriter() {
if (writer_) {
diff --git a/tools/aapt2/flatten/Archive.h b/tools/aapt2/format/Archive.h
similarity index 96%
rename from tools/aapt2/flatten/Archive.h
rename to tools/aapt2/format/Archive.h
index 4ee4ce7..4e8a39d 100644
--- a/tools/aapt2/flatten/Archive.h
+++ b/tools/aapt2/format/Archive.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAPT_FLATTEN_ARCHIVE_H
-#define AAPT_FLATTEN_ARCHIVE_H
+#ifndef AAPT_FORMAT_ARCHIVE_H
+#define AAPT_FORMAT_ARCHIVE_H
#include <fstream>
#include <memory>
@@ -78,4 +78,4 @@
} // namespace aapt
-#endif /* AAPT_FLATTEN_ARCHIVE_H */
+#endif /* AAPT_FORMAT_ARCHIVE_H */
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
similarity index 75%
rename from tools/aapt2/unflatten/BinaryResourceParser.cpp
rename to tools/aapt2/format/binary/BinaryResourceParser.cpp
index 892aee6..95eec4a 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "unflatten/BinaryResourceParser.h"
+#include "format/binary/BinaryResourceParser.h"
#include <algorithm>
#include <map>
@@ -31,22 +31,22 @@
#include "ResourceValues.h"
#include "Source.h"
#include "ValueVisitor.h"
-#include "unflatten/ResChunkPullParser.h"
+#include "format/binary/ResChunkPullParser.h"
#include "util/Util.h"
-namespace aapt {
-
using namespace android;
using ::android::base::StringPrintf;
+namespace aapt {
+
namespace {
// Visitor that converts a reference's resource ID to a resource name, given a mapping from
// resource ID to resource name.
-class ReferenceIdToNameVisitor : public ValueVisitor {
+class ReferenceIdToNameVisitor : public DescendingValueVisitor {
public:
- using ValueVisitor::Visit;
+ using DescendingValueVisitor::Visit;
explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>* mapping)
: mapping_(mapping) {
@@ -118,15 +118,11 @@
return true;
}
-/**
- * Parses the resource table, which contains all the packages, types, and
- * entries.
- */
+// Parses the resource table, which contains all the packages, types, and entries.
bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) {
const ResTable_header* table_header = ConvertTo<ResTable_header>(chunk);
if (!table_header) {
- context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "corrupt ResTable_header chunk");
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_header chunk");
return false;
}
@@ -136,21 +132,20 @@
switch (util::DeviceToHost16(parser.chunk()->type)) {
case android::RES_STRING_POOL_TYPE:
if (value_pool_.getError() == NO_INIT) {
- status_t err = value_pool_.setTo(
- parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
+ status_t err =
+ value_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
if (err != NO_ERROR) {
- context_->GetDiagnostics()->Error(
- DiagMessage(source_) << "corrupt string pool in ResTable: "
- << value_pool_.getError());
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "corrupt string pool in ResTable: "
+ << value_pool_.getError());
return false;
}
// Reserve some space for the strings we are going to add.
- table_->string_pool.HintWillAdd(value_pool_.size(),
- value_pool_.styleCount());
+ table_->string_pool.HintWillAdd(value_pool_.size(), value_pool_.styleCount());
} else {
- context_->GetDiagnostics()->Warn(
- DiagMessage(source_) << "unexpected string pool in ResTable");
+ context_->GetDiagnostics()->Warn(DiagMessage(source_)
+ << "unexpected string pool in ResTable");
}
break;
@@ -169,8 +164,8 @@
}
if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
- context_->GetDiagnostics()->Error(
- DiagMessage(source_) << "corrupt resource table: " << parser.error());
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "corrupt resource table: " << parser.error());
return false;
}
return true;
@@ -187,26 +182,25 @@
uint32_t package_id = util::DeviceToHost32(package_header->id);
if (package_id > std::numeric_limits<uint8_t>::max()) {
- context_->GetDiagnostics()->Error(
- DiagMessage(source_) << "package ID is too big (" << package_id << ")");
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "package ID is too big (" << package_id << ")");
return false;
}
// Extract the package name.
- size_t len = strnlen16((const char16_t*)package_header->name,
- arraysize(package_header->name));
+ size_t len = strnlen16((const char16_t*)package_header->name, arraysize(package_header->name));
std::u16string package_name;
package_name.resize(len);
for (size_t i = 0; i < len; i++) {
package_name[i] = util::DeviceToHost16(package_header->name[i]);
}
- ResourceTablePackage* package = table_->CreatePackage(
- util::Utf16ToUtf8(package_name), static_cast<uint8_t>(package_id));
+ ResourceTablePackage* package =
+ table_->CreatePackage(util::Utf16ToUtf8(package_name), static_cast<uint8_t>(package_id));
if (!package) {
- context_->GetDiagnostics()->Error(
- DiagMessage(source_) << "incompatible package '" << package_name
- << "' with ID " << package_id);
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "incompatible package '" << package_name << "' with ID "
+ << package_id);
return false;
}
@@ -221,23 +215,21 @@
switch (util::DeviceToHost16(parser.chunk()->type)) {
case android::RES_STRING_POOL_TYPE:
if (type_pool_.getError() == NO_INIT) {
- status_t err = type_pool_.setTo(
- parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
+ status_t err =
+ type_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
if (err != NO_ERROR) {
context_->GetDiagnostics()->Error(DiagMessage(source_)
<< "corrupt type string pool in "
- << "ResTable_package: "
- << type_pool_.getError());
+ << "ResTable_package: " << type_pool_.getError());
return false;
}
} else if (key_pool_.getError() == NO_INIT) {
- status_t err = key_pool_.setTo(
- parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
+ status_t err =
+ key_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
if (err != NO_ERROR) {
context_->GetDiagnostics()->Error(DiagMessage(source_)
<< "corrupt key string pool in "
- << "ResTable_package: "
- << key_pool_.getError());
+ << "ResTable_package: " << key_pool_.getError());
return false;
}
} else {
@@ -272,8 +264,8 @@
}
if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
- context_->GetDiagnostics()->Error(
- DiagMessage(source_) << "corrupt ResTable_package: " << parser.error());
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "corrupt ResTable_package: " << parser.error());
return false;
}
@@ -286,22 +278,19 @@
bool BinaryResourceParser::ParseTypeSpec(const ResChunk_header* chunk) {
if (type_pool_.getError() != NO_ERROR) {
- context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "missing type string pool");
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing type string pool");
return false;
}
const ResTable_typeSpec* type_spec = ConvertTo<ResTable_typeSpec>(chunk);
if (!type_spec) {
- context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "corrupt ResTable_typeSpec chunk");
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_typeSpec chunk");
return false;
}
if (type_spec->id == 0) {
context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "ResTable_typeSpec has invalid id: "
- << type_spec->id);
+ << "ResTable_typeSpec has invalid id: " << type_spec->id);
return false;
}
return true;
@@ -310,14 +299,12 @@
bool BinaryResourceParser::ParseType(const ResourceTablePackage* package,
const ResChunk_header* chunk) {
if (type_pool_.getError() != NO_ERROR) {
- context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "missing type string pool");
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing type string pool");
return false;
}
if (key_pool_.getError() != NO_ERROR) {
- context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "missing key string pool");
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing key string pool");
return false;
}
@@ -325,15 +312,13 @@
// a lot and has its own code to handle variable size.
const ResTable_type* type = ConvertTo<ResTable_type, kResTableTypeMinSize>(chunk);
if (!type) {
- context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "corrupt ResTable_type chunk");
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_type chunk");
return false;
}
if (type->id == 0) {
context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "ResTable_type has invalid id: "
- << (int)type->id);
+ << "ResTable_type has invalid id: " << (int)type->id);
return false;
}
@@ -344,9 +329,9 @@
const ResourceType* parsed_type = ParseResourceType(type_str);
if (!parsed_type) {
- context_->GetDiagnostics()->Error(
- DiagMessage(source_) << "invalid type name '" << type_str
- << "' for type with ID " << (int)type->id);
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "invalid type name '" << type_str << "' for type with ID "
+ << (int)type->id);
return false;
}
@@ -357,12 +342,10 @@
continue;
}
- const ResourceName name(
- package->name, *parsed_type,
- util::GetString(key_pool_, util::DeviceToHost32(entry->key.index)));
+ const ResourceName name(package->name, *parsed_type,
+ util::GetString(key_pool_, util::DeviceToHost32(entry->key.index)));
- const ResourceId res_id(package->id.value(), type->id,
- static_cast<uint16_t>(it.index()));
+ const ResourceId res_id(package->id.value(), type->id, static_cast<uint16_t>(it.index()));
std::unique_ptr<Value> resource_value;
if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
@@ -377,10 +360,9 @@
}
if (!resource_value) {
- context_->GetDiagnostics()->Error(
- DiagMessage(source_) << "failed to parse value for resource " << name
- << " (" << res_id << ") with configuration '"
- << config << "'");
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to parse value for resource " << name << " ("
+ << res_id << ") with configuration '" << config << "'");
return false;
}
@@ -433,19 +415,19 @@
if (file_ref != nullptr) {
file_ref->file = files_->FindFile(*file_ref->path);
if (file_ref->file == nullptr) {
- context_->GetDiagnostics()->Warn(DiagMessage() << "resource " << name << " for config '"
- << config << "' is a file reference to '"
- << *file_ref->path
- << "' but no such path exists");
+ context_->GetDiagnostics()->Warn(DiagMessage()
+ << "resource " << name << " for config '" << config
+ << "' is a file reference to '" << *file_ref->path
+ << "' but no such path exists");
}
}
}
return item;
}
-std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(
- const ResourceNameRef& name, const ConfigDescription& config,
- const ResTable_map_entry* map) {
+std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
switch (name.type) {
case ResourceType::kStyle:
return ParseStyle(name, config, map);
@@ -470,9 +452,9 @@
return {};
}
-std::unique_ptr<Style> BinaryResourceParser::ParseStyle(
- const ResourceNameRef& name, const ConfigDescription& config,
- const ResTable_map_entry* map) {
+std::unique_ptr<Style> BinaryResourceParser::ParseStyle(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
std::unique_ptr<Style> style = util::make_unique<Style>();
if (util::DeviceToHost32(map->parent.ident) != 0) {
// The parent is a regular reference to a resource.
@@ -495,19 +477,16 @@
return style;
}
-std::unique_ptr<Attribute> BinaryResourceParser::ParseAttr(
- const ResourceNameRef& name, const ConfigDescription& config,
- const ResTable_map_entry* map) {
- const bool is_weak =
- (util::DeviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0;
+std::unique_ptr<Attribute> BinaryResourceParser::ParseAttr(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ const bool is_weak = (util::DeviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0;
std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(is_weak);
// First we must discover what type of attribute this is. Find the type mask.
- auto type_mask_iter =
- std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
- return util::DeviceToHost32(entry.name.ident) ==
- ResTable_map::ATTR_TYPE;
- });
+ auto type_mask_iter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
+ return util::DeviceToHost32(entry.name.ident) == ResTable_map::ATTR_TYPE;
+ });
if (type_mask_iter != end(map)) {
attr->type_mask = util::DeviceToHost32(type_mask_iter->value.data);
@@ -526,8 +505,7 @@
continue;
}
- if (attr->type_mask &
- (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
+ if (attr->type_mask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
Attribute::Symbol symbol;
symbol.value = util::DeviceToHost32(map_entry.value.data);
symbol.symbol = Reference(util::DeviceToHost32(map_entry.name.ident));
@@ -539,9 +517,9 @@
return attr;
}
-std::unique_ptr<Array> BinaryResourceParser::ParseArray(
- const ResourceNameRef& name, const ConfigDescription& config,
- const ResTable_map_entry* map) {
+std::unique_ptr<Array> BinaryResourceParser::ParseArray(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
std::unique_ptr<Array> array = util::make_unique<Array>();
for (const ResTable_map& map_entry : map) {
array->elements.push_back(ParseValue(name, config, map_entry.value));
@@ -549,9 +527,9 @@
return array;
}
-std::unique_ptr<Plural> BinaryResourceParser::ParsePlural(
- const ResourceNameRef& name, const ConfigDescription& config,
- const ResTable_map_entry* map) {
+std::unique_ptr<Plural> BinaryResourceParser::ParsePlural(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const ResTable_map_entry* map) {
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
for (const ResTable_map& map_entry : map) {
std::unique_ptr<Item> item = ParseValue(name, config, map_entry.value);
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h
similarity index 73%
rename from tools/aapt2/unflatten/BinaryResourceParser.h
rename to tools/aapt2/format/binary/BinaryResourceParser.h
index c41ada0..dc9a384 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.h
+++ b/tools/aapt2/format/binary/BinaryResourceParser.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAPT_BINARY_RESOURCE_PARSER_H
-#define AAPT_BINARY_RESOURCE_PARSER_H
+#ifndef AAPT_FORMAT_BINARY_RESOURCEPARSER_H
+#define AAPT_FORMAT_BINARY_RESOURCEPARSER_H
#include <string>
@@ -32,25 +32,17 @@
struct SymbolTable_entry;
-/*
- * Parses a binary resource table (resources.arsc) and adds the entries
- * to a ResourceTable. This is different than the libandroidfw ResTable
- * in that it scans the table from top to bottom and doesn't require
- * support for random access. It is also able to parse non-runtime
- * chunks and types.
- */
+// Parses a binary resource table (resources.arsc) and adds the entries to a ResourceTable.
+// This is different than the libandroidfw ResTable in that it scans the table from top to bottom
+// and doesn't require support for random access.
class BinaryResourceParser {
public:
- /*
- * Creates a parser, which will read `len` bytes from `data`, and
- * add any resources parsed to `table`. `source` is for logging purposes.
- */
+ // Creates a parser, which will read `len` bytes from `data`, and add any resources parsed to
+ // `table`. `source` is for logging purposes.
BinaryResourceParser(IAaptContext* context, ResourceTable* table, const Source& source,
const void* data, size_t data_len, io::IFileCollection* files = nullptr);
- /*
- * Parses the binary resource table and returns true if successful.
- */
+ // Parses the binary resource table and returns true if successful.
bool Parse();
private:
@@ -59,31 +51,25 @@
bool ParseTable(const android::ResChunk_header* chunk);
bool ParsePackage(const android::ResChunk_header* chunk);
bool ParseTypeSpec(const android::ResChunk_header* chunk);
- bool ParseType(const ResourceTablePackage* package,
- const android::ResChunk_header* chunk);
+ bool ParseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
bool ParseLibrary(const android::ResChunk_header* chunk);
std::unique_ptr<Item> ParseValue(const ResourceNameRef& name, const ConfigDescription& config,
const android::Res_value& value);
- std::unique_ptr<Value> ParseMapEntry(const ResourceNameRef& name,
- const ConfigDescription& config,
+ std::unique_ptr<Value> ParseMapEntry(const ResourceNameRef& name, const ConfigDescription& config,
const android::ResTable_map_entry* map);
- std::unique_ptr<Style> ParseStyle(const ResourceNameRef& name,
- const ConfigDescription& config,
+ std::unique_ptr<Style> ParseStyle(const ResourceNameRef& name, const ConfigDescription& config,
const android::ResTable_map_entry* map);
- std::unique_ptr<Attribute> ParseAttr(const ResourceNameRef& name,
- const ConfigDescription& config,
+ std::unique_ptr<Attribute> ParseAttr(const ResourceNameRef& name, const ConfigDescription& config,
const android::ResTable_map_entry* map);
- std::unique_ptr<Array> ParseArray(const ResourceNameRef& name,
- const ConfigDescription& config,
+ std::unique_ptr<Array> ParseArray(const ResourceNameRef& name, const ConfigDescription& config,
const android::ResTable_map_entry* map);
- std::unique_ptr<Plural> ParsePlural(const ResourceNameRef& name,
- const ConfigDescription& config,
+ std::unique_ptr<Plural> ParsePlural(const ResourceNameRef& name, const ConfigDescription& config,
const android::ResTable_map_entry* map);
/**
@@ -125,13 +111,10 @@
namespace android {
-/**
- * Iterator functionality for ResTable_map_entry.
- */
+// Iterator functionality for ResTable_map_entry.
inline const ResTable_map* begin(const ResTable_map_entry* map) {
- return (const ResTable_map*)((const uint8_t*)map +
- aapt::util::DeviceToHost32(map->size));
+ return (const ResTable_map*)((const uint8_t*)map + ::aapt::util::DeviceToHost32(map->size));
}
inline const ResTable_map* end(const ResTable_map_entry* map) {
@@ -140,4 +123,4 @@
} // namespace android
-#endif // AAPT_BINARY_RESOURCE_PARSER_H
+#endif // AAPT_FORMAT_BINARY_RESOURCEPARSER_H
diff --git a/tools/aapt2/flatten/ChunkWriter.h b/tools/aapt2/format/binary/ChunkWriter.h
similarity index 85%
rename from tools/aapt2/flatten/ChunkWriter.h
rename to tools/aapt2/format/binary/ChunkWriter.h
index 968d3ee..1892a29 100644
--- a/tools/aapt2/flatten/ChunkWriter.h
+++ b/tools/aapt2/format/binary/ChunkWriter.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAPT_FLATTEN_CHUNKWRITER_H
-#define AAPT_FLATTEN_CHUNKWRITER_H
+#ifndef AAPT_FORMAT_BINARY_CHUNKWRITER_H
+#define AAPT_FORMAT_BINARY_CHUNKWRITER_H
#include "android-base/macros.h"
#include "androidfw/ResourceTypes.h"
@@ -27,7 +27,8 @@
class ChunkWriter {
public:
- explicit inline ChunkWriter(BigBuffer* buffer) : buffer_(buffer) {}
+ explicit inline ChunkWriter(BigBuffer* buffer) : buffer_(buffer) {
+ }
ChunkWriter(ChunkWriter&&) = default;
ChunkWriter& operator=(ChunkWriter&&) = default;
@@ -46,11 +47,17 @@
return buffer_->NextBlock<T>(count);
}
- inline BigBuffer* buffer() { return buffer_; }
+ inline BigBuffer* buffer() {
+ return buffer_;
+ }
- inline android::ResChunk_header* chunk_header() { return header_; }
+ inline android::ResChunk_header* chunk_header() {
+ return header_;
+ }
- inline size_t size() { return buffer_->size() - start_size_; }
+ inline size_t size() {
+ return buffer_->size() - start_size_;
+ }
inline android::ResChunk_header* Finish() {
buffer_->Align4();
@@ -77,4 +84,4 @@
} // namespace aapt
-#endif /* AAPT_FLATTEN_CHUNKWRITER_H */
+#endif /* AAPT_FORMAT_BINARY_CHUNKWRITER_H */
diff --git a/tools/aapt2/unflatten/ResChunkPullParser.cpp b/tools/aapt2/format/binary/ResChunkPullParser.cpp
similarity index 92%
rename from tools/aapt2/unflatten/ResChunkPullParser.cpp
rename to tools/aapt2/format/binary/ResChunkPullParser.cpp
index 8d92bd9..fd6919d 100644
--- a/tools/aapt2/unflatten/ResChunkPullParser.cpp
+++ b/tools/aapt2/format/binary/ResChunkPullParser.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "unflatten/ResChunkPullParser.h"
+#include "format/binary/ResChunkPullParser.h"
#include <inttypes.h>
#include <cstddef>
@@ -44,9 +44,8 @@
if (event_ == Event::kStartDocument) {
current_chunk_ = data_;
} else {
- current_chunk_ =
- (const ResChunk_header*)(((const char*)current_chunk_) +
- util::DeviceToHost32(current_chunk_->size));
+ current_chunk_ = (const ResChunk_header*)(((const char*)current_chunk_) +
+ util::DeviceToHost32(current_chunk_->size));
}
const std::ptrdiff_t diff = (const char*)current_chunk_ - (const char*)data_;
diff --git a/tools/aapt2/unflatten/ResChunkPullParser.h b/tools/aapt2/format/binary/ResChunkPullParser.h
similarity index 64%
rename from tools/aapt2/unflatten/ResChunkPullParser.h
rename to tools/aapt2/format/binary/ResChunkPullParser.h
index 5827753..5ff1359 100644
--- a/tools/aapt2/unflatten/ResChunkPullParser.h
+++ b/tools/aapt2/format/binary/ResChunkPullParser.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAPT_RES_CHUNK_PULL_PARSER_H
-#define AAPT_RES_CHUNK_PULL_PARSER_H
+#ifndef AAPT_FORMAT_BINARY_RESCHUNKPULLPARSER_H
+#define AAPT_FORMAT_BINARY_RESCHUNKPULLPARSER_H
#include <string>
@@ -26,18 +26,13 @@
namespace aapt {
-/**
- * A pull parser, modeled after XmlPullParser, that reads
- * android::ResChunk_header structs from a block of data.
- *
- * An android::ResChunk_header specifies a type, headerSize,
- * and size. The pull parser will verify that the chunk's size
- * doesn't extend beyond the available data, and will iterate
- * over each chunk in the given block of data.
- *
- * Processing nested chunks is done by creating a new ResChunkPullParser
- * pointing to the data portion of a chunk.
- */
+// A pull parser, modeled after XmlPullParser, that reads android::ResChunk_header structs from a
+// block of data.
+// An android::ResChunk_header specifies a type, headerSize, and size. The pull parser will verify
+// that the chunk's size doesn't extend beyond the available data, and will iterate over each chunk
+// in the given block of data.
+// Processing nested chunks is done by creating a new ResChunkPullParser pointing to the data
+// portion of a chunk.
class ResChunkPullParser {
public:
enum class Event {
@@ -48,24 +43,18 @@
kChunk,
};
- /**
- * Returns false if the event is EndDocument or BadDocument.
- */
+ // Returns false if the event is EndDocument or BadDocument.
static bool IsGoodEvent(Event event);
- /**
- * Create a ResChunkPullParser to read android::ResChunk_headers
- * from the memory pointed to by data, of len bytes.
- */
+ // Create a ResChunkPullParser to read android::ResChunk_headers from the memory pointed to by
+ // data, of len bytes.
ResChunkPullParser(const void* data, size_t len);
Event event() const;
const std::string& error() const;
const android::ResChunk_header* chunk() const;
- /**
- * Move to the next android::ResChunk_header.
- */
+ // Move to the next android::ResChunk_header.
Event Next();
private:
@@ -86,15 +75,12 @@
return reinterpret_cast<const T*>(chunk);
}
-inline static const uint8_t* GetChunkData(
- const android::ResChunk_header* chunk) {
- return reinterpret_cast<const uint8_t*>(chunk) +
- util::DeviceToHost16(chunk->headerSize);
+inline static const uint8_t* GetChunkData(const android::ResChunk_header* chunk) {
+ return reinterpret_cast<const uint8_t*>(chunk) + util::DeviceToHost16(chunk->headerSize);
}
inline static uint32_t GetChunkDataLen(const android::ResChunk_header* chunk) {
- return util::DeviceToHost32(chunk->size) -
- util::DeviceToHost16(chunk->headerSize);
+ return util::DeviceToHost32(chunk->size) - util::DeviceToHost16(chunk->headerSize);
}
//
@@ -109,13 +95,16 @@
: event_(Event::kStartDocument),
data_(reinterpret_cast<const android::ResChunk_header*>(data)),
len_(len),
- current_chunk_(nullptr) {}
+ current_chunk_(nullptr) {
+}
inline ResChunkPullParser::Event ResChunkPullParser::event() const {
return event_;
}
-inline const std::string& ResChunkPullParser::error() const { return error_; }
+inline const std::string& ResChunkPullParser::error() const {
+ return error_;
+}
inline const android::ResChunk_header* ResChunkPullParser::chunk() const {
return current_chunk_;
@@ -123,4 +112,4 @@
} // namespace aapt
-#endif // AAPT_RES_CHUNK_PULL_PARSER_H
+#endif // AAPT_FORMAT_BINARY_RESCHUNKPULLPARSER_H
diff --git a/tools/aapt2/flatten/ResourceTypeExtensions.h b/tools/aapt2/format/binary/ResourceTypeExtensions.h
similarity index 75%
rename from tools/aapt2/flatten/ResourceTypeExtensions.h
rename to tools/aapt2/format/binary/ResourceTypeExtensions.h
index 6359b41..7f58df80 100644
--- a/tools/aapt2/flatten/ResourceTypeExtensions.h
+++ b/tools/aapt2/format/binary/ResourceTypeExtensions.h
@@ -14,18 +14,14 @@
* limitations under the License.
*/
-#ifndef AAPT_RESOURCE_TYPE_EXTENSIONS_H
-#define AAPT_RESOURCE_TYPE_EXTENSIONS_H
+#ifndef AAPT_FORMAT_BINARY_RESOURCETYPEEXTENSIONS_H
+#define AAPT_FORMAT_BINARY_RESOURCETYPEEXTENSIONS_H
#include "androidfw/ResourceTypes.h"
namespace aapt {
-/**
- * An alternative struct to use instead of ResTable_map_entry. This one is a
- * standard_layout
- * struct.
- */
+// An alternative struct to use instead of ResTable_map_entry. This one is a standard_layout struct.
struct ResTable_entry_ext {
android::ResTable_entry entry;
android::ResTable_ref parent;
@@ -34,4 +30,4 @@
} // namespace aapt
-#endif // AAPT_RESOURCE_TYPE_EXTENSIONS_H
+#endif // AAPT_FORMAT_BINARY_RESOURCETYPEEXTENSIONS_H
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
similarity index 92%
rename from tools/aapt2/flatten/TableFlattener.cpp
rename to tools/aapt2/format/binary/TableFlattener.cpp
index 14b776b..57565a5 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "flatten/TableFlattener.h"
+#include "format/binary/TableFlattener.h"
#include <algorithm>
#include <numeric>
@@ -28,8 +28,8 @@
#include "ResourceValues.h"
#include "SdkConstants.h"
#include "ValueVisitor.h"
-#include "flatten/ChunkWriter.h"
-#include "flatten/ResourceTypeExtensions.h"
+#include "format/binary/ChunkWriter.h"
+#include "format/binary/ResourceTypeExtensions.h"
#include "util/BigBuffer.h"
using namespace android;
@@ -76,12 +76,13 @@
uint32_t entry_key;
};
-class MapFlattenVisitor : public RawValueVisitor {
+class MapFlattenVisitor : public ValueVisitor {
public:
- using RawValueVisitor::Visit;
+ using ValueVisitor::Visit;
MapFlattenVisitor(ResTable_entry_ext* out_entry, BigBuffer* buffer)
- : out_entry_(out_entry), buffer_(buffer) {}
+ : out_entry_(out_entry), buffer_(buffer) {
+ }
void Visit(Attribute* attr) override {
{
@@ -92,15 +93,13 @@
if (attr->min_int != std::numeric_limits<int32_t>::min()) {
Reference key = Reference(ResourceId(ResTable_map::ATTR_MIN));
- BinaryPrimitive val(Res_value::TYPE_INT_DEC,
- static_cast<uint32_t>(attr->min_int));
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->min_int));
FlattenEntry(&key, &val);
}
if (attr->max_int != std::numeric_limits<int32_t>::max()) {
Reference key = Reference(ResourceId(ResTable_map::ATTR_MAX));
- BinaryPrimitive val(Res_value::TYPE_INT_DEC,
- static_cast<uint32_t>(attr->max_int));
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->max_int));
FlattenEntry(&key, &val);
}
@@ -188,7 +187,9 @@
* Call this after visiting a Value. This will finish any work that
* needs to be done to prepare the entry.
*/
- void Finish() { out_entry_->count = util::HostToDevice32(entry_count_); }
+ void Finish() {
+ out_entry_->count = util::HostToDevice32(entry_count_);
+ }
private:
DISALLOW_COPY_AND_ASSIGN(MapFlattenVisitor);
@@ -223,7 +224,8 @@
diag_(context->GetDiagnostics()),
package_(package),
shared_libs_(shared_libs),
- use_sparse_entries_(use_sparse_entries) {}
+ use_sparse_entries_(use_sparse_entries) {
+ }
bool FlattenPackage(BigBuffer* buffer) {
ChunkWriter pkg_writer(buffer);
@@ -271,9 +273,9 @@
template <typename T, bool IsItem>
T* WriteEntry(FlatEntry* entry, BigBuffer* buffer) {
- static_assert(std::is_same<ResTable_entry, T>::value ||
- std::is_same<ResTable_entry_ext, T>::value,
- "T must be ResTable_entry or ResTable_entry_ext");
+ static_assert(
+ std::is_same<ResTable_entry, T>::value || std::is_same<ResTable_entry_ext, T>::value,
+ "T must be ResTable_entry or ResTable_entry_ext");
T* result = buffer->NextBlock<T>();
ResTable_entry* out_entry = (ResTable_entry*)result;
@@ -302,8 +304,7 @@
CHECK(item->Flatten(outValue)) << "flatten failed";
outValue->size = util::HostToDevice16(sizeof(*outValue));
} else {
- ResTable_entry_ext* out_entry =
- WriteEntry<ResTable_entry_ext, false>(entry, buffer);
+ ResTable_entry_ext* out_entry = WriteEntry<ResTable_entry_ext, false>(entry, buffer);
MapFlattenVisitor visitor(out_entry, buffer);
entry->value->Accept(&visitor);
visitor.Finish();
@@ -318,8 +319,7 @@
CHECK(num_total_entries <= std::numeric_limits<uint16_t>::max());
ChunkWriter type_writer(buffer);
- ResTable_type* type_header =
- type_writer.StartChunk<ResTable_type>(RES_TABLE_TYPE_TYPE);
+ ResTable_type* type_header = type_writer.StartChunk<ResTable_type>(RES_TABLE_TYPE_TYPE);
type_header->id = type->id.value();
type_header->config = config;
type_header->config.swapHtoD();
@@ -395,8 +395,7 @@
sorted_types.push_back(type.get());
}
- std::sort(sorted_types.begin(), sorted_types.end(),
- cmp_ids<ResourceTableType>);
+ std::sort(sorted_types.begin(), sorted_types.end(), cmp_ids<ResourceTableType>);
return sorted_types;
}
@@ -411,13 +410,11 @@
return sorted_entries;
}
- bool FlattenTypeSpec(ResourceTableType* type,
- std::vector<ResourceEntry*>* sorted_entries,
+ bool FlattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sorted_entries,
BigBuffer* buffer) {
ChunkWriter type_spec_writer(buffer);
ResTable_typeSpec* spec_header =
- type_spec_writer.StartChunk<ResTable_typeSpec>(
- RES_TABLE_TYPE_SPEC_TYPE);
+ type_spec_writer.StartChunk<ResTable_typeSpec>(RES_TABLE_TYPE_SPEC_TYPE);
spec_header->id = type->id.value();
if (sorted_entries->empty()) {
@@ -443,8 +440,7 @@
// Populate the config masks for this entry.
if (entry->symbol_status.state == SymbolState::kPublic) {
- config_masks[entry->id.value()] |=
- util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
+ config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
}
const size_t config_count = entry->values.size();
diff --git a/tools/aapt2/flatten/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h
similarity index 89%
rename from tools/aapt2/flatten/TableFlattener.h
rename to tools/aapt2/format/binary/TableFlattener.h
index 223aef8..88cbddf 100644
--- a/tools/aapt2/flatten/TableFlattener.h
+++ b/tools/aapt2/format/binary/TableFlattener.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAPT_FLATTEN_TABLEFLATTENER_H
-#define AAPT_FLATTEN_TABLEFLATTENER_H
+#ifndef AAPT_FORMAT_BINARY_TABLEFLATTENER_H
+#define AAPT_FORMAT_BINARY_TABLEFLATTENER_H
#include "android-base/macros.h"
@@ -40,7 +40,8 @@
class TableFlattener : public IResourceTableConsumer {
public:
explicit TableFlattener(const TableFlattenerOptions& options, BigBuffer* buffer)
- : options_(options), buffer_(buffer) {}
+ : options_(options), buffer_(buffer) {
+ }
bool Consume(IAaptContext* context, ResourceTable* table) override;
@@ -53,4 +54,4 @@
} // namespace aapt
-#endif /* AAPT_FLATTEN_TABLEFLATTENER_H */
+#endif /* AAPT_FORMAT_BINARY_TABLEFLATTENER_H */
diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
similarity index 83%
rename from tools/aapt2/flatten/TableFlattener_test.cpp
rename to tools/aapt2/format/binary/TableFlattener_test.cpp
index 4fdb2ec..6d75973 100644
--- a/tools/aapt2/flatten/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-#include "flatten/TableFlattener.h"
+#include "format/binary/TableFlattener.h"
#include "android-base/stringprintf.h"
#include "ResourceUtils.h"
#include "SdkConstants.h"
+#include "format/binary/BinaryResourceParser.h"
#include "test/Test.h"
-#include "unflatten/BinaryResourceParser.h"
#include "util/Util.h"
using namespace android;
@@ -34,10 +34,8 @@
class TableFlattenerTest : public ::testing::Test {
public:
void SetUp() override {
- context_ = test::ContextBuilder()
- .SetCompilationPackage("com.app.test")
- .SetPackageId(0x7f)
- .Build();
+ context_ =
+ test::ContextBuilder().SetCompilationPackage("com.app.test").SetPackageId(0x7f).Build();
}
::testing::AssertionResult Flatten(IAaptContext* context, const TableFlattenerOptions& options,
@@ -80,12 +78,10 @@
return ::testing::AssertionSuccess();
}
- ::testing::AssertionResult Exists(ResTable* table,
- const StringPiece& expected_name,
+ ::testing::AssertionResult Exists(ResTable* table, const StringPiece& expected_name,
const ResourceId& expected_id,
const ConfigDescription& expected_config,
- const uint8_t expected_data_type,
- const uint32_t expected_data,
+ const uint8_t expected_data_type, const uint32_t expected_data,
const uint32_t expected_spec_flags) {
const ResourceName expected_res_name = test::ParseNameOrDie(expected_name);
@@ -94,28 +90,26 @@
ResTable_config config;
Res_value val;
uint32_t spec_flags;
- if (table->getResource(expected_id.id, &val, false, 0, &spec_flags,
- &config) < 0) {
+ if (table->getResource(expected_id.id, &val, false, 0, &spec_flags, &config) < 0) {
return ::testing::AssertionFailure() << "could not find resource with";
}
if (expected_data_type != val.dataType) {
return ::testing::AssertionFailure()
<< "expected data type " << std::hex << (int)expected_data_type
- << " but got data type " << (int)val.dataType << std::dec
- << " instead";
+ << " but got data type " << (int)val.dataType << std::dec << " instead";
}
if (expected_data != val.data) {
return ::testing::AssertionFailure()
- << "expected data " << std::hex << expected_data
- << " but got data " << val.data << std::dec << " instead";
+ << "expected data " << std::hex << expected_data << " but got data " << val.data
+ << std::dec << " instead";
}
if (expected_spec_flags != spec_flags) {
return ::testing::AssertionFailure()
- << "expected specFlags " << std::hex << expected_spec_flags
- << " but got specFlags " << spec_flags << std::dec << " instead";
+ << "expected specFlags " << std::hex << expected_spec_flags << " but got specFlags "
+ << spec_flags << std::dec << " instead";
}
ResTable::resource_name actual_name;
@@ -127,16 +121,14 @@
if (!resName) {
return ::testing::AssertionFailure()
<< "expected name '" << expected_res_name << "' but got '"
- << StringPiece16(actual_name.package, actual_name.packageLen)
- << ":" << StringPiece16(actual_name.type, actual_name.typeLen)
- << "/" << StringPiece16(actual_name.name, actual_name.nameLen)
- << "'";
+ << StringPiece16(actual_name.package, actual_name.packageLen) << ":"
+ << StringPiece16(actual_name.type, actual_name.typeLen) << "/"
+ << StringPiece16(actual_name.name, actual_name.nameLen) << "'";
}
if (expected_config != config) {
- return ::testing::AssertionFailure() << "expected config '"
- << expected_config << "' but got '"
- << ConfigDescription(config) << "'";
+ return ::testing::AssertionFailure() << "expected config '" << expected_config
+ << "' but got '" << ConfigDescription(config) << "'";
}
return ::testing::AssertionSuccess();
}
@@ -152,57 +144,46 @@
.AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
.AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
.AddValue("com.app.test:id/three", ResourceId(0x7f020002),
- test::BuildReference("com.app.test:id/one",
- ResourceId(0x7f020000)))
+ test::BuildReference("com.app.test:id/one", ResourceId(0x7f020000)))
.AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
- util::make_unique<BinaryPrimitive>(
- uint8_t(Res_value::TYPE_INT_DEC), 1u))
+ util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
.AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
ResourceId(0x7f030000),
- util::make_unique<BinaryPrimitive>(
- uint8_t(Res_value::TYPE_INT_DEC), 2u))
+ util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
.AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
- .AddString("com.app.test:layout/bar", ResourceId(0x7f050000),
- "res/layout/bar.xml")
+ .AddString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
.Build();
ResTable res_table;
ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020000),
- {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020000), {},
+ Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/two", ResourceId(0x7f020001),
- {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/two", ResourceId(0x7f020001), {},
+ Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three",
- ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE,
- 0x7f020000u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020002), {},
+ Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one",
- ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
- ResTable_config::CONFIG_VERSION));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one", ResourceId(0x7f030000), {},
+ Res_value::TYPE_INT_DEC, 1u, ResTable_config::CONFIG_VERSION));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one",
- ResourceId(0x7f030000), test::ParseConfigOrDie("v1"),
- Res_value::TYPE_INT_DEC, 2u,
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one", ResourceId(0x7f030000),
+ test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 2u,
ResTable_config::CONFIG_VERSION));
std::u16string foo_str = u"foo";
- ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(),
- foo_str.size());
+ ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
ASSERT_GE(idx, 0);
- EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test",
- ResourceId(0x7f040000), {}, Res_value::TYPE_STRING,
- (uint32_t)idx, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {},
+ Res_value::TYPE_STRING, (uint32_t)idx, 0u));
std::u16string bar_path = u"res/layout/bar.xml";
- idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(),
- bar_path.size());
+ idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
ASSERT_GE(idx, 0);
- EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/bar",
- ResourceId(0x7f050000), {}, Res_value::TYPE_STRING,
- (uint32_t)idx, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/bar", ResourceId(0x7f050000), {},
+ Res_value::TYPE_STRING, (uint32_t)idx, 0u));
}
TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) {
@@ -216,11 +197,10 @@
ResTable res_table;
ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020001),
- {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three",
- ResourceId(0x7f020003), {}, Res_value::TYPE_INT_BOOLEAN,
- 0u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020001), {},
+ Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020003), {},
+ Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
}
TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) {
@@ -231,8 +211,7 @@
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("android", 0x01)
- .AddValue("android:attr/foo", ResourceId(0x01010000),
- util::make_unique<Attribute>(attr))
+ .AddValue("android:attr/foo", ResourceId(0x01010000), util::make_unique<Attribute>(attr))
.Build();
ResourceTable result;
@@ -308,9 +287,12 @@
ASSERT_THAT(value, NotNull());
EXPECT_EQ(0u, value->value.data);
- ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1", sparse_config), IsNull());
+ ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1",
+ sparse_config),
+ IsNull());
- value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4", sparse_config);
+ value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4",
+ sparse_config);
ASSERT_THAT(value, NotNull());
EXPECT_EQ(4u, value->value.data);
}
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/format/binary/XmlFlattener.cpp
similarity index 93%
rename from tools/aapt2/flatten/XmlFlattener.cpp
rename to tools/aapt2/format/binary/XmlFlattener.cpp
index b3b308a..f8f09ab 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "flatten/XmlFlattener.h"
+#include "format/binary/XmlFlattener.h"
#include <algorithm>
#include <map>
@@ -26,8 +26,8 @@
#include "utils/misc.h"
#include "SdkConstants.h"
-#include "flatten/ChunkWriter.h"
-#include "flatten/ResourceTypeExtensions.h"
+#include "format/binary/ChunkWriter.h"
+#include "format/binary/ResourceTypeExtensions.h"
#include "xml/XmlDom.h"
using namespace android;
@@ -71,7 +71,8 @@
std::vector<StringFlattenDest> string_refs;
XmlFlattenerVisitor(BigBuffer* buffer, XmlFlattenerOptions options)
- : buffer_(buffer), options_(options) {}
+ : buffer_(buffer), options_(options) {
+ }
void Visit(xml::Text* node) override {
if (util::TrimWhitespace(node->text).empty()) {
@@ -152,15 +153,14 @@
private:
DISALLOW_COPY_AND_ASSIGN(XmlFlattenerVisitor);
- void AddString(const StringPiece& str, uint32_t priority,
- android::ResStringPool_ref* dest,
+ void AddString(const StringPiece& str, uint32_t priority, android::ResStringPool_ref* dest,
bool treat_empty_string_as_null = false) {
if (str.empty() && treat_empty_string_as_null) {
// Some parts of the runtime treat null differently than empty string.
dest->index = util::DeviceToHost32(-1);
} else {
- string_refs.push_back(StringFlattenDest{
- pool.MakeRef(str, StringPool::Context(priority)), dest});
+ string_refs.push_back(
+ StringFlattenDest{pool.MakeRef(str, StringPool::Context(priority)), dest});
}
}
@@ -208,8 +208,7 @@
uint16_t attribute_index = 1;
for (const xml::Attribute* xml_attr : filtered_attrs_) {
// Assign the indices for specific attributes.
- if (xml_attr->compiled_attribute &&
- xml_attr->compiled_attribute.value().id &&
+ if (xml_attr->compiled_attribute && xml_attr->compiled_attribute.value().id &&
xml_attr->compiled_attribute.value().id.value() == kIdAttr) {
flat_elem->idIndex = util::HostToDevice16(attribute_index);
} else if (xml_attr->namespace_uri.empty()) {
@@ -241,9 +240,8 @@
// Lookup the StringPool for this package and make the reference there.
const xml::AaptAttribute& aapt_attr = xml_attr->compiled_attribute.value();
- StringPool::Ref name_ref =
- package_pools[aapt_attr.id.value().package_id()].MakeRef(
- xml_attr->name, StringPool::Context(aapt_attr.id.value().id));
+ StringPool::Ref name_ref = package_pools[aapt_attr.id.value().package_id()].MakeRef(
+ xml_attr->name, StringPool::Context(aapt_attr.id.value().id));
// Add it to the list of strings to flatten.
AddString(name_ref, &flat_attr->name);
@@ -272,7 +270,7 @@
flat_attr->typedValue.dataType = android::Res_value::TYPE_STRING;
AddString(str_builder.ToString(), kLowPriority,
- (ResStringPool_ref*) &flat_attr->typedValue.data);
+ (ResStringPool_ref*)&flat_attr->typedValue.data);
}
flat_attr->typedValue.size = util::HostToDevice16(sizeof(flat_attr->typedValue));
diff --git a/tools/aapt2/flatten/XmlFlattener.h b/tools/aapt2/format/binary/XmlFlattener.h
similarity index 83%
rename from tools/aapt2/flatten/XmlFlattener.h
rename to tools/aapt2/format/binary/XmlFlattener.h
index 87557f2..6a48835 100644
--- a/tools/aapt2/flatten/XmlFlattener.h
+++ b/tools/aapt2/format/binary/XmlFlattener.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAPT_FLATTEN_XMLFLATTENER_H
-#define AAPT_FLATTEN_XMLFLATTENER_H
+#ifndef AAPT_FORMAT_BINARY_XMLFLATTENER_H
+#define AAPT_FORMAT_BINARY_XMLFLATTENER_H
#include "android-base/macros.h"
@@ -26,16 +26,15 @@
namespace aapt {
struct XmlFlattenerOptions {
- /**
- * Keep attribute raw string values along with typed values.
- */
+ // Keep attribute raw string values along with typed values.
bool keep_raw_values = false;
};
class XmlFlattener : public IXmlResourceConsumer {
public:
XmlFlattener(BigBuffer* buffer, XmlFlattenerOptions options)
- : buffer_(buffer), options_(options) {}
+ : buffer_(buffer), options_(options) {
+ }
bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
@@ -50,4 +49,4 @@
} // namespace aapt
-#endif /* AAPT_FLATTEN_XMLFLATTENER_H */
+#endif /* AAPT_FORMAT_BINARY_XMLFLATTENER_H */
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/format/binary/XmlFlattener_test.cpp
similarity index 98%
rename from tools/aapt2/flatten/XmlFlattener_test.cpp
rename to tools/aapt2/format/binary/XmlFlattener_test.cpp
index a57e317..0450f6c 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "flatten/XmlFlattener.h"
+#include "format/binary/XmlFlattener.h"
#include "androidfw/ResourceTypes.h"
@@ -55,8 +55,7 @@
.Build();
}
- ::testing::AssertionResult Flatten(xml::XmlResource* doc,
- android::ResXMLTree* out_tree,
+ ::testing::AssertionResult Flatten(xml::XmlResource* doc, android::ResXMLTree* out_tree,
const XmlFlattenerOptions& options = {}) {
using namespace android; // For NO_ERROR on windows because it is a macro.
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
new file mode 100644
index 0000000..c14f09a
--- /dev/null
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -0,0 +1,918 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "format/proto/ProtoDeserialize.h"
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+
+#include "Locale.h"
+#include "ResourceTable.h"
+#include "ResourceUtils.h"
+#include "ValueVisitor.h"
+
+using ::android::ResStringPool;
+using ::google::protobuf::io::CodedInputStream;
+
+namespace aapt {
+
+namespace {
+
+class ReferenceIdToNameVisitor : public DescendingValueVisitor {
+ public:
+ using DescendingValueVisitor::Visit;
+
+ explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping)
+ : mapping_(mapping) {
+ CHECK(mapping_ != nullptr);
+ }
+
+ void Visit(Reference* reference) override {
+ if (!reference->id || !reference->id.value().is_valid()) {
+ return;
+ }
+
+ ResourceId id = reference->id.value();
+ auto cache_iter = mapping_->find(id);
+ if (cache_iter != mapping_->end()) {
+ reference->name = cache_iter->second.ToResourceName();
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReferenceIdToNameVisitor);
+
+ const std::map<ResourceId, ResourceNameRef>* mapping_;
+};
+
+} // namespace
+
+bool DeserializeConfigFromPb(const pb::Configuration& pb_config, ConfigDescription* out_config,
+ std::string* out_error) {
+ out_config->mcc = static_cast<uint16_t>(pb_config.mcc());
+ out_config->mnc = static_cast<uint16_t>(pb_config.mnc());
+
+ if (!pb_config.locale().empty()) {
+ LocaleValue lv;
+ if (!lv.InitFromBcp47Tag(pb_config.locale())) {
+ std::ostringstream error;
+ error << "configuration has invalid locale '" << pb_config.locale() << "'";
+ *out_error = error.str();
+ return false;
+ }
+ lv.WriteTo(out_config);
+ }
+
+ switch (pb_config.layout_direction()) {
+ case pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_LTR:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_LAYOUTDIR) |
+ ConfigDescription::LAYOUTDIR_LTR;
+ break;
+
+ case pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_RTL:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_LAYOUTDIR) |
+ ConfigDescription::LAYOUTDIR_RTL;
+ break;
+
+ default:
+ break;
+ }
+
+ out_config->smallestScreenWidthDp = static_cast<uint16_t>(pb_config.smallest_screen_width_dp());
+ out_config->screenWidthDp = static_cast<uint16_t>(pb_config.screen_width_dp());
+ out_config->screenHeightDp = static_cast<uint16_t>(pb_config.screen_height_dp());
+
+ switch (pb_config.screen_layout_size()) {
+ case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_SMALL:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) |
+ ConfigDescription::SCREENSIZE_SMALL;
+ break;
+
+ case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_NORMAL:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) |
+ ConfigDescription::SCREENSIZE_NORMAL;
+ break;
+
+ case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_LARGE:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) |
+ ConfigDescription::SCREENSIZE_LARGE;
+ break;
+
+ case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_XLARGE:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) |
+ ConfigDescription::SCREENSIZE_XLARGE;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.screen_layout_long()) {
+ case pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_LONG:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENLONG) |
+ ConfigDescription::SCREENLONG_YES;
+ break;
+
+ case pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_NOTLONG:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENLONG) |
+ ConfigDescription::SCREENLONG_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.screen_round()) {
+ case pb::Configuration_ScreenRound_SCREEN_ROUND_ROUND:
+ out_config->screenLayout2 =
+ (out_config->screenLayout2 & ~ConfigDescription::MASK_SCREENROUND) |
+ ConfigDescription::SCREENROUND_YES;
+ break;
+
+ case pb::Configuration_ScreenRound_SCREEN_ROUND_NOTROUND:
+ out_config->screenLayout2 =
+ (out_config->screenLayout2 & ~ConfigDescription::MASK_SCREENROUND) |
+ ConfigDescription::SCREENROUND_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.wide_color_gamut()) {
+ case pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_WIDECG:
+ out_config->colorMode = (out_config->colorMode & ~ConfigDescription::MASK_WIDE_COLOR_GAMUT) |
+ ConfigDescription::WIDE_COLOR_GAMUT_YES;
+ break;
+
+ case pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_NOWIDECG:
+ out_config->colorMode = (out_config->colorMode & ~ConfigDescription::MASK_WIDE_COLOR_GAMUT) |
+ ConfigDescription::WIDE_COLOR_GAMUT_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.hdr()) {
+ case pb::Configuration_Hdr_HDR_HIGHDR:
+ out_config->colorMode =
+ (out_config->colorMode & ~ConfigDescription::MASK_HDR) | ConfigDescription::HDR_YES;
+ break;
+
+ case pb::Configuration_Hdr_HDR_LOWDR:
+ out_config->colorMode =
+ (out_config->colorMode & ~ConfigDescription::MASK_HDR) | ConfigDescription::HDR_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.orientation()) {
+ case pb::Configuration_Orientation_ORIENTATION_PORT:
+ out_config->orientation = ConfigDescription::ORIENTATION_PORT;
+ break;
+
+ case pb::Configuration_Orientation_ORIENTATION_LAND:
+ out_config->orientation = ConfigDescription::ORIENTATION_LAND;
+ break;
+
+ case pb::Configuration_Orientation_ORIENTATION_SQUARE:
+ out_config->orientation = ConfigDescription::ORIENTATION_SQUARE;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.ui_mode_type()) {
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_NORMAL:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_NORMAL;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_DESK:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_DESK;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_CAR:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_CAR;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_TELEVISION:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_TELEVISION;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_APPLIANCE:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_APPLIANCE;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_WATCH:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_WATCH;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_VRHEADSET:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_VR_HEADSET;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.ui_mode_night()) {
+ case pb::Configuration_UiModeNight_UI_MODE_NIGHT_NIGHT:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_NIGHT) |
+ ConfigDescription::UI_MODE_NIGHT_YES;
+ break;
+
+ case pb::Configuration_UiModeNight_UI_MODE_NIGHT_NOTNIGHT:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_NIGHT) |
+ ConfigDescription::UI_MODE_NIGHT_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ out_config->density = static_cast<uint16_t>(pb_config.density());
+
+ switch (pb_config.touchscreen()) {
+ case pb::Configuration_Touchscreen_TOUCHSCREEN_NOTOUCH:
+ out_config->touchscreen = ConfigDescription::TOUCHSCREEN_NOTOUCH;
+ break;
+
+ case pb::Configuration_Touchscreen_TOUCHSCREEN_STYLUS:
+ out_config->touchscreen = ConfigDescription::TOUCHSCREEN_STYLUS;
+ break;
+
+ case pb::Configuration_Touchscreen_TOUCHSCREEN_FINGER:
+ out_config->touchscreen = ConfigDescription::TOUCHSCREEN_FINGER;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.keys_hidden()) {
+ case pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSEXPOSED:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_KEYSHIDDEN) |
+ ConfigDescription::KEYSHIDDEN_NO;
+ break;
+
+ case pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSHIDDEN:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_KEYSHIDDEN) |
+ ConfigDescription::KEYSHIDDEN_YES;
+ break;
+
+ case pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSSOFT:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_KEYSHIDDEN) |
+ ConfigDescription::KEYSHIDDEN_SOFT;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.keyboard()) {
+ case pb::Configuration_Keyboard_KEYBOARD_NOKEYS:
+ out_config->keyboard = ConfigDescription::KEYBOARD_NOKEYS;
+ break;
+
+ case pb::Configuration_Keyboard_KEYBOARD_QWERTY:
+ out_config->keyboard = ConfigDescription::KEYBOARD_QWERTY;
+ break;
+
+ case pb::Configuration_Keyboard_KEYBOARD_TWELVEKEY:
+ out_config->keyboard = ConfigDescription::KEYBOARD_12KEY;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.nav_hidden()) {
+ case pb::Configuration_NavHidden_NAV_HIDDEN_NAVEXPOSED:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_NAVHIDDEN) |
+ ConfigDescription::NAVHIDDEN_NO;
+ break;
+
+ case pb::Configuration_NavHidden_NAV_HIDDEN_NAVHIDDEN:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_NAVHIDDEN) |
+ ConfigDescription::NAVHIDDEN_YES;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.navigation()) {
+ case pb::Configuration_Navigation_NAVIGATION_NONAV:
+ out_config->navigation = ConfigDescription::NAVIGATION_NONAV;
+ break;
+
+ case pb::Configuration_Navigation_NAVIGATION_DPAD:
+ out_config->navigation = ConfigDescription::NAVIGATION_DPAD;
+ break;
+
+ case pb::Configuration_Navigation_NAVIGATION_TRACKBALL:
+ out_config->navigation = ConfigDescription::NAVIGATION_TRACKBALL;
+ break;
+
+ case pb::Configuration_Navigation_NAVIGATION_WHEEL:
+ out_config->navigation = ConfigDescription::NAVIGATION_WHEEL;
+ break;
+
+ default:
+ break;
+ }
+
+ out_config->screenWidth = static_cast<uint16_t>(pb_config.screen_width());
+ out_config->screenHeight = static_cast<uint16_t>(pb_config.screen_height());
+ out_config->sdkVersion = static_cast<uint16_t>(pb_config.sdk_version());
+ return true;
+}
+
+static void DeserializeSourceFromPb(const pb::Source& pb_source, const ResStringPool& src_pool,
+ Source* out_source) {
+ out_source->path = util::GetString(src_pool, pb_source.path_idx());
+ out_source->line = static_cast<size_t>(pb_source.position().line_number());
+}
+
+static SymbolState DeserializeVisibilityFromPb(const pb::SymbolStatus_Visibility& pb_visibility) {
+ switch (pb_visibility) {
+ case pb::SymbolStatus_Visibility_PRIVATE:
+ return SymbolState::kPrivate;
+ case pb::SymbolStatus_Visibility_PUBLIC:
+ return SymbolState::kPublic;
+ default:
+ break;
+ }
+ return SymbolState::kUndefined;
+}
+
+static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStringPool& src_pool,
+ ResourceTable* out_table, std::string* out_error) {
+ Maybe<uint8_t> id;
+ if (pb_package.has_package_id()) {
+ id = static_cast<uint8_t>(pb_package.package_id().id());
+ }
+
+ std::map<ResourceId, ResourceNameRef> id_index;
+
+ ResourceTablePackage* pkg = out_table->CreatePackage(pb_package.package_name(), id);
+ for (const pb::Type& pb_type : pb_package.type()) {
+ const ResourceType* res_type = ParseResourceType(pb_type.name());
+ if (res_type == nullptr) {
+ std::ostringstream error;
+ error << "unknown type '" << pb_type.name() << "'";
+ *out_error = error.str();
+ return false;
+ }
+
+ ResourceTableType* type = pkg->FindOrCreateType(*res_type);
+ for (const pb::Entry& pb_entry : pb_type.entry()) {
+ ResourceEntry* entry = type->FindOrCreateEntry(pb_entry.name());
+
+ // Deserialize the symbol status (public/private with source and comments).
+ if (pb_entry.has_symbol_status()) {
+ const pb::SymbolStatus& pb_status = pb_entry.symbol_status();
+ if (pb_status.has_source()) {
+ DeserializeSourceFromPb(pb_status.source(), src_pool, &entry->symbol_status.source);
+ }
+
+ entry->symbol_status.comment = pb_status.comment();
+ entry->symbol_status.allow_new = pb_status.allow_new();
+
+ const SymbolState visibility = DeserializeVisibilityFromPb(pb_status.visibility());
+ entry->symbol_status.state = visibility;
+
+ if (visibility == SymbolState::kPublic) {
+ // This is a public symbol, we must encode the ID now if there is one.
+ if (pb_entry.has_entry_id()) {
+ entry->id = static_cast<uint16_t>(pb_entry.entry_id().id());
+ }
+
+ if (type->symbol_status.state != SymbolState::kPublic) {
+ // If the type has not been made public, do so now.
+ type->symbol_status.state = SymbolState::kPublic;
+ if (pb_type.has_type_id()) {
+ type->id = static_cast<uint8_t>(pb_type.type_id().id());
+ }
+ }
+ } else if (visibility == SymbolState::kPrivate) {
+ if (type->symbol_status.state == SymbolState::kUndefined) {
+ type->symbol_status.state = SymbolState::kPrivate;
+ }
+ }
+ }
+
+ ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
+ pb_entry.entry_id().id());
+ if (resid.is_valid()) {
+ id_index[resid] = ResourceNameRef(pkg->name, type->type, entry->name);
+ }
+
+ for (const pb::ConfigValue& pb_config_value : pb_entry.config_value()) {
+ const pb::Configuration& pb_config = pb_config_value.config();
+
+ ConfigDescription config;
+ if (!DeserializeConfigFromPb(pb_config, &config, out_error)) {
+ return false;
+ }
+
+ ResourceConfigValue* config_value = entry->FindOrCreateValue(config, pb_config.product());
+ if (config_value->value != nullptr) {
+ *out_error = "duplicate configuration in resource table";
+ return false;
+ }
+
+ config_value->value = DeserializeValueFromPb(pb_config_value.value(), src_pool, config,
+ &out_table->string_pool, out_error);
+ if (config_value->value == nullptr) {
+ return false;
+ }
+ }
+ }
+ }
+
+ ReferenceIdToNameVisitor visitor(&id_index);
+ VisitAllValuesInPackage(pkg, &visitor);
+ return true;
+}
+
+bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, ResourceTable* out_table,
+ std::string* out_error) {
+ // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
+ // causes errors when qualifying it with android::
+ using namespace android;
+
+ ResStringPool source_pool;
+ if (pb_table.has_source_pool()) {
+ status_t result = source_pool.setTo(pb_table.source_pool().data().data(),
+ pb_table.source_pool().data().size());
+ if (result != NO_ERROR) {
+ *out_error = "invalid source pool";
+ return false;
+ }
+ }
+
+ for (const pb::Package& pb_package : pb_table.package()) {
+ if (!DeserializePackageFromPb(pb_package, source_pool, out_table, out_error)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DeserializeCompiledFileFromPb(const pb::internal::CompiledFile& pb_file,
+ ResourceFile* out_file, std::string* out_error) {
+ ResourceNameRef name_ref;
+ if (!ResourceUtils::ParseResourceName(pb_file.resource_name(), &name_ref)) {
+ std::ostringstream error;
+ error << "invalid resource name in compiled file header: " << pb_file.resource_name();
+ *out_error = error.str();
+ return false;
+ }
+
+ out_file->name = name_ref.ToResourceName();
+ out_file->source.path = pb_file.source_path();
+
+ std::string config_error;
+ if (!DeserializeConfigFromPb(pb_file.config(), &out_file->config, &config_error)) {
+ std::ostringstream error;
+ error << "invalid resource configuration in compiled file header: " << config_error;
+ *out_error = error.str();
+ return false;
+ }
+
+ for (const pb::internal::CompiledFile_Symbol& pb_symbol : pb_file.exported_symbol()) {
+ if (!ResourceUtils::ParseResourceName(pb_symbol.resource_name(), &name_ref)) {
+ std::ostringstream error;
+ error << "invalid resource name for exported symbol in compiled file header: "
+ << pb_file.resource_name();
+ *out_error = error.str();
+ return false;
+ }
+
+ size_t line = 0u;
+ if (pb_symbol.has_source()) {
+ line = pb_symbol.source().line_number();
+ }
+ out_file->exported_symbols.push_back(SourcedResourceName{name_ref.ToResourceName(), line});
+ }
+ return true;
+}
+
+static Reference::Type DeserializeReferenceTypeFromPb(const pb::Reference_Type& pb_type) {
+ switch (pb_type) {
+ case pb::Reference_Type_REFERENCE:
+ return Reference::Type::kResource;
+ case pb::Reference_Type_ATTRIBUTE:
+ return Reference::Type::kAttribute;
+ default:
+ break;
+ }
+ return Reference::Type::kResource;
+}
+
+static bool DeserializeReferenceFromPb(const pb::Reference& pb_ref, Reference* out_ref,
+ std::string* out_error) {
+ out_ref->reference_type = DeserializeReferenceTypeFromPb(pb_ref.type());
+ out_ref->private_reference = pb_ref.private_();
+
+ if (pb_ref.id() != 0) {
+ out_ref->id = ResourceId(pb_ref.id());
+ }
+
+ if (!pb_ref.name().empty()) {
+ ResourceNameRef name_ref;
+ if (!ResourceUtils::ParseResourceName(pb_ref.name(), &name_ref, nullptr)) {
+ std::ostringstream error;
+ error << "reference has invalid resource name '" << pb_ref.name() << "'";
+ *out_error = error.str();
+ return false;
+ }
+ out_ref->name = name_ref.ToResourceName();
+ }
+ return true;
+}
+
+template <typename T>
+static void DeserializeItemMetaDataFromPb(const T& pb_item, const android::ResStringPool& src_pool,
+ Value* out_value) {
+ if (pb_item.has_source()) {
+ Source source;
+ DeserializeSourceFromPb(pb_item.source(), src_pool, &source);
+ out_value->SetSource(std::move(source));
+ }
+ out_value->SetComment(pb_item.comment());
+}
+
+static size_t DeserializePluralEnumFromPb(const pb::Plural_Arity& arity) {
+ switch (arity) {
+ case pb::Plural_Arity_ZERO:
+ return Plural::Zero;
+ case pb::Plural_Arity_ONE:
+ return Plural::One;
+ case pb::Plural_Arity_TWO:
+ return Plural::Two;
+ case pb::Plural_Arity_FEW:
+ return Plural::Few;
+ case pb::Plural_Arity_MANY:
+ return Plural::Many;
+ default:
+ break;
+ }
+ return Plural::Other;
+}
+
+std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
+ const android::ResStringPool& src_pool,
+ const ConfigDescription& config,
+ StringPool* value_pool, std::string* out_error) {
+ std::unique_ptr<Value> value;
+ if (pb_value.has_item()) {
+ value = DeserializeItemFromPb(pb_value.item(), src_pool, config, value_pool, out_error);
+ if (value == nullptr) {
+ return {};
+ }
+
+ } else if (pb_value.has_compound_value()) {
+ const pb::CompoundValue& pb_compound_value = pb_value.compound_value();
+ switch (pb_compound_value.value_case()) {
+ case pb::CompoundValue::kAttr: {
+ const pb::Attribute& pb_attr = pb_compound_value.attr();
+ std::unique_ptr<Attribute> attr = util::make_unique<Attribute>();
+ attr->type_mask = pb_attr.format_flags();
+ attr->min_int = pb_attr.min_int();
+ attr->max_int = pb_attr.max_int();
+ for (const pb::Attribute_Symbol& pb_symbol : pb_attr.symbol()) {
+ Attribute::Symbol symbol;
+ DeserializeItemMetaDataFromPb(pb_symbol, src_pool, &symbol.symbol);
+ if (!DeserializeReferenceFromPb(pb_symbol.name(), &symbol.symbol, out_error)) {
+ return {};
+ }
+ symbol.value = pb_symbol.value();
+ attr->symbols.push_back(std::move(symbol));
+ }
+ value = std::move(attr);
+ } break;
+
+ case pb::CompoundValue::kStyle: {
+ const pb::Style& pb_style = pb_compound_value.style();
+ std::unique_ptr<Style> style = util::make_unique<Style>();
+ if (pb_style.has_parent()) {
+ style->parent = Reference();
+ if (!DeserializeReferenceFromPb(pb_style.parent(), &style->parent.value(), out_error)) {
+ return {};
+ }
+
+ if (pb_style.has_parent_source()) {
+ Source parent_source;
+ DeserializeSourceFromPb(pb_style.parent_source(), src_pool, &parent_source);
+ style->parent.value().SetSource(std::move(parent_source));
+ }
+ }
+
+ for (const pb::Style_Entry& pb_entry : pb_style.entry()) {
+ Style::Entry entry;
+ if (!DeserializeReferenceFromPb(pb_entry.key(), &entry.key, out_error)) {
+ return {};
+ }
+ DeserializeItemMetaDataFromPb(pb_entry, src_pool, &entry.key);
+ entry.value =
+ DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, out_error);
+ if (entry.value == nullptr) {
+ return {};
+ }
+
+ // Copy the meta-data into the value as well.
+ DeserializeItemMetaDataFromPb(pb_entry, src_pool, entry.value.get());
+ style->entries.push_back(std::move(entry));
+ }
+ value = std::move(style);
+ } break;
+
+ case pb::CompoundValue::kStyleable: {
+ const pb::Styleable& pb_styleable = pb_compound_value.styleable();
+ std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+ for (const pb::Styleable_Entry& pb_entry : pb_styleable.entry()) {
+ Reference attr_ref;
+ DeserializeItemMetaDataFromPb(pb_entry, src_pool, &attr_ref);
+ DeserializeReferenceFromPb(pb_entry.attr(), &attr_ref, out_error);
+ styleable->entries.push_back(std::move(attr_ref));
+ }
+ value = std::move(styleable);
+ } break;
+
+ case pb::CompoundValue::kArray: {
+ const pb::Array& pb_array = pb_compound_value.array();
+ std::unique_ptr<Array> array = util::make_unique<Array>();
+ for (const pb::Array_Element& pb_entry : pb_array.element()) {
+ std::unique_ptr<Item> item =
+ DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, out_error);
+ if (item == nullptr) {
+ return {};
+ }
+
+ DeserializeItemMetaDataFromPb(pb_entry, src_pool, item.get());
+ array->elements.push_back(std::move(item));
+ }
+ value = std::move(array);
+ } break;
+
+ case pb::CompoundValue::kPlural: {
+ const pb::Plural& pb_plural = pb_compound_value.plural();
+ std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ for (const pb::Plural_Entry& pb_entry : pb_plural.entry()) {
+ size_t plural_idx = DeserializePluralEnumFromPb(pb_entry.arity());
+ plural->values[plural_idx] =
+ DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, out_error);
+ if (!plural->values[plural_idx]) {
+ return {};
+ }
+
+ DeserializeItemMetaDataFromPb(pb_entry, src_pool, plural->values[plural_idx].get());
+ }
+ value = std::move(plural);
+ } break;
+
+ default:
+ LOG(FATAL) << "unknown compound value: " << (int)pb_compound_value.value_case();
+ break;
+ }
+ } else {
+ LOG(FATAL) << "unknown value: " << (int)pb_value.value_case();
+ return {};
+ }
+
+ CHECK(value) << "forgot to set value";
+
+ value->SetWeak(pb_value.weak());
+ DeserializeItemMetaDataFromPb(pb_value, src_pool, value.get());
+ return value;
+}
+
+std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
+ const android::ResStringPool& src_pool,
+ const ConfigDescription& config, StringPool* value_pool,
+ std::string* out_error) {
+ switch (pb_item.value_case()) {
+ case pb::Item::kRef: {
+ const pb::Reference& pb_ref = pb_item.ref();
+ std::unique_ptr<Reference> ref = util::make_unique<Reference>();
+ if (!DeserializeReferenceFromPb(pb_ref, ref.get(), out_error)) {
+ return {};
+ }
+ return std::move(ref);
+ } break;
+
+ case pb::Item::kPrim: {
+ const pb::Primitive& pb_prim = pb_item.prim();
+ return util::make_unique<BinaryPrimitive>(static_cast<uint8_t>(pb_prim.type()),
+ pb_prim.data());
+ } break;
+
+ case pb::Item::kId: {
+ return util::make_unique<Id>();
+ } break;
+
+ case pb::Item::kStr: {
+ return util::make_unique<String>(
+ value_pool->MakeRef(pb_item.str().value(), StringPool::Context(config)));
+ } break;
+
+ case pb::Item::kRawStr: {
+ return util::make_unique<RawString>(
+ value_pool->MakeRef(pb_item.raw_str().value(), StringPool::Context(config)));
+ } break;
+
+ case pb::Item::kStyledStr: {
+ const pb::StyledString& pb_str = pb_item.styled_str();
+ StyleString style_str{pb_str.value()};
+ for (const pb::StyledString::Span& pb_span : pb_str.span()) {
+ style_str.spans.push_back(Span{pb_span.tag(), pb_span.first_char(), pb_span.last_char()});
+ }
+ return util::make_unique<StyledString>(value_pool->MakeRef(
+ style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
+ } break;
+
+ case pb::Item::kFile: {
+ return util::make_unique<FileReference>(value_pool->MakeRef(
+ pb_item.file().path(), StringPool::Context(StringPool::Context::kHighPriority, config)));
+ } break;
+
+ default:
+ LOG(FATAL) << "unknown item: " << (int)pb_item.value_case();
+ break;
+ }
+ return {};
+}
+
+std::unique_ptr<xml::XmlResource> DeserializeXmlResourceFromPb(const pb::XmlNode& pb_node,
+ std::string* out_error) {
+ if (!pb_node.has_element()) {
+ return {};
+ }
+
+ std::unique_ptr<xml::XmlResource> resource = util::make_unique<xml::XmlResource>();
+ resource->root = util::make_unique<xml::Element>();
+ if (!DeserializeXmlFromPb(pb_node, resource->root.get(), &resource->string_pool, out_error)) {
+ return {};
+ }
+ return resource;
+}
+
+bool DeserializeXmlFromPb(const pb::XmlNode& pb_node, xml::Element* out_el, StringPool* value_pool,
+ std::string* out_error) {
+ const pb::XmlElement& pb_el = pb_node.element();
+ out_el->name = pb_el.name();
+ out_el->namespace_uri = pb_el.namespace_uri();
+ out_el->line_number = pb_node.source().line_number();
+ out_el->column_number = pb_node.source().column_number();
+
+ for (const pb::XmlNamespace& pb_ns : pb_el.namespace_declaration()) {
+ xml::NamespaceDecl decl;
+ decl.uri = pb_ns.uri();
+ decl.prefix = pb_ns.prefix();
+ decl.line_number = pb_ns.source().line_number();
+ decl.column_number = pb_ns.source().column_number();
+ out_el->namespace_decls.push_back(std::move(decl));
+ }
+
+ for (const pb::XmlAttribute& pb_attr : pb_el.attribute()) {
+ xml::Attribute attr;
+ attr.name = pb_attr.name();
+ attr.namespace_uri = pb_attr.namespace_uri();
+ attr.value = pb_attr.value();
+ if (pb_attr.resource_id() != 0u) {
+ attr.compiled_attribute = xml::AaptAttribute{Attribute(), ResourceId(pb_attr.resource_id())};
+ }
+ if (pb_attr.has_compiled_item()) {
+ attr.compiled_value =
+ DeserializeItemFromPb(pb_attr.compiled_item(), {}, {}, value_pool, out_error);
+ if (attr.compiled_value == nullptr) {
+ return {};
+ }
+ attr.compiled_value->SetSource(Source().WithLine(pb_attr.source().line_number()));
+ }
+ out_el->attributes.push_back(std::move(attr));
+ }
+
+ // Deserialize the children.
+ for (const pb::XmlNode& pb_child : pb_el.child()) {
+ switch (pb_child.node_case()) {
+ case pb::XmlNode::NodeCase::kText: {
+ std::unique_ptr<xml::Text> text = util::make_unique<xml::Text>();
+ text->line_number = pb_child.source().line_number();
+ text->column_number = pb_child.source().column_number();
+ text->text = pb_child.text();
+ out_el->AppendChild(std::move(text));
+ } break;
+
+ case pb::XmlNode::NodeCase::kElement: {
+ std::unique_ptr<xml::Element> child_el = util::make_unique<xml::Element>();
+ if (!DeserializeXmlFromPb(pb_child, child_el.get(), value_pool, out_error)) {
+ return false;
+ }
+ out_el->AppendChild(std::move(child_el));
+ } break;
+
+ default:
+ LOG(FATAL) << "unknown XmlNode " << (int)pb_child.node_case();
+ break;
+ }
+ }
+ return true;
+}
+
+CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size)
+ : in_(static_cast<const uint8_t*>(data), size) {
+}
+
+void CompiledFileInputStream::EnsureAlignedRead() {
+ const int overflow = in_.CurrentPosition() % 4;
+ if (overflow > 0) {
+ // Reads are always 4 byte aligned.
+ in_.Skip(4 - overflow);
+ }
+}
+
+bool CompiledFileInputStream::ReadLittleEndian32(uint32_t* out_val) {
+ EnsureAlignedRead();
+ return in_.ReadLittleEndian32(out_val);
+}
+
+bool CompiledFileInputStream::ReadCompiledFile(pb::internal::CompiledFile* out_val) {
+ EnsureAlignedRead();
+
+ google::protobuf::uint64 pb_size = 0u;
+ if (!in_.ReadLittleEndian64(&pb_size)) {
+ return false;
+ }
+
+ CodedInputStream::Limit l = in_.PushLimit(static_cast<int>(pb_size));
+
+ // Check that we haven't tried to read past the end.
+ if (static_cast<uint64_t>(in_.BytesUntilLimit()) != pb_size) {
+ in_.PopLimit(l);
+ in_.PushLimit(0);
+ return false;
+ }
+
+ if (!out_val->ParsePartialFromCodedStream(&in_)) {
+ in_.PopLimit(l);
+ in_.PushLimit(0);
+ return false;
+ }
+
+ in_.PopLimit(l);
+ return true;
+}
+
+bool CompiledFileInputStream::ReadDataMetaData(uint64_t* out_offset, uint64_t* out_len) {
+ EnsureAlignedRead();
+
+ google::protobuf::uint64 pb_size = 0u;
+ if (!in_.ReadLittleEndian64(&pb_size)) {
+ return false;
+ }
+
+ // Check that we aren't trying to read past the end.
+ if (pb_size > static_cast<uint64_t>(in_.BytesUntilLimit())) {
+ in_.PushLimit(0);
+ return false;
+ }
+
+ uint64_t offset = static_cast<uint64_t>(in_.CurrentPosition());
+ if (!in_.Skip(pb_size)) {
+ return false;
+ }
+
+ *out_offset = offset;
+ *out_len = pb_size;
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.h b/tools/aapt2/format/proto/ProtoDeserialize.h
new file mode 100644
index 0000000..c8a7199
--- /dev/null
+++ b/tools/aapt2/format/proto/ProtoDeserialize.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef AAPT_FORMAT_PROTO_PROTODESERIALIZE_H
+#define AAPT_FORMAT_PROTO_PROTODESERIALIZE_H
+
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+#include "google/protobuf/io/coded_stream.h"
+
+#include "ConfigDescription.h"
+#include "Configuration.pb.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "Resources.pb.h"
+#include "ResourcesInternal.pb.h"
+#include "StringPool.h"
+#include "xml/XmlDom.h"
+
+namespace aapt {
+
+std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
+ const android::ResStringPool& src_pool,
+ const ConfigDescription& config,
+ StringPool* value_pool, std::string* out_error);
+
+std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
+ const android::ResStringPool& src_pool,
+ const ConfigDescription& config, StringPool* value_pool,
+ std::string* out_error);
+
+std::unique_ptr<xml::XmlResource> DeserializeXmlResourceFromPb(const pb::XmlNode& pb_node,
+ std::string* out_error);
+
+bool DeserializeXmlFromPb(const pb::XmlNode& pb_node, xml::Element* out_el, StringPool* value_pool,
+ std::string* out_error);
+
+bool DeserializeConfigFromPb(const pb::Configuration& pb_config, ConfigDescription* out_config,
+ std::string* out_error);
+
+bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, ResourceTable* out_table,
+ std::string* out_error);
+
+bool DeserializeCompiledFileFromPb(const pb::internal::CompiledFile& pb_file,
+ ResourceFile* out_file, std::string* out_error);
+
+class CompiledFileInputStream {
+ public:
+ explicit CompiledFileInputStream(const void* data, size_t size);
+
+ bool ReadLittleEndian32(uint32_t* outVal);
+ bool ReadCompiledFile(pb::internal::CompiledFile* outVal);
+ bool ReadDataMetaData(uint64_t* outOffset, uint64_t* outLen);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompiledFileInputStream);
+
+ void EnsureAlignedRead();
+
+ ::google::protobuf::io::CodedInputStream in_;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_FORMAT_PROTO_PROTODESERIALIZE_H */
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
new file mode 100644
index 0000000..c0d3614
--- /dev/null
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "format/proto/ProtoSerialize.h"
+
+#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
+
+#include "ValueVisitor.h"
+#include "util/BigBuffer.h"
+
+using ::google::protobuf::io::CodedOutputStream;
+using ::google::protobuf::io::ZeroCopyOutputStream;
+
+namespace aapt {
+
+void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool) {
+ BigBuffer buffer(1024);
+ StringPool::FlattenUtf8(&buffer, pool);
+
+ std::string* data = out_pb_pool->mutable_data();
+ data->reserve(buffer.size());
+
+ size_t offset = 0;
+ for (const BigBuffer::Block& block : buffer) {
+ data->insert(data->begin() + offset, block.buffer.get(), block.buffer.get() + block.size);
+ offset += block.size;
+ }
+}
+
+void SerializeSourceToPb(const Source& source, StringPool* src_pool, pb::Source* out_pb_source) {
+ StringPool::Ref ref = src_pool->MakeRef(source.path);
+ out_pb_source->set_path_idx(static_cast<uint32_t>(ref.index()));
+ if (source.line) {
+ out_pb_source->mutable_position()->set_line_number(static_cast<uint32_t>(source.line.value()));
+ }
+}
+
+static pb::SymbolStatus_Visibility SerializeVisibilityToPb(SymbolState state) {
+ switch (state) {
+ case SymbolState::kPrivate:
+ return pb::SymbolStatus_Visibility_PRIVATE;
+ case SymbolState::kPublic:
+ return pb::SymbolStatus_Visibility_PUBLIC;
+ default:
+ break;
+ }
+ return pb::SymbolStatus_Visibility_UNKNOWN;
+}
+
+void SerializeConfig(const ConfigDescription& config, pb::Configuration* out_pb_config) {
+ out_pb_config->set_mcc(config.mcc);
+ out_pb_config->set_mnc(config.mnc);
+ out_pb_config->set_locale(config.GetBcp47LanguageTag());
+
+ switch (config.screenLayout & ConfigDescription::MASK_LAYOUTDIR) {
+ case ConfigDescription::LAYOUTDIR_LTR:
+ out_pb_config->set_layout_direction(pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_LTR);
+ break;
+
+ case ConfigDescription::LAYOUTDIR_RTL:
+ out_pb_config->set_layout_direction(pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_RTL);
+ break;
+ }
+
+ out_pb_config->set_screen_width(config.screenWidth);
+ out_pb_config->set_screen_height(config.screenHeight);
+ out_pb_config->set_screen_width_dp(config.screenWidthDp);
+ out_pb_config->set_screen_height_dp(config.screenHeightDp);
+ out_pb_config->set_smallest_screen_width_dp(config.smallestScreenWidthDp);
+
+ switch (config.screenLayout & ConfigDescription::MASK_SCREENSIZE) {
+ case ConfigDescription::SCREENSIZE_SMALL:
+ out_pb_config->set_screen_layout_size(
+ pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_SMALL);
+ break;
+
+ case ConfigDescription::SCREENSIZE_NORMAL:
+ out_pb_config->set_screen_layout_size(
+ pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_NORMAL);
+ break;
+
+ case ConfigDescription::SCREENSIZE_LARGE:
+ out_pb_config->set_screen_layout_size(
+ pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_LARGE);
+ break;
+
+ case ConfigDescription::SCREENSIZE_XLARGE:
+ out_pb_config->set_screen_layout_size(
+ pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_XLARGE);
+ break;
+ }
+
+ switch (config.screenLayout & ConfigDescription::MASK_SCREENLONG) {
+ case ConfigDescription::SCREENLONG_YES:
+ out_pb_config->set_screen_layout_long(
+ pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_LONG);
+ break;
+
+ case ConfigDescription::SCREENLONG_NO:
+ out_pb_config->set_screen_layout_long(
+ pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_NOTLONG);
+ break;
+ }
+
+ switch (config.screenLayout2 & ConfigDescription::MASK_SCREENROUND) {
+ case ConfigDescription::SCREENROUND_YES:
+ out_pb_config->set_screen_round(pb::Configuration_ScreenRound_SCREEN_ROUND_ROUND);
+ break;
+
+ case ConfigDescription::SCREENROUND_NO:
+ out_pb_config->set_screen_round(pb::Configuration_ScreenRound_SCREEN_ROUND_NOTROUND);
+ break;
+ }
+
+ switch (config.colorMode & ConfigDescription::MASK_WIDE_COLOR_GAMUT) {
+ case ConfigDescription::WIDE_COLOR_GAMUT_YES:
+ out_pb_config->set_wide_color_gamut(pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_WIDECG);
+ break;
+
+ case ConfigDescription::WIDE_COLOR_GAMUT_NO:
+ out_pb_config->set_wide_color_gamut(
+ pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_NOWIDECG);
+ break;
+ }
+
+ switch (config.colorMode & ConfigDescription::MASK_HDR) {
+ case ConfigDescription::HDR_YES:
+ out_pb_config->set_hdr(pb::Configuration_Hdr_HDR_HIGHDR);
+ break;
+
+ case ConfigDescription::HDR_NO:
+ out_pb_config->set_hdr(pb::Configuration_Hdr_HDR_LOWDR);
+ break;
+ }
+
+ switch (config.orientation) {
+ case ConfigDescription::ORIENTATION_PORT:
+ out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_PORT);
+ break;
+
+ case ConfigDescription::ORIENTATION_LAND:
+ out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_LAND);
+ break;
+
+ case ConfigDescription::ORIENTATION_SQUARE:
+ out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_SQUARE);
+ break;
+ }
+
+ switch (config.uiMode & ConfigDescription::MASK_UI_MODE_TYPE) {
+ case ConfigDescription::UI_MODE_TYPE_NORMAL:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_NORMAL);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_DESK:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_DESK);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_CAR:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_CAR);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_TELEVISION:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_TELEVISION);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_APPLIANCE:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_APPLIANCE);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_WATCH:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_WATCH);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_VR_HEADSET:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_VRHEADSET);
+ break;
+ }
+
+ switch (config.uiMode & ConfigDescription::MASK_UI_MODE_NIGHT) {
+ case ConfigDescription::UI_MODE_NIGHT_YES:
+ out_pb_config->set_ui_mode_night(pb::Configuration_UiModeNight_UI_MODE_NIGHT_NIGHT);
+ break;
+
+ case ConfigDescription::UI_MODE_NIGHT_NO:
+ out_pb_config->set_ui_mode_night(pb::Configuration_UiModeNight_UI_MODE_NIGHT_NOTNIGHT);
+ break;
+ }
+
+ out_pb_config->set_density(config.density);
+
+ switch (config.touchscreen) {
+ case ConfigDescription::TOUCHSCREEN_NOTOUCH:
+ out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_NOTOUCH);
+ break;
+
+ case ConfigDescription::TOUCHSCREEN_STYLUS:
+ out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_STYLUS);
+ break;
+
+ case ConfigDescription::TOUCHSCREEN_FINGER:
+ out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_FINGER);
+ break;
+ }
+
+ switch (config.inputFlags & ConfigDescription::MASK_KEYSHIDDEN) {
+ case ConfigDescription::KEYSHIDDEN_NO:
+ out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSEXPOSED);
+ break;
+
+ case ConfigDescription::KEYSHIDDEN_YES:
+ out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSHIDDEN);
+ break;
+
+ case ConfigDescription::KEYSHIDDEN_SOFT:
+ out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSSOFT);
+ break;
+ }
+
+ switch (config.keyboard) {
+ case ConfigDescription::KEYBOARD_NOKEYS:
+ out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_NOKEYS);
+ break;
+
+ case ConfigDescription::KEYBOARD_QWERTY:
+ out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_QWERTY);
+ break;
+
+ case ConfigDescription::KEYBOARD_12KEY:
+ out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_TWELVEKEY);
+ break;
+ }
+
+ switch (config.inputFlags & ConfigDescription::MASK_NAVHIDDEN) {
+ case ConfigDescription::NAVHIDDEN_NO:
+ out_pb_config->set_nav_hidden(pb::Configuration_NavHidden_NAV_HIDDEN_NAVEXPOSED);
+ break;
+
+ case ConfigDescription::NAVHIDDEN_YES:
+ out_pb_config->set_nav_hidden(pb::Configuration_NavHidden_NAV_HIDDEN_NAVHIDDEN);
+ break;
+ }
+
+ switch (config.navigation) {
+ case ConfigDescription::NAVIGATION_NONAV:
+ out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_NONAV);
+ break;
+
+ case ConfigDescription::NAVIGATION_DPAD:
+ out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_DPAD);
+ break;
+
+ case ConfigDescription::NAVIGATION_TRACKBALL:
+ out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_TRACKBALL);
+ break;
+
+ case ConfigDescription::NAVIGATION_WHEEL:
+ out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_WHEEL);
+ break;
+ }
+
+ out_pb_config->set_sdk_version(config.sdkVersion);
+}
+
+void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table) {
+ StringPool source_pool;
+ for (const std::unique_ptr<ResourceTablePackage>& package : table.packages) {
+ pb::Package* pb_package = out_table->add_package();
+ if (package->id) {
+ pb_package->mutable_package_id()->set_id(package->id.value());
+ }
+ pb_package->set_package_name(package->name);
+
+ for (const std::unique_ptr<ResourceTableType>& type : package->types) {
+ pb::Type* pb_type = pb_package->add_type();
+ if (type->id) {
+ pb_type->mutable_type_id()->set_id(type->id.value());
+ }
+ pb_type->set_name(ToString(type->type).to_string());
+
+ for (const std::unique_ptr<ResourceEntry>& entry : type->entries) {
+ pb::Entry* pb_entry = pb_type->add_entry();
+ if (entry->id) {
+ pb_entry->mutable_entry_id()->set_id(entry->id.value());
+ }
+ pb_entry->set_name(entry->name);
+
+ // Write the SymbolStatus struct.
+ pb::SymbolStatus* pb_status = pb_entry->mutable_symbol_status();
+ pb_status->set_visibility(SerializeVisibilityToPb(entry->symbol_status.state));
+ SerializeSourceToPb(entry->symbol_status.source, &source_pool, pb_status->mutable_source());
+ pb_status->set_comment(entry->symbol_status.comment);
+ pb_status->set_allow_new(entry->symbol_status.allow_new);
+
+ for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
+ pb::ConfigValue* pb_config_value = pb_entry->add_config_value();
+ SerializeConfig(config_value->config, pb_config_value->mutable_config());
+ pb_config_value->mutable_config()->set_product(config_value->product);
+ SerializeValueToPb(*config_value->value, pb_config_value->mutable_value(), &source_pool);
+ }
+ }
+ }
+ }
+ SerializeStringPoolToPb(source_pool, out_table->mutable_source_pool());
+}
+
+static pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type) {
+ switch (type) {
+ case Reference::Type::kResource:
+ return pb::Reference_Type_REFERENCE;
+ case Reference::Type::kAttribute:
+ return pb::Reference_Type_ATTRIBUTE;
+ default:
+ break;
+ }
+ return pb::Reference_Type_REFERENCE;
+}
+
+static void SerializeReferenceToPb(const Reference& ref, pb::Reference* pb_ref) {
+ pb_ref->set_id(ref.id.value_or_default(ResourceId(0x0)).id);
+
+ if (ref.name) {
+ pb_ref->set_name(ref.name.value().ToString());
+ }
+
+ pb_ref->set_private_(ref.private_reference);
+ pb_ref->set_type(SerializeReferenceTypeToPb(ref.reference_type));
+}
+
+template <typename T>
+static void SerializeItemMetaDataToPb(const Item& item, T* pb_item, StringPool* src_pool) {
+ if (src_pool != nullptr) {
+ SerializeSourceToPb(item.GetSource(), src_pool, pb_item->mutable_source());
+ }
+ pb_item->set_comment(item.GetComment());
+}
+
+static pb::Plural_Arity SerializePluralEnumToPb(size_t plural_idx) {
+ switch (plural_idx) {
+ case Plural::Zero:
+ return pb::Plural_Arity_ZERO;
+ case Plural::One:
+ return pb::Plural_Arity_ONE;
+ case Plural::Two:
+ return pb::Plural_Arity_TWO;
+ case Plural::Few:
+ return pb::Plural_Arity_FEW;
+ case Plural::Many:
+ return pb::Plural_Arity_MANY;
+ default:
+ break;
+ }
+ return pb::Plural_Arity_OTHER;
+}
+
+namespace {
+
+class ValueSerializer : public ConstValueVisitor {
+ public:
+ using ConstValueVisitor::Visit;
+
+ ValueSerializer(pb::Value* out_value, StringPool* src_pool)
+ : out_value_(out_value), src_pool_(src_pool) {
+ }
+
+ void Visit(const Reference* ref) override {
+ SerializeReferenceToPb(*ref, out_value_->mutable_item()->mutable_ref());
+ }
+
+ void Visit(const String* str) override {
+ out_value_->mutable_item()->mutable_str()->set_value(*str->value);
+ }
+
+ void Visit(const RawString* str) override {
+ out_value_->mutable_item()->mutable_raw_str()->set_value(*str->value);
+ }
+
+ void Visit(const StyledString* str) override {
+ pb::StyledString* pb_str = out_value_->mutable_item()->mutable_styled_str();
+ pb_str->set_value(str->value->value);
+ for (const StringPool::Span& span : str->value->spans) {
+ pb::StyledString::Span* pb_span = pb_str->add_span();
+ pb_span->set_tag(*span.name);
+ pb_span->set_first_char(span.first_char);
+ pb_span->set_last_char(span.last_char);
+ }
+ }
+
+ void Visit(const FileReference* file) override {
+ out_value_->mutable_item()->mutable_file()->set_path(*file->path);
+ }
+
+ void Visit(const Id* /*id*/) override {
+ out_value_->mutable_item()->mutable_id();
+ }
+
+ void Visit(const BinaryPrimitive* prim) override {
+ android::Res_value val = {};
+ prim->Flatten(&val);
+
+ pb::Primitive* pb_prim = out_value_->mutable_item()->mutable_prim();
+ pb_prim->set_type(val.dataType);
+ pb_prim->set_data(val.data);
+ }
+
+ void Visit(const Attribute* attr) override {
+ pb::Attribute* pb_attr = out_value_->mutable_compound_value()->mutable_attr();
+ pb_attr->set_format_flags(attr->type_mask);
+ pb_attr->set_min_int(attr->min_int);
+ pb_attr->set_max_int(attr->max_int);
+
+ for (auto& symbol : attr->symbols) {
+ pb::Attribute_Symbol* pb_symbol = pb_attr->add_symbol();
+ SerializeItemMetaDataToPb(symbol.symbol, pb_symbol, src_pool_);
+ SerializeReferenceToPb(symbol.symbol, pb_symbol->mutable_name());
+ pb_symbol->set_value(symbol.value);
+ }
+ }
+
+ void Visit(const Style* style) override {
+ pb::Style* pb_style = out_value_->mutable_compound_value()->mutable_style();
+ if (style->parent) {
+ const Reference& parent = style->parent.value();
+ SerializeReferenceToPb(parent, pb_style->mutable_parent());
+ if (src_pool_ != nullptr) {
+ SerializeSourceToPb(parent.GetSource(), src_pool_, pb_style->mutable_parent_source());
+ }
+ }
+
+ for (const Style::Entry& entry : style->entries) {
+ pb::Style_Entry* pb_entry = pb_style->add_entry();
+ SerializeReferenceToPb(entry.key, pb_entry->mutable_key());
+ SerializeItemMetaDataToPb(entry.key, pb_entry, src_pool_);
+ SerializeItemToPb(*entry.value, pb_entry->mutable_item());
+ }
+ }
+
+ void Visit(const Styleable* styleable) override {
+ pb::Styleable* pb_styleable = out_value_->mutable_compound_value()->mutable_styleable();
+ for (const Reference& entry : styleable->entries) {
+ pb::Styleable_Entry* pb_entry = pb_styleable->add_entry();
+ SerializeItemMetaDataToPb(entry, pb_entry, src_pool_);
+ SerializeReferenceToPb(entry, pb_entry->mutable_attr());
+ }
+ }
+
+ void Visit(const Array* array) override {
+ pb::Array* pb_array = out_value_->mutable_compound_value()->mutable_array();
+ for (const std::unique_ptr<Item>& element : array->elements) {
+ pb::Array_Element* pb_element = pb_array->add_element();
+ SerializeItemMetaDataToPb(*element, pb_element, src_pool_);
+ SerializeItemToPb(*element, pb_element->mutable_item());
+ }
+ }
+
+ void Visit(const Plural* plural) override {
+ pb::Plural* pb_plural = out_value_->mutable_compound_value()->mutable_plural();
+ const size_t count = plural->values.size();
+ for (size_t i = 0; i < count; i++) {
+ if (!plural->values[i]) {
+ // No plural value set here.
+ continue;
+ }
+
+ pb::Plural_Entry* pb_entry = pb_plural->add_entry();
+ pb_entry->set_arity(SerializePluralEnumToPb(i));
+ SerializeItemMetaDataToPb(*plural->values[i], pb_entry, src_pool_);
+ SerializeItemToPb(*plural->values[i], pb_entry->mutable_item());
+ }
+ }
+
+ void VisitAny(const Value* unknown) override {
+ LOG(FATAL) << "unimplemented value: " << *unknown;
+ }
+
+ private:
+ pb::Value* out_value_;
+ StringPool* src_pool_;
+};
+
+} // namespace
+
+void SerializeValueToPb(const Value& value, pb::Value* out_value, StringPool* src_pool) {
+ ValueSerializer serializer(out_value, src_pool);
+ value.Accept(&serializer);
+
+ // Serialize the meta-data of the Value.
+ out_value->set_comment(value.GetComment());
+ out_value->set_weak(value.IsWeak());
+ if (src_pool != nullptr) {
+ SerializeSourceToPb(value.GetSource(), src_pool, out_value->mutable_source());
+ }
+}
+
+void SerializeItemToPb(const Item& item, pb::Item* out_item) {
+ pb::Value value;
+ ValueSerializer serializer(&value, nullptr);
+ item.Accept(&serializer);
+ out_item->MergeFrom(value.item());
+}
+
+void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file) {
+ out_file->set_resource_name(file.name.ToString());
+ out_file->set_source_path(file.source.path);
+ SerializeConfig(file.config, out_file->mutable_config());
+
+ for (const SourcedResourceName& exported : file.exported_symbols) {
+ pb::internal::CompiledFile_Symbol* pb_symbol = out_file->add_exported_symbol();
+ pb_symbol->set_resource_name(exported.name.ToString());
+ pb_symbol->mutable_source()->set_line_number(exported.line);
+ }
+}
+
+static void SerializeXmlCommon(const xml::Node& node, pb::XmlNode* out_node) {
+ pb::SourcePosition* pb_src = out_node->mutable_source();
+ pb_src->set_line_number(node.line_number);
+ pb_src->set_column_number(node.column_number);
+}
+
+void SerializeXmlToPb(const xml::Element& el, pb::XmlNode* out_node) {
+ SerializeXmlCommon(el, out_node);
+
+ pb::XmlElement* pb_element = out_node->mutable_element();
+ pb_element->set_name(el.name);
+ pb_element->set_namespace_uri(el.namespace_uri);
+
+ for (const xml::NamespaceDecl& ns : el.namespace_decls) {
+ pb::XmlNamespace* pb_ns = pb_element->add_namespace_declaration();
+ pb_ns->set_prefix(ns.prefix);
+ pb_ns->set_uri(ns.uri);
+ pb::SourcePosition* pb_src = pb_ns->mutable_source();
+ pb_src->set_line_number(ns.line_number);
+ pb_src->set_column_number(ns.column_number);
+ }
+
+ for (const xml::Attribute& attr : el.attributes) {
+ pb::XmlAttribute* pb_attr = pb_element->add_attribute();
+ pb_attr->set_name(attr.name);
+ pb_attr->set_namespace_uri(attr.namespace_uri);
+ pb_attr->set_value(attr.value);
+ if (attr.compiled_attribute) {
+ const ResourceId attr_id = attr.compiled_attribute.value().id.value_or_default({});
+ pb_attr->set_resource_id(attr_id.id);
+ }
+ if (attr.compiled_value != nullptr) {
+ SerializeItemToPb(*attr.compiled_value, pb_attr->mutable_compiled_item());
+ pb::SourcePosition* pb_src = pb_attr->mutable_source();
+ pb_src->set_line_number(attr.compiled_value->GetSource().line.value_or_default(0));
+ }
+ }
+
+ for (const std::unique_ptr<xml::Node>& child : el.children) {
+ if (const xml::Element* child_el = xml::NodeCast<xml::Element>(child.get())) {
+ SerializeXmlToPb(*child_el, pb_element->add_child());
+ } else if (const xml::Text* text_el = xml::NodeCast<xml::Text>(child.get())) {
+ pb::XmlNode* pb_child_node = pb_element->add_child();
+ SerializeXmlCommon(*text_el, pb_child_node);
+ pb_child_node->set_text(text_el->text);
+ } else {
+ LOG(FATAL) << "unhandled XmlNode type";
+ }
+ }
+}
+
+void SerializeXmlResourceToPb(const xml::XmlResource& resource, pb::XmlNode* out_node) {
+ SerializeXmlToPb(*resource.root, out_node);
+}
+
+CompiledFileOutputStream::CompiledFileOutputStream(ZeroCopyOutputStream* out) : out_(out) {
+}
+
+void CompiledFileOutputStream::EnsureAlignedWrite() {
+ const int overflow = out_.ByteCount() % 4;
+ if (overflow > 0) {
+ uint32_t zero = 0u;
+ out_.WriteRaw(&zero, 4 - overflow);
+ }
+}
+
+void CompiledFileOutputStream::WriteLittleEndian32(uint32_t val) {
+ EnsureAlignedWrite();
+ out_.WriteLittleEndian32(val);
+}
+
+void CompiledFileOutputStream::WriteCompiledFile(const pb::internal::CompiledFile& compiled_file) {
+ EnsureAlignedWrite();
+ out_.WriteLittleEndian64(static_cast<uint64_t>(compiled_file.ByteSize()));
+ compiled_file.SerializeWithCachedSizes(&out_);
+}
+
+void CompiledFileOutputStream::WriteData(const BigBuffer& buffer) {
+ EnsureAlignedWrite();
+ out_.WriteLittleEndian64(static_cast<uint64_t>(buffer.size()));
+ for (const BigBuffer::Block& block : buffer) {
+ out_.WriteRaw(block.buffer.get(), block.size);
+ }
+}
+
+void CompiledFileOutputStream::WriteData(const void* data, size_t len) {
+ EnsureAlignedWrite();
+ out_.WriteLittleEndian64(static_cast<uint64_t>(len));
+ out_.WriteRaw(data, len);
+}
+
+bool CompiledFileOutputStream::HadError() {
+ return out_.HadError();
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoSerialize.h b/tools/aapt2/format/proto/ProtoSerialize.h
new file mode 100644
index 0000000..1694b16
--- /dev/null
+++ b/tools/aapt2/format/proto/ProtoSerialize.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef AAPT_FORMAT_PROTO_PROTOSERIALIZE_H
+#define AAPT_FORMAT_PROTO_PROTOSERIALIZE_H
+
+#include "android-base/macros.h"
+#include "google/protobuf/io/coded_stream.h"
+
+#include "ConfigDescription.h"
+#include "Configuration.pb.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "Resources.pb.h"
+#include "ResourcesInternal.pb.h"
+#include "StringPool.h"
+#include "xml/XmlDom.h"
+
+namespace google {
+namespace protobuf {
+namespace io {
+class ZeroCopyOutputStream;
+} // namespace io
+} // namespace protobuf
+} // namespace google
+
+namespace aapt {
+
+// Serializes a Value to its protobuf representation. An optional StringPool will hold the
+// source path string.
+void SerializeValueToPb(const Value& value, pb::Value* out_value, StringPool* src_pool = nullptr);
+
+// Serialize an Item into its protobuf representation. pb::Item does not store the source path nor
+// comments of an Item.
+void SerializeItemToPb(const Item& item, pb::Item* out_item);
+
+// Serializes an XML element into its protobuf representation.
+void SerializeXmlToPb(const xml::Element& el, pb::XmlNode* out_node);
+
+// Serializes an XmlResource into its protobuf representation. The ResourceFile is NOT serialized.
+void SerializeXmlResourceToPb(const xml::XmlResource& resource, pb::XmlNode* out_node);
+
+// Serializes a StringPool into its protobuf representation, which is really just the binary
+// ResStringPool representation stuffed into a bytes field.
+void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool);
+
+// Serializes a ConfigDescription into its protobuf representation.
+void SerializeConfig(const ConfigDescription& config, pb::Configuration* out_pb_config);
+
+// Serializes a ResourceTable into its protobuf representation.
+void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table);
+
+// Serializes a ResourceFile into its protobuf representation.
+void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file);
+
+class CompiledFileOutputStream {
+ public:
+ explicit CompiledFileOutputStream(::google::protobuf::io::ZeroCopyOutputStream* out);
+
+ void WriteLittleEndian32(uint32_t value);
+ void WriteCompiledFile(const pb::internal::CompiledFile& compiledFile);
+ void WriteData(const BigBuffer& buffer);
+ void WriteData(const void* data, size_t len);
+ bool HadError();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompiledFileOutputStream);
+
+ void EnsureAlignedWrite();
+
+ ::google::protobuf::io::CodedOutputStream out_;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_FORMAT_PROTO_PROTOSERIALIZE_H */
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
new file mode 100644
index 0000000..2154d5a
--- /dev/null
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "format/proto/ProtoSerialize.h"
+
+#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
+
+#include "ResourceUtils.h"
+#include "format/proto/ProtoDeserialize.h"
+#include "test/Test.h"
+
+using ::android::StringPiece;
+using ::google::protobuf::io::StringOutputStream;
+using ::testing::Eq;
+using ::testing::IsEmpty;
+using ::testing::NotNull;
+using ::testing::SizeIs;
+using ::testing::StrEq;
+
+namespace aapt {
+
+TEST(ProtoSerializeTest, SerializeSinglePackage) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.a", 0x7f)
+ .AddFileReference("com.app.a:layout/main", ResourceId(0x7f020000), "res/layout/main.xml")
+ .AddReference("com.app.a:layout/other", ResourceId(0x7f020001), "com.app.a:layout/main")
+ .AddString("com.app.a:string/text", {}, "hi")
+ .AddValue("com.app.a:id/foo", {}, util::make_unique<Id>())
+ .SetSymbolState("com.app.a:bool/foo", {}, SymbolState::kUndefined, true /*allow_new*/)
+ .Build();
+
+ Symbol public_symbol;
+ public_symbol.state = SymbolState::kPublic;
+ ASSERT_TRUE(table->SetSymbolState(test::ParseNameOrDie("com.app.a:layout/main"),
+ ResourceId(0x7f020000), public_symbol,
+ context->GetDiagnostics()));
+
+ Id* id = test::GetValue<Id>(table.get(), "com.app.a:id/foo");
+ ASSERT_THAT(id, NotNull());
+
+ // Make a plural.
+ std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ plural->values[Plural::One] = util::make_unique<String>(table->string_pool.MakeRef("one"));
+ ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.app.a:plurals/hey"), ConfigDescription{},
+ {}, std::move(plural), context->GetDiagnostics()));
+
+ // Make a styled string.
+ StyleString style_string;
+ style_string.str = "hello";
+ style_string.spans.push_back(Span{"b", 0u, 4u});
+ ASSERT_TRUE(
+ table->AddResource(test::ParseNameOrDie("com.app.a:string/styled"), ConfigDescription{}, {},
+ util::make_unique<StyledString>(table->string_pool.MakeRef(style_string)),
+ context->GetDiagnostics()));
+
+ // Make a resource with different products.
+ ASSERT_TRUE(table->AddResource(
+ test::ParseNameOrDie("com.app.a:integer/one"), test::ParseConfigOrDie("land"), {},
+ test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 123u), context->GetDiagnostics()));
+ ASSERT_TRUE(table->AddResource(
+ test::ParseNameOrDie("com.app.a:integer/one"), test::ParseConfigOrDie("land"), "tablet",
+ test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 321u), context->GetDiagnostics()));
+
+ // Make a reference with both resource name and resource ID.
+ // The reference should point to a resource outside of this table to test that both name and id
+ // get serialized.
+ Reference expected_ref;
+ expected_ref.name = test::ParseNameOrDie("android:layout/main");
+ expected_ref.id = ResourceId(0x01020000);
+ ASSERT_TRUE(table->AddResource(
+ test::ParseNameOrDie("com.app.a:layout/abc"), ConfigDescription::DefaultConfig(), {},
+ util::make_unique<Reference>(expected_ref), context->GetDiagnostics()));
+
+ pb::ResourceTable pb_table;
+ SerializeTableToPb(*table, &pb_table);
+
+ ResourceTable new_table;
+ std::string error;
+ ASSERT_TRUE(DeserializeTableFromPb(pb_table, &new_table, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ Id* new_id = test::GetValue<Id>(&new_table, "com.app.a:id/foo");
+ ASSERT_THAT(new_id, NotNull());
+ EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak()));
+
+ Maybe<ResourceTable::SearchResult> result =
+ new_table.FindResource(test::ParseNameOrDie("com.app.a:layout/main"));
+ ASSERT_TRUE(result);
+
+ EXPECT_THAT(result.value().type->symbol_status.state, Eq(SymbolState::kPublic));
+ EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic));
+
+ result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
+ ASSERT_TRUE(result);
+ EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kUndefined));
+ EXPECT_TRUE(result.value().entry->symbol_status.allow_new);
+
+ // Find the product-dependent values
+ BinaryPrimitive* prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
+ &new_table, "com.app.a:integer/one", test::ParseConfigOrDie("land"), "");
+ ASSERT_THAT(prim, NotNull());
+ EXPECT_THAT(prim->value.data, Eq(123u));
+
+ prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
+ &new_table, "com.app.a:integer/one", test::ParseConfigOrDie("land"), "tablet");
+ ASSERT_THAT(prim, NotNull());
+ EXPECT_THAT(prim->value.data, Eq(321u));
+
+ Reference* actual_ref = test::GetValue<Reference>(&new_table, "com.app.a:layout/abc");
+ ASSERT_THAT(actual_ref, NotNull());
+ ASSERT_TRUE(actual_ref->name);
+ ASSERT_TRUE(actual_ref->id);
+ EXPECT_THAT(*actual_ref, Eq(expected_ref));
+
+ StyledString* actual_styled_str =
+ test::GetValue<StyledString>(&new_table, "com.app.a:string/styled");
+ ASSERT_THAT(actual_styled_str, NotNull());
+ EXPECT_THAT(actual_styled_str->value->value, Eq("hello"));
+ ASSERT_THAT(actual_styled_str->value->spans, SizeIs(1u));
+ EXPECT_THAT(*actual_styled_str->value->spans[0].name, Eq("b"));
+ EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u));
+ EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u));
+}
+
+TEST(ProtoSerializeTest, SerializeFileHeader) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ ResourceFile f;
+ f.config = test::ParseConfigOrDie("hdpi-v9");
+ f.name = test::ParseNameOrDie("com.app.a:layout/main");
+ f.source.path = "res/layout-hdpi-v9/main.xml";
+ f.exported_symbols.push_back(SourcedResourceName{test::ParseNameOrDie("id/unchecked"), 23u});
+
+ const std::string expected_data1 = "123";
+ const std::string expected_data2 = "1234";
+
+ std::string output_str;
+ {
+ pb::internal::CompiledFile pb_f1, pb_f2;
+ SerializeCompiledFileToPb(f, &pb_f1);
+
+ f.name.entry = "__" + f.name.entry + "$0";
+ SerializeCompiledFileToPb(f, &pb_f2);
+
+ StringOutputStream out_stream(&output_str);
+ CompiledFileOutputStream out_file_stream(&out_stream);
+ out_file_stream.WriteLittleEndian32(2);
+ out_file_stream.WriteCompiledFile(pb_f1);
+ out_file_stream.WriteData(expected_data1.data(), expected_data1.size());
+ out_file_stream.WriteCompiledFile(pb_f2);
+ out_file_stream.WriteData(expected_data2.data(), expected_data2.size());
+ ASSERT_FALSE(out_file_stream.HadError());
+ }
+
+ CompiledFileInputStream in_file_stream(output_str.data(), output_str.size());
+ uint32_t num_files = 0;
+ ASSERT_TRUE(in_file_stream.ReadLittleEndian32(&num_files));
+ ASSERT_EQ(2u, num_files);
+
+ // Read the first compiled file.
+
+ pb::internal::CompiledFile new_pb_f1;
+ ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_f1));
+
+ ResourceFile new_f1;
+ std::string error;
+ ASSERT_TRUE(DeserializeCompiledFileFromPb(new_pb_f1, &new_f1, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ uint64_t offset, len;
+ ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len));
+
+ std::string actual_data(output_str.data() + offset, len);
+ EXPECT_EQ(expected_data1, actual_data);
+
+ // Expect the data to be aligned.
+ EXPECT_EQ(0u, offset & 0x03);
+
+ ASSERT_EQ(1u, new_f1.exported_symbols.size());
+ EXPECT_EQ(test::ParseNameOrDie("id/unchecked"), new_f1.exported_symbols[0].name);
+
+ // Read the second compiled file.
+
+ pb::internal::CompiledFile new_pb_f2;
+ ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_f2));
+
+ ResourceFile new_f2;
+ ASSERT_TRUE(DeserializeCompiledFileFromPb(new_pb_f2, &new_f2, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len));
+
+ actual_data = std::string(output_str.data() + offset, len);
+ EXPECT_EQ(expected_data2, actual_data);
+
+ // Expect the data to be aligned.
+ EXPECT_EQ(0u, offset & 0x03);
+}
+
+TEST(ProtoSerializeTest, DeserializeCorruptHeaderSafely) {
+ ResourceFile f;
+ pb::internal::CompiledFile pb_file;
+ SerializeCompiledFileToPb(f, &pb_file);
+
+ const std::string expected_data = "1234";
+
+ std::string output_str;
+ {
+ StringOutputStream out_stream(&output_str);
+ CompiledFileOutputStream out_file_stream(&out_stream);
+ out_file_stream.WriteLittleEndian32(1);
+ out_file_stream.WriteCompiledFile(pb_file);
+ out_file_stream.WriteData(expected_data.data(), expected_data.size());
+ ASSERT_FALSE(out_file_stream.HadError());
+ }
+
+ output_str[4] = 0xff;
+
+ CompiledFileInputStream in_file_stream(output_str.data(), output_str.size());
+
+ uint32_t num_files = 0;
+ EXPECT_TRUE(in_file_stream.ReadLittleEndian32(&num_files));
+ EXPECT_EQ(1u, num_files);
+
+ pb::internal::CompiledFile new_pb_file;
+ EXPECT_FALSE(in_file_stream.ReadCompiledFile(&new_pb_file));
+
+ uint64_t offset, len;
+ EXPECT_FALSE(in_file_stream.ReadDataMetaData(&offset, &len));
+}
+
+TEST(ProtoSerializeTest, SerializeAndDeserializeXml) {
+ xml::Element element;
+ element.line_number = 22;
+ element.column_number = 23;
+ element.name = "element";
+ element.namespace_uri = "uri://";
+
+ xml::NamespaceDecl decl;
+ decl.prefix = "android";
+ decl.uri = xml::kSchemaAndroid;
+ decl.line_number = 21;
+ decl.column_number = 24;
+
+ element.namespace_decls.push_back(decl);
+
+ xml::Attribute attr;
+ attr.name = "name";
+ attr.namespace_uri = xml::kSchemaAndroid;
+ attr.value = "23dp";
+ attr.compiled_attribute = xml::AaptAttribute({}, ResourceId(0x01010000));
+ attr.compiled_value =
+ ResourceUtils::TryParseItemForAttribute(attr.value, android::ResTable_map::TYPE_DIMENSION);
+ attr.compiled_value->SetSource(Source().WithLine(25));
+ element.attributes.push_back(std::move(attr));
+
+ std::unique_ptr<xml::Text> text = util::make_unique<xml::Text>();
+ text->line_number = 25;
+ text->column_number = 3;
+ text->text = "hey there";
+ element.AppendChild(std::move(text));
+
+ std::unique_ptr<xml::Element> child = util::make_unique<xml::Element>();
+ child->name = "child";
+
+ text = util::make_unique<xml::Text>();
+ text->text = "woah there";
+ child->AppendChild(std::move(text));
+
+ element.AppendChild(std::move(child));
+
+ pb::XmlNode pb_xml;
+ SerializeXmlToPb(element, &pb_xml);
+
+ StringPool pool;
+ xml::Element actual_el;
+ std::string error;
+ ASSERT_TRUE(DeserializeXmlFromPb(pb_xml, &actual_el, &pool, &error));
+ ASSERT_THAT(error, IsEmpty());
+
+ EXPECT_THAT(actual_el.name, StrEq("element"));
+ EXPECT_THAT(actual_el.namespace_uri, StrEq("uri://"));
+ EXPECT_THAT(actual_el.line_number, Eq(22u));
+ EXPECT_THAT(actual_el.column_number, Eq(23u));
+
+ ASSERT_THAT(actual_el.namespace_decls, SizeIs(1u));
+ const xml::NamespaceDecl& actual_decl = actual_el.namespace_decls[0];
+ EXPECT_THAT(actual_decl.prefix, StrEq("android"));
+ EXPECT_THAT(actual_decl.uri, StrEq(xml::kSchemaAndroid));
+ EXPECT_THAT(actual_decl.line_number, Eq(21u));
+ EXPECT_THAT(actual_decl.column_number, Eq(24u));
+
+ ASSERT_THAT(actual_el.attributes, SizeIs(1u));
+ const xml::Attribute& actual_attr = actual_el.attributes[0];
+ EXPECT_THAT(actual_attr.name, StrEq("name"));
+ EXPECT_THAT(actual_attr.namespace_uri, StrEq(xml::kSchemaAndroid));
+ EXPECT_THAT(actual_attr.value, StrEq("23dp"));
+
+ ASSERT_THAT(actual_attr.compiled_value, NotNull());
+ const BinaryPrimitive* prim = ValueCast<BinaryPrimitive>(actual_attr.compiled_value.get());
+ ASSERT_THAT(prim, NotNull());
+ EXPECT_THAT(prim->value.dataType, Eq(android::Res_value::TYPE_DIMENSION));
+
+ ASSERT_TRUE(actual_attr.compiled_attribute);
+ ASSERT_TRUE(actual_attr.compiled_attribute.value().id);
+
+ ASSERT_THAT(actual_el.children, SizeIs(2u));
+ const xml::Text* child_text = xml::NodeCast<xml::Text>(actual_el.children[0].get());
+ ASSERT_THAT(child_text, NotNull());
+ const xml::Element* child_el = xml::NodeCast<xml::Element>(actual_el.children[1].get());
+ ASSERT_THAT(child_el, NotNull());
+
+ EXPECT_THAT(child_text->line_number, Eq(25u));
+ EXPECT_THAT(child_text->column_number, Eq(3u));
+ EXPECT_THAT(child_text->text, StrEq("hey there"));
+
+ EXPECT_THAT(child_el->name, StrEq("child"));
+ ASSERT_THAT(child_el->children, SizeIs(1u));
+
+ child_text = xml::NodeCast<xml::Text>(child_el->children[0].get());
+ ASSERT_THAT(child_text, NotNull());
+ EXPECT_THAT(child_text->text, StrEq("woah there"));
+}
+
+static void ExpectConfigSerializes(const StringPiece& config_str) {
+ const ConfigDescription expected_config = test::ParseConfigOrDie(config_str);
+ pb::Configuration pb_config;
+ SerializeConfig(expected_config, &pb_config);
+
+ ConfigDescription actual_config;
+ std::string error;
+ ASSERT_TRUE(DeserializeConfigFromPb(pb_config, &actual_config, &error));
+ ASSERT_THAT(error, IsEmpty());
+ EXPECT_EQ(expected_config, actual_config);
+}
+
+TEST(ProtoSerializeTest, SerializeDeserializeConfiguration) {
+ ExpectConfigSerializes("");
+
+ ExpectConfigSerializes("mcc123");
+
+ ExpectConfigSerializes("mnc123");
+
+ ExpectConfigSerializes("en");
+ ExpectConfigSerializes("en-rGB");
+ ExpectConfigSerializes("b+en+GB");
+
+ ExpectConfigSerializes("ldltr");
+ ExpectConfigSerializes("ldrtl");
+
+ ExpectConfigSerializes("sw3600dp");
+
+ ExpectConfigSerializes("w300dp");
+
+ ExpectConfigSerializes("h400dp");
+
+ ExpectConfigSerializes("small");
+ ExpectConfigSerializes("normal");
+ ExpectConfigSerializes("large");
+ ExpectConfigSerializes("xlarge");
+
+ ExpectConfigSerializes("long");
+ ExpectConfigSerializes("notlong");
+
+ ExpectConfigSerializes("round");
+ ExpectConfigSerializes("notround");
+
+ ExpectConfigSerializes("widecg");
+ ExpectConfigSerializes("nowidecg");
+
+ ExpectConfigSerializes("highdr");
+ ExpectConfigSerializes("lowdr");
+
+ ExpectConfigSerializes("port");
+ ExpectConfigSerializes("land");
+ ExpectConfigSerializes("square");
+
+ ExpectConfigSerializes("desk");
+ ExpectConfigSerializes("car");
+ ExpectConfigSerializes("television");
+ ExpectConfigSerializes("appliance");
+ ExpectConfigSerializes("watch");
+ ExpectConfigSerializes("vrheadset");
+
+ ExpectConfigSerializes("night");
+ ExpectConfigSerializes("notnight");
+
+ ExpectConfigSerializes("300dpi");
+ ExpectConfigSerializes("hdpi");
+
+ ExpectConfigSerializes("notouch");
+ ExpectConfigSerializes("stylus");
+ ExpectConfigSerializes("finger");
+
+ ExpectConfigSerializes("keysexposed");
+ ExpectConfigSerializes("keyshidden");
+ ExpectConfigSerializes("keyssoft");
+
+ ExpectConfigSerializes("nokeys");
+ ExpectConfigSerializes("qwerty");
+ ExpectConfigSerializes("12key");
+
+ ExpectConfigSerializes("navhidden");
+ ExpectConfigSerializes("navexposed");
+
+ ExpectConfigSerializes("nonav");
+ ExpectConfigSerializes("dpad");
+ ExpectConfigSerializes("trackball");
+ ExpectConfigSerializes("wheel");
+
+ ExpectConfigSerializes("300x200");
+
+ ExpectConfigSerializes("v8");
+
+ ExpectConfigSerializes(
+ "mcc123-mnc456-b+en+GB-ldltr-sw300dp-w300dp-h400dp-large-long-round-widecg-highdr-land-car-"
+ "night-xhdpi-stylus-keysexposed-qwerty-navhidden-dpad-300x200-v23");
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index ec1ddb8..02ee876 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -21,7 +21,7 @@
#include "google/protobuf/message_lite.h"
-#include "flatten/Archive.h"
+#include "format/Archive.h"
#include "io/File.h"
#include "io/Io.h"
#include "process/IResourceTableConsumer.h"
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
index 6ad0dd6..0c57e7e 100644
--- a/tools/aapt2/java/ClassDefinition.cpp
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -41,18 +41,21 @@
ClassDefinition::Result ClassDefinition::AddMember(std::unique_ptr<ClassMember> member) {
Result result = Result::kAdded;
- auto iter = members_.find(member);
- if (iter != members_.end()) {
- members_.erase(iter);
+ auto iter = indexed_members_.find(member->GetName());
+ if (iter != indexed_members_.end()) {
+ // Overwrite the entry.
+ ordered_members_[iter->second].reset();
result = Result::kOverridden;
}
- members_.insert(std::move(member));
+
+ indexed_members_[member->GetName()] = ordered_members_.size();
+ ordered_members_.push_back(std::move(member));
return result;
}
bool ClassDefinition::empty() const {
- for (const std::unique_ptr<ClassMember>& member : members_) {
- if (!member->empty()) {
+ for (const std::unique_ptr<ClassMember>& member : ordered_members_) {
+ if (member != nullptr && !member->empty()) {
return false;
}
}
@@ -61,7 +64,7 @@
void ClassDefinition::WriteToStream(const StringPiece& prefix, bool final,
std::ostream* out) const {
- if (members_.empty() && !create_if_empty_) {
+ if (empty() && !create_if_empty_) {
return;
}
@@ -76,9 +79,14 @@
std::string new_prefix = prefix.to_string();
new_prefix.append(kIndent);
- for (const std::unique_ptr<ClassMember>& member : members_) {
- member->WriteToStream(new_prefix, final, out);
- *out << "\n";
+ for (const std::unique_ptr<ClassMember>& member : ordered_members_) {
+ // There can be nullptr members when a member is added to the ClassDefinition
+ // and takes precedence over a previous member with the same name. The overridden member is
+ // set to nullptr.
+ if (member != nullptr) {
+ member->WriteToStream(new_prefix, final, out);
+ *out << "\n";
+ }
}
*out << prefix << "}";
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index 6c4bcad..28a3489 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -18,8 +18,9 @@
#define AAPT_JAVA_CLASSDEFINITION_H
#include <ostream>
-#include <set>
#include <string>
+#include <unordered_map>
+#include <vector>
#include "android-base/macros.h"
#include "androidfw/StringPiece.h"
@@ -73,21 +74,18 @@
void WriteToStream(const android::StringPiece& prefix, bool final,
std::ostream* out) const override {
ClassMember::WriteToStream(prefix, final, out);
-
- *out << prefix << "public static " << (final ? "final " : "") << "int "
- << name_ << "=" << val_ << ";";
+ *out << prefix << "public static " << (final ? "final " : "") << "int " << name_ << "=" << val_
+ << ";";
}
private:
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
+
std::string name_;
T val_;
-
- DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
};
-/**
- * Specialization for strings so they get the right type and are quoted with "".
- */
+// Specialization for strings so they get the right type and are quoted with "".
template <>
class PrimitiveMember<std::string> : public ClassMember {
public:
@@ -111,10 +109,10 @@
}
private:
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
+
std::string name_;
std::string val_;
-
- DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
};
using IntMember = PrimitiveMember<uint32_t>;
@@ -160,10 +158,10 @@
}
private:
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveArrayMember);
+
std::string name_;
std::vector<T> elements_;
-
- DISALLOW_COPY_AND_ASSIGN(PrimitiveArrayMember);
};
using ResourceArrayMember = PrimitiveArrayMember<ResourceId>;
@@ -185,12 +183,16 @@
}
// Even if the method is empty, we always want to write the method signature.
- bool empty() const override { return false; }
+ bool empty() const override {
+ return false;
+ }
void WriteToStream(const android::StringPiece& prefix, bool final,
std::ostream* out) const override;
private:
+ DISALLOW_COPY_AND_ASSIGN(MethodDefinition);
+
std::string signature_;
std::vector<std::string> statements_;
};
@@ -222,19 +224,13 @@
std::ostream* out) const override;
private:
- struct ClassMemberCompare {
- using T = std::unique_ptr<ClassMember>;
- bool operator()(const T& a, const T& b) const {
- return a->GetName() < b->GetName();
- }
- };
+ DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
std::string name_;
ClassQualifier qualifier_;
bool create_if_empty_;
- std::set<std::unique_ptr<ClassMember>, ClassMemberCompare> members_;
-
- DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
+ std::vector<std::unique_ptr<ClassMember>> ordered_members_;
+ std::unordered_map<android::StringPiece, size_t> indexed_members_;
};
} // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 4f449f0d..668e434 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -24,6 +24,8 @@
using ::android::StringPiece;
using ::testing::HasSubstr;
+using ::testing::Lt;
+using ::testing::Ne;
using ::testing::Not;
namespace aapt {
@@ -306,6 +308,53 @@
EXPECT_THAT(output, HasSubstr(styleable.GetComment()));
}
+TEST(JavaClassGeneratorTest, StyleableAndIndicesAreColocated) {
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .AddValue("android:attr/layout_gravity", util::make_unique<Attribute>())
+ .AddValue("android:attr/background", util::make_unique<Attribute>())
+ .AddValue("android:styleable/ActionBar",
+ test::StyleableBuilder()
+ .AddItem("android:attr/background", ResourceId(0x01010000))
+ .Build())
+ .AddValue("android:styleable/ActionBar.LayoutParams",
+ test::StyleableBuilder()
+ .AddItem("android:attr/layout_gravity", ResourceId(0x01010001))
+ .Build())
+ .Build();
+
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
+
+ JavaClassGeneratorOptions options;
+ JavaClassGenerator generator(context.get(), table.get(), {});
+ std::stringstream out;
+ ASSERT_TRUE(generator.Generate("android", &out));
+ std::string output = out.str();
+
+ std::string::size_type actionbar_pos = output.find("int[] ActionBar");
+ ASSERT_THAT(actionbar_pos, Ne(std::string::npos));
+
+ std::string::size_type actionbar_background_pos = output.find("int ActionBar_background");
+ ASSERT_THAT(actionbar_background_pos, Ne(std::string::npos));
+
+ std::string::size_type actionbar_layout_params_pos = output.find("int[] ActionBar_LayoutParams");
+ ASSERT_THAT(actionbar_layout_params_pos, Ne(std::string::npos));
+
+ std::string::size_type actionbar_layout_params_layout_gravity_pos =
+ output.find("int ActionBar_LayoutParams_layout_gravity");
+ ASSERT_THAT(actionbar_layout_params_layout_gravity_pos, Ne(std::string::npos));
+
+ EXPECT_THAT(actionbar_pos, Lt(actionbar_background_pos));
+ EXPECT_THAT(actionbar_pos, Lt(actionbar_layout_params_pos));
+ EXPECT_THAT(actionbar_background_pos, Lt(actionbar_layout_params_pos));
+ EXPECT_THAT(actionbar_layout_params_pos, Lt(actionbar_layout_params_layout_gravity_pos));
+}
+
TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
Attribute attr(false);
attr.SetComment(StringPiece("removed"));
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 71e828b..ad7d8b6 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -42,9 +42,9 @@
// to the reference object.
//
// NOTE: All of the entries in the ResourceTable must be assigned IDs.
-class ReferenceLinkerVisitor : public ValueVisitor {
+class ReferenceLinkerVisitor : public DescendingValueVisitor {
public:
- using ValueVisitor::Visit;
+ using DescendingValueVisitor::Visit;
ReferenceLinkerVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
StringPool* string_pool, xml::IPackageDeclStack* decl)
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 3b11bee..b0b4945 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -21,7 +21,6 @@
#include "Resource.h"
#include "ResourceValues.h"
-#include "ValueVisitor.h"
#include "link/Linkers.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index 6ebb80f..8852c8e 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -21,6 +21,7 @@
#include "Diagnostics.h"
#include "ResourceUtils.h"
#include "SdkConstants.h"
+#include "ValueVisitor.h"
#include "link/ReferenceLinker.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
@@ -34,9 +35,9 @@
// Visits all references (including parents of styles, references in styles, arrays, etc) and
// links their symbolic name to their Resource ID, performing mangling and package aliasing
// as needed.
-class ReferenceVisitor : public ValueVisitor {
+class ReferenceVisitor : public DescendingValueVisitor {
public:
- using ValueVisitor::Visit;
+ using DescendingValueVisitor::Visit;
ReferenceVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
xml::IPackageDeclStack* decls)
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index 5ff8908..6803088 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -22,22 +22,50 @@
#include "androidfw/StringPiece.h"
#include "LoadedApk.h"
+#include "ResourceUtils.h"
+#include "ValueVisitor.h"
#include "configuration/ConfigurationParser.h"
#include "filter/AbiFilter.h"
#include "filter/Filter.h"
-#include "flatten/Archive.h"
+#include "format/Archive.h"
+#include "format/binary/XmlFlattener.h"
#include "optimize/VersionCollapser.h"
#include "process/IResourceTableConsumer.h"
#include "split/TableSplitter.h"
#include "util/Files.h"
+#include "xml/XmlDom.h"
+#include "xml/XmlUtil.h"
namespace aapt {
using ::aapt::configuration::AndroidSdk;
using ::aapt::configuration::Artifact;
using ::aapt::configuration::PostProcessingConfiguration;
+using ::aapt::xml::kSchemaAndroid;
+using ::aapt::xml::XmlResource;
using ::android::StringPiece;
+namespace {
+
+Maybe<AndroidSdk> GetAndroidSdk(const Artifact& artifact, const PostProcessingConfiguration& config,
+ IDiagnostics* diag) {
+ if (!artifact.android_sdk_group) {
+ return {};
+ }
+
+ const std::string& group_name = artifact.android_sdk_group.value();
+ auto group = config.android_sdk_groups.find(group_name);
+ // TODO: Remove validation when configuration parser ensures referential integrity.
+ if (group == config.android_sdk_groups.end()) {
+ diag->Error(DiagMessage() << "could not find referenced group '" << group_name << "'");
+ return {};
+ }
+
+ return group->second;
+}
+
+} // namespace
+
/**
* Context wrapper that allows the min Android SDK value to be overridden.
*/
@@ -127,6 +155,13 @@
return false;
}
+ std::unique_ptr<XmlResource> manifest;
+ if (!UpdateManifest(artifact, config, &manifest, diag)) {
+ diag->Error(DiagMessage() << "could not update AndroidManifest.xml for "
+ << artifact_name.value());
+ return false;
+ }
+
std::string out = options.out_dir;
if (!file::mkdirs(out)) {
context_->GetDiagnostics()->Warn(DiagMessage() << "could not create out dir: " << out);
@@ -145,7 +180,7 @@
}
if (!apk_->WriteToArchive(context_, table.get(), options.table_flattener_options, &filters,
- writer.get())) {
+ writer.get(), manifest.get())) {
return false;
}
}
@@ -208,37 +243,15 @@
splits.config_filter = &axis_filter;
}
- if (artifact.android_sdk_group) {
- const std::string& group_name = artifact.android_sdk_group.value();
- auto group = config.android_sdk_groups.find(group_name);
- // TODO: Remove validation when configuration parser ensures referential integrity.
- if (group == config.android_sdk_groups.end()) {
- context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced group '"
- << group_name << "'");
- return {};
- }
-
- const AndroidSdk& sdk = group->second;
- if (!sdk.min_sdk_version) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "skipping SDK version. No min SDK: " << group_name);
- return {};
- }
-
- ConfigDescription c;
- const std::string& version = sdk.min_sdk_version.value();
- if (!ConfigDescription::Parse(version, &c)) {
- context_->GetDiagnostics()->Error(DiagMessage() << "could not parse min SDK: " << version);
- return {};
- }
-
- wrappedContext.SetMinSdkVersion(c.sdkVersion);
+ Maybe<AndroidSdk> sdk = GetAndroidSdk(artifact, config, context_->GetDiagnostics());
+ if (sdk && sdk.value().min_sdk_version) {
+ wrappedContext.SetMinSdkVersion(sdk.value().min_sdk_version.value());
}
std::unique_ptr<ResourceTable> table = old_table.Clone();
VersionCollapser collapser;
- if (!collapser.Consume(context_, table.get())) {
+ if (!collapser.Consume(&wrappedContext, table.get())) {
context_->GetDiagnostics()->Error(DiagMessage() << "Failed to strip versioned resources");
return {};
}
@@ -248,4 +261,95 @@
return table;
}
+bool MultiApkGenerator::UpdateManifest(const Artifact& artifact,
+ const PostProcessingConfiguration& config,
+ std::unique_ptr<XmlResource>* updated_manifest,
+ IDiagnostics* diag) {
+ *updated_manifest = apk_->InflateManifest(context_);
+ XmlResource* manifest = updated_manifest->get();
+ if (manifest == nullptr) {
+ return false;
+ }
+
+ // Make sure the first element is <manifest> with package attribute.
+ xml::Element* manifest_el = manifest->root.get();
+ if (manifest_el == nullptr) {
+ return false;
+ }
+
+ if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") {
+ diag->Error(DiagMessage(manifest->file.source) << "root tag must be <manifest>");
+ return false;
+ }
+
+ // Update the versionCode attribute.
+ xml::Attribute* versionCode = manifest_el->FindAttribute(kSchemaAndroid, "versionCode");
+ if (versionCode == nullptr) {
+ diag->Error(DiagMessage(manifest->file.source) << "manifest must have a versionCode attribute");
+ return false;
+ }
+
+ auto* compiled_version = ValueCast<BinaryPrimitive>(versionCode->compiled_value.get());
+ if (compiled_version == nullptr) {
+ diag->Error(DiagMessage(manifest->file.source) << "versionCode is invalid");
+ return false;
+ }
+
+ int new_version = compiled_version->value.data + artifact.version;
+ versionCode->compiled_value = ResourceUtils::TryParseInt(std::to_string(new_version));
+
+ // Check to see if the minSdkVersion needs to be updated.
+ Maybe<AndroidSdk> maybe_sdk = GetAndroidSdk(artifact, config, diag);
+ if (maybe_sdk) {
+ // TODO(safarmer): Handle the rest of the Android SDK.
+ const AndroidSdk& android_sdk = maybe_sdk.value();
+
+ if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
+ if (xml::Attribute* min_sdk_attr =
+ uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
+ // Populate with a pre-compiles attribute to we don't need to relink etc.
+ const std::string& min_sdk_str = std::to_string(android_sdk.min_sdk_version.value());
+ min_sdk_attr->compiled_value = ResourceUtils::TryParseInt(min_sdk_str);
+ } else {
+ // There was no minSdkVersion. This is strange since at this point we should have been
+ // through the manifest fixer which sets the default minSdkVersion.
+ diag->Error(DiagMessage(manifest->file.source) << "missing minSdkVersion from <uses-sdk>");
+ return false;
+ }
+ } else {
+ // No uses-sdk present. This is strange since at this point we should have been
+ // through the manifest fixer which should have added it.
+ diag->Error(DiagMessage(manifest->file.source) << "missing <uses-sdk> from <manifest>");
+ return false;
+ }
+ }
+
+ if (artifact.screen_density_group) {
+ auto densities = config.screen_density_groups.find(artifact.screen_density_group.value());
+ CHECK(densities != config.screen_density_groups.end()) << "Missing density group";
+
+ xml::Element* screens_el = manifest_el->FindChild({}, "compatible-screens");
+ if (!screens_el) {
+ // create a new element.
+ std::unique_ptr<xml::Element> new_screens_el = util::make_unique<xml::Element>();
+ new_screens_el->name = "compatible-screens";
+ screens_el = new_screens_el.get();
+ manifest_el->InsertChild(0, std::move(new_screens_el));
+ } else {
+ // clear out the old element.
+ screens_el->GetChildElements().clear();
+ }
+
+ for (const auto& density : densities->second) {
+ std::unique_ptr<xml::Element> screen_el = util::make_unique<xml::Element>();
+ screen_el->name = "screen";
+ const char* density_str = density.toString().string();
+ screen_el->attributes.push_back(xml::Attribute{kSchemaAndroid, "screenDensity", density_str});
+ screens_el->AppendChild(std::move(screen_el));
+ }
+ }
+
+ return true;
+}
+
} // namespace aapt
diff --git a/tools/aapt2/optimize/MultiApkGenerator.h b/tools/aapt2/optimize/MultiApkGenerator.h
index b064400..e6546ee 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.h
+++ b/tools/aapt2/optimize/MultiApkGenerator.h
@@ -54,6 +54,10 @@
return context_->GetDiagnostics();
}
+ bool UpdateManifest(const configuration::Artifact& artifact,
+ const configuration::PostProcessingConfiguration& config,
+ std::unique_ptr<xml::XmlResource>* updated_manifest, IDiagnostics* diag);
+
LoadedApk* apk_;
IAaptContext* context_;
};
diff --git a/tools/aapt2/optimize/MultiApkGenerator_test.cpp b/tools/aapt2/optimize/MultiApkGenerator_test.cpp
index 23f573c..c8f3524 100644
--- a/tools/aapt2/optimize/MultiApkGenerator_test.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator_test.cpp
@@ -25,8 +25,8 @@
#include "ResourceTable.h"
#include "configuration/ConfigurationParser.h"
#include "filter/Filter.h"
-#include "flatten/Archive.h"
-#include "flatten/TableFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/TableFlattener.h"
#include "process/IResourceTableConsumer.h"
#include "test/Context.h"
#include "test/Test.h"
@@ -50,14 +50,6 @@
using ::testing::Test;
using ::testing::_;
-/** Subclass the LoadedApk class so that we can mock the WriteToArchive method. */
-class MockApk : public LoadedApk {
- public:
- MockApk(std::unique_ptr<ResourceTable> table) : LoadedApk({"test.apk"}, {}, std::move(table)){};
- MOCK_METHOD5(WriteToArchive, bool(IAaptContext*, ResourceTable*, const TableFlattenerOptions&,
- FilterChain*, IArchiveWriter*));
-};
-
/**
* Subclass the MultiApkGenerator class so that we can access the protected FilterTable method to
* directly test table filter.
@@ -111,54 +103,10 @@
ConfigDescription v21_ = ParseConfigOrDie("v21");
};
-TEST_F(MultiApkGeneratorTest, FromBaseApk) {
- std::unique_ptr<ResourceTable> table = BuildTable();
-
- MockApk apk{std::move(table)};
-
- EXPECT_CALL(apk, WriteToArchive(_, _, _, _, _)).Times(0);
-
- test::Context ctx;
- PostProcessingConfiguration empty_config;
- TableFlattenerOptions table_flattener_options;
-
- MultiApkGenerator generator{&apk, &ctx};
- EXPECT_TRUE(generator.FromBaseApk({"out", empty_config, table_flattener_options}));
-
- Artifact x64 = test::ArtifactBuilder()
- .SetName("${basename}.x64.apk")
- .SetAbiGroup("x64")
- .SetLocaleGroup("en")
- .SetDensityGroup("xhdpi")
- .Build();
-
- Artifact intel = test::ArtifactBuilder()
- .SetName("${basename}.intel.apk")
- .SetAbiGroup("intel")
- .SetLocaleGroup("europe")
- .SetDensityGroup("large")
- .Build();
-
- auto config = test::PostProcessingConfigurationBuilder()
- .SetLocaleGroup("en", {"en"})
- .SetLocaleGroup("europe", {"en", "fr", "de", "es"})
- .SetAbiGroup("x64", {Abi::kX86_64})
- .SetAbiGroup("intel", {Abi::kX86_64, Abi::kX86})
- .SetDensityGroup("xhdpi", {"xhdpi"})
- .SetDensityGroup("large", {"xhdpi", "xxhdpi", "xxxhdpi"})
- .AddArtifact(x64)
- .AddArtifact(intel)
- .Build();
-
- // Called once for each artifact.
- EXPECT_CALL(apk, WriteToArchive(Eq(&ctx), _, _, _, _)).Times(2).WillRepeatedly(Return(true));
- EXPECT_TRUE(generator.FromBaseApk({"out", config, table_flattener_options}));
-}
-
TEST_F(MultiApkGeneratorTest, VersionFilterNewerVersion) {
std::unique_ptr<ResourceTable> table = BuildTable();
- MockApk apk{std::move(table)};
+ LoadedApk apk = {{"test.apk"}, {}, std::move(table)};
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(19).Build();
PostProcessingConfiguration empty_config;
TableFlattenerOptions table_flattener_options;
@@ -174,7 +122,7 @@
.SetLocaleGroup("en", {"en"})
.SetAbiGroup("x64", {Abi::kX86_64})
.SetDensityGroup("xhdpi", {"xhdpi"})
- .SetAndroidSdk("v23", AndroidSdk::ForMinSdk("v23"))
+ .SetAndroidSdk("v23", AndroidSdk::ForMinSdk(23))
.AddArtifact(x64)
.Build();
@@ -199,7 +147,7 @@
TEST_F(MultiApkGeneratorTest, VersionFilterOlderVersion) {
std::unique_ptr<ResourceTable> table = BuildTable();
- MockApk apk{std::move(table)};
+ LoadedApk apk = {{"test.apk"}, {}, std::move(table)};
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build();
PostProcessingConfiguration empty_config;
TableFlattenerOptions table_flattener_options;
@@ -215,7 +163,7 @@
.SetLocaleGroup("en", {"en"})
.SetAbiGroup("x64", {Abi::kX86_64})
.SetDensityGroup("xhdpi", {"xhdpi"})
- .SetAndroidSdk("v4", AndroidSdk::ForMinSdk("v4"))
+ .SetAndroidSdk("v4", AndroidSdk::ForMinSdk(4))
.AddArtifact(x64)
.Build();
@@ -238,7 +186,7 @@
TEST_F(MultiApkGeneratorTest, VersionFilterNoVersion) {
std::unique_ptr<ResourceTable> table = BuildTable();
- MockApk apk{std::move(table)};
+ LoadedApk apk = {{"test.apk"}, {}, std::move(table)};
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build();
PostProcessingConfiguration empty_config;
TableFlattenerOptions table_flattener_options;
diff --git a/tools/aapt2/proto/ProtoHelpers.cpp b/tools/aapt2/proto/ProtoHelpers.cpp
deleted file mode 100644
index aa99c98..0000000
--- a/tools/aapt2/proto/ProtoHelpers.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#include "proto/ProtoHelpers.h"
-
-namespace aapt {
-
-void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool) {
- BigBuffer buffer(1024);
- StringPool::FlattenUtf8(&buffer, pool);
-
- std::string* data = out_pb_pool->mutable_data();
- data->reserve(buffer.size());
-
- size_t offset = 0;
- for (const BigBuffer::Block& block : buffer) {
- data->insert(data->begin() + offset, block.buffer.get(), block.buffer.get() + block.size);
- offset += block.size;
- }
-}
-
-void SerializeSourceToPb(const Source& source, StringPool* src_pool, pb::Source* out_pb_source) {
- StringPool::Ref ref = src_pool->MakeRef(source.path);
- out_pb_source->set_path_idx(static_cast<uint32_t>(ref.index()));
- if (source.line) {
- out_pb_source->mutable_position()->set_line_number(static_cast<uint32_t>(source.line.value()));
- }
-}
-
-void DeserializeSourceFromPb(const pb::Source& pb_source, const android::ResStringPool& src_pool,
- Source* out_source) {
- if (pb_source.has_path_idx()) {
- out_source->path = util::GetString(src_pool, pb_source.path_idx());
- }
-
- if (pb_source.has_position()) {
- out_source->line = static_cast<size_t>(pb_source.position().line_number());
- }
-}
-
-pb::SymbolStatus_Visibility SerializeVisibilityToPb(SymbolState state) {
- switch (state) {
- case SymbolState::kPrivate:
- return pb::SymbolStatus_Visibility_PRIVATE;
- case SymbolState::kPublic:
- return pb::SymbolStatus_Visibility_PUBLIC;
- default:
- break;
- }
- return pb::SymbolStatus_Visibility_UNKNOWN;
-}
-
-SymbolState DeserializeVisibilityFromPb(pb::SymbolStatus_Visibility pb_visibility) {
- switch (pb_visibility) {
- case pb::SymbolStatus_Visibility_PRIVATE:
- return SymbolState::kPrivate;
- case pb::SymbolStatus_Visibility_PUBLIC:
- return SymbolState::kPublic;
- default:
- break;
- }
- return SymbolState::kUndefined;
-}
-
-void SerializeConfig(const ConfigDescription& config, pb::ConfigDescription* out_pb_config) {
- android::ResTable_config flat_config = config;
- flat_config.size = sizeof(flat_config);
- flat_config.swapHtoD();
- out_pb_config->set_data(&flat_config, sizeof(flat_config));
-}
-
-bool DeserializeConfigDescriptionFromPb(const pb::ConfigDescription& pb_config,
- ConfigDescription* out_config) {
- if (!pb_config.has_data()) {
- return false;
- }
-
- const android::ResTable_config* config;
- if (pb_config.data().size() > sizeof(*config)) {
- return false;
- }
-
- config = reinterpret_cast<const android::ResTable_config*>(pb_config.data().data());
- out_config->copyFromDtoH(*config);
- return true;
-}
-
-pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type) {
- switch (type) {
- case Reference::Type::kResource:
- return pb::Reference_Type_REFERENCE;
- case Reference::Type::kAttribute:
- return pb::Reference_Type_ATTRIBUTE;
- default:
- break;
- }
- return pb::Reference_Type_REFERENCE;
-}
-
-Reference::Type DeserializeReferenceTypeFromPb(pb::Reference_Type pb_type) {
- switch (pb_type) {
- case pb::Reference_Type_REFERENCE:
- return Reference::Type::kResource;
- case pb::Reference_Type_ATTRIBUTE:
- return Reference::Type::kAttribute;
- default:
- break;
- }
- return Reference::Type::kResource;
-}
-
-pb::Plural_Arity SerializePluralEnumToPb(size_t plural_idx) {
- switch (plural_idx) {
- case Plural::Zero:
- return pb::Plural_Arity_ZERO;
- case Plural::One:
- return pb::Plural_Arity_ONE;
- case Plural::Two:
- return pb::Plural_Arity_TWO;
- case Plural::Few:
- return pb::Plural_Arity_FEW;
- case Plural::Many:
- return pb::Plural_Arity_MANY;
- default:
- break;
- }
- return pb::Plural_Arity_OTHER;
-}
-
-size_t DeserializePluralEnumFromPb(pb::Plural_Arity arity) {
- switch (arity) {
- case pb::Plural_Arity_ZERO:
- return Plural::Zero;
- case pb::Plural_Arity_ONE:
- return Plural::One;
- case pb::Plural_Arity_TWO:
- return Plural::Two;
- case pb::Plural_Arity_FEW:
- return Plural::Few;
- case pb::Plural_Arity_MANY:
- return Plural::Many;
- default:
- break;
- }
- return Plural::Other;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/proto/ProtoHelpers.h b/tools/aapt2/proto/ProtoHelpers.h
deleted file mode 100644
index 2f268f4..0000000
--- a/tools/aapt2/proto/ProtoHelpers.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#ifndef AAPT_PROTO_PROTOHELPERS_H
-#define AAPT_PROTO_PROTOHELPERS_H
-
-#include "androidfw/ResourceTypes.h"
-
-#include "ConfigDescription.h"
-#include "ResourceTable.h"
-#include "Source.h"
-#include "StringPool.h"
-#include "Resources.pb.h"
-#include "ResourcesInternal.pb.h"
-
-namespace aapt {
-
-void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool);
-
-void SerializeSourceToPb(const Source& source, StringPool* src_pool, pb::Source* out_pb_source);
-
-void DeserializeSourceFromPb(const pb::Source& pb_source, const android::ResStringPool& src_pool,
- Source* out_source);
-
-pb::SymbolStatus_Visibility SerializeVisibilityToPb(SymbolState state);
-
-SymbolState DeserializeVisibilityFromPb(pb::SymbolStatus_Visibility pb_visibility);
-
-void SerializeConfig(const ConfigDescription& config, pb::ConfigDescription* out_pb_config);
-
-bool DeserializeConfigDescriptionFromPb(const pb::ConfigDescription& pb_config,
- ConfigDescription* out_config);
-
-pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type);
-
-Reference::Type DeserializeReferenceTypeFromPb(pb::Reference_Type pb_type);
-
-pb::Plural_Arity SerializePluralEnumToPb(size_t plural_idx);
-
-size_t DeserializePluralEnumFromPb(pb::Plural_Arity arity);
-
-} // namespace aapt
-
-#endif /* AAPT_PROTO_PROTOHELPERS_H */
diff --git a/tools/aapt2/proto/ProtoSerialize.h b/tools/aapt2/proto/ProtoSerialize.h
deleted file mode 100644
index 8c46642..0000000
--- a/tools/aapt2/proto/ProtoSerialize.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#ifndef AAPT_FLATTEN_TABLEPROTOSERIALIZER_H
-#define AAPT_FLATTEN_TABLEPROTOSERIALIZER_H
-
-#include "android-base/macros.h"
-#include "google/protobuf/io/coded_stream.h"
-#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
-
-#include "Diagnostics.h"
-#include "ResourceTable.h"
-#include "Source.h"
-#include "proto/ProtoHelpers.h"
-
-namespace aapt {
-
-class CompiledFileOutputStream {
- public:
- explicit CompiledFileOutputStream(google::protobuf::io::ZeroCopyOutputStream* out);
-
- void WriteLittleEndian32(uint32_t value);
- void WriteCompiledFile(const pb::internal::CompiledFile* compiledFile);
- void WriteData(const BigBuffer* buffer);
- void WriteData(const void* data, size_t len);
- bool HadError();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CompiledFileOutputStream);
-
- void EnsureAlignedWrite();
-
- google::protobuf::io::CodedOutputStream out_;
-};
-
-class CompiledFileInputStream {
- public:
- explicit CompiledFileInputStream(const void* data, size_t size);
-
- bool ReadLittleEndian32(uint32_t* outVal);
- bool ReadCompiledFile(pb::internal::CompiledFile* outVal);
- bool ReadDataMetaData(uint64_t* outOffset, uint64_t* outLen);
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CompiledFileInputStream);
-
- void EnsureAlignedRead();
-
- google::protobuf::io::CodedInputStream in_;
-};
-
-std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table);
-std::unique_ptr<ResourceTable> DeserializeTableFromPb(const pb::ResourceTable& pbTable,
- const Source& source, IDiagnostics* diag);
-
-std::unique_ptr<pb::internal::CompiledFile> SerializeCompiledFileToPb(const ResourceFile& file);
-std::unique_ptr<ResourceFile> DeserializeCompiledFileFromPb(
- const pb::internal::CompiledFile& pbFile, const Source& source, IDiagnostics* diag);
-
-} // namespace aapt
-
-#endif /* AAPT_FLATTEN_TABLEPROTOSERIALIZER_H */
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
deleted file mode 100644
index b9d5878..0000000
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#include "proto/ProtoSerialize.h"
-
-#include "android-base/logging.h"
-#include "androidfw/ResourceTypes.h"
-
-#include "ResourceTable.h"
-#include "ResourceUtils.h"
-#include "ValueVisitor.h"
-#include "proto/ProtoHelpers.h"
-
-namespace aapt {
-
-namespace {
-
-class ReferenceIdToNameVisitor : public ValueVisitor {
- public:
- using ValueVisitor::Visit;
-
- explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping)
- : mapping_(mapping) {
- CHECK(mapping_ != nullptr);
- }
-
- void Visit(Reference* reference) override {
- if (!reference->id || !reference->id.value().is_valid()) {
- return;
- }
-
- ResourceId id = reference->id.value();
- auto cache_iter = mapping_->find(id);
- if (cache_iter != mapping_->end()) {
- reference->name = cache_iter->second.ToResourceName();
- }
- }
-
- private:
- const std::map<ResourceId, ResourceNameRef>* mapping_;
-};
-
-class PackagePbDeserializer {
- public:
- PackagePbDeserializer(const android::ResStringPool* sourcePool, const Source& source,
- IDiagnostics* diag)
- : source_pool_(sourcePool), source_(source), diag_(diag) {
- }
-
- public:
- bool DeserializeFromPb(const pb::Package& pb_package, ResourceTable* table) {
- Maybe<uint8_t> id;
- if (pb_package.has_package_id()) {
- id = static_cast<uint8_t>(pb_package.package_id());
- }
-
- std::map<ResourceId, ResourceNameRef> id_index;
-
- ResourceTablePackage* pkg = table->CreatePackage(pb_package.package_name(), id);
- for (const pb::Type& pb_type : pb_package.type()) {
- const ResourceType* res_type = ParseResourceType(pb_type.name());
- if (res_type == nullptr) {
- diag_->Error(DiagMessage(source_) << "unknown type '" << pb_type.name() << "'");
- return {};
- }
-
- ResourceTableType* type = pkg->FindOrCreateType(*res_type);
-
- for (const pb::Entry& pb_entry : pb_type.entry()) {
- ResourceEntry* entry = type->FindOrCreateEntry(pb_entry.name());
-
- // Deserialize the symbol status (public/private with source and comments).
- if (pb_entry.has_symbol_status()) {
- const pb::SymbolStatus& pb_status = pb_entry.symbol_status();
- if (pb_status.has_source()) {
- DeserializeSourceFromPb(pb_status.source(), *source_pool_,
- &entry->symbol_status.source);
- }
-
- if (pb_status.has_comment()) {
- entry->symbol_status.comment = pb_status.comment();
- }
-
- entry->symbol_status.allow_new = pb_status.allow_new();
-
- SymbolState visibility = DeserializeVisibilityFromPb(pb_status.visibility());
- entry->symbol_status.state = visibility;
-
- if (visibility == SymbolState::kPublic) {
- // This is a public symbol, we must encode the ID now if there is one.
- if (pb_entry.has_id()) {
- entry->id = static_cast<uint16_t>(pb_entry.id());
- }
-
- if (type->symbol_status.state != SymbolState::kPublic) {
- // If the type has not been made public, do so now.
- type->symbol_status.state = SymbolState::kPublic;
- if (pb_type.has_id()) {
- type->id = static_cast<uint8_t>(pb_type.id());
- }
- }
- } else if (visibility == SymbolState::kPrivate) {
- if (type->symbol_status.state == SymbolState::kUndefined) {
- type->symbol_status.state = SymbolState::kPrivate;
- }
- }
- }
-
- ResourceId resid(pb_package.package_id(), pb_type.id(), pb_entry.id());
- if (resid.is_valid()) {
- id_index[resid] = ResourceNameRef(pkg->name, type->type, entry->name);
- }
-
- for (const pb::ConfigValue& pb_config_value : pb_entry.config_value()) {
- const pb::ConfigDescription& pb_config = pb_config_value.config();
-
- ConfigDescription config;
- if (!DeserializeConfigDescriptionFromPb(pb_config, &config)) {
- diag_->Error(DiagMessage(source_) << "invalid configuration");
- return {};
- }
-
- ResourceConfigValue* config_value = entry->FindOrCreateValue(config, pb_config.product());
- if (config_value->value) {
- // Duplicate config.
- diag_->Error(DiagMessage(source_) << "duplicate configuration");
- return {};
- }
-
- config_value->value =
- DeserializeValueFromPb(pb_config_value.value(), config, &table->string_pool);
- if (!config_value->value) {
- return {};
- }
- }
- }
- }
-
- ReferenceIdToNameVisitor visitor(&id_index);
- VisitAllValuesInPackage(pkg, &visitor);
- return true;
- }
-
- private:
- std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
- const ConfigDescription& config, StringPool* pool) {
- if (pb_item.has_ref()) {
- const pb::Reference& pb_ref = pb_item.ref();
- std::unique_ptr<Reference> ref = util::make_unique<Reference>();
- if (!DeserializeReferenceFromPb(pb_ref, ref.get())) {
- return {};
- }
- return std::move(ref);
-
- } else if (pb_item.has_prim()) {
- const pb::Primitive& pb_prim = pb_item.prim();
- return util::make_unique<BinaryPrimitive>(static_cast<uint8_t>(pb_prim.type()),
- pb_prim.data());
-
- } else if (pb_item.has_id()) {
- return util::make_unique<Id>();
-
- } else if (pb_item.has_str()) {
- return util::make_unique<String>(
- pool->MakeRef(pb_item.str().value(), StringPool::Context(config)));
-
- } else if (pb_item.has_raw_str()) {
- return util::make_unique<RawString>(
- pool->MakeRef(pb_item.raw_str().value(), StringPool::Context(config)));
-
- } else if (pb_item.has_styled_str()) {
- const pb::StyledString& pb_str = pb_item.styled_str();
- StyleString style_str{pb_str.value()};
- for (const pb::StyledString::Span& pb_span : pb_str.span()) {
- style_str.spans.push_back(Span{pb_span.tag(), pb_span.first_char(), pb_span.last_char()});
- }
- return util::make_unique<StyledString>(pool->MakeRef(
- style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
-
- } else if (pb_item.has_file()) {
- return util::make_unique<FileReference>(pool->MakeRef(
- pb_item.file().path(), StringPool::Context(StringPool::Context::kHighPriority, config)));
-
- } else {
- diag_->Error(DiagMessage(source_) << "unknown item");
- }
- return {};
- }
-
- std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
- const ConfigDescription& config,
- StringPool* pool) {
- std::unique_ptr<Value> value;
- if (pb_value.has_item()) {
- value = DeserializeItemFromPb(pb_value.item(), config, pool);
- if (!value) {
- return {};
- }
-
- } else if (pb_value.has_compound_value()) {
- const pb::CompoundValue& pb_compound_value = pb_value.compound_value();
- if (pb_compound_value.has_attr()) {
- const pb::Attribute& pb_attr = pb_compound_value.attr();
- std::unique_ptr<Attribute> attr = util::make_unique<Attribute>();
- attr->type_mask = pb_attr.format_flags();
- attr->min_int = pb_attr.min_int();
- attr->max_int = pb_attr.max_int();
- for (const pb::Attribute_Symbol& pb_symbol : pb_attr.symbol()) {
- Attribute::Symbol symbol;
- DeserializeItemCommon(pb_symbol, &symbol.symbol);
- if (!DeserializeReferenceFromPb(pb_symbol.name(), &symbol.symbol)) {
- return {};
- }
- symbol.value = pb_symbol.value();
- attr->symbols.push_back(std::move(symbol));
- }
- value = std::move(attr);
-
- } else if (pb_compound_value.has_style()) {
- const pb::Style& pb_style = pb_compound_value.style();
- std::unique_ptr<Style> style = util::make_unique<Style>();
- if (pb_style.has_parent()) {
- style->parent = Reference();
- if (!DeserializeReferenceFromPb(pb_style.parent(), &style->parent.value())) {
- return {};
- }
-
- if (pb_style.has_parent_source()) {
- Source parent_source;
- DeserializeSourceFromPb(pb_style.parent_source(), *source_pool_, &parent_source);
- style->parent.value().SetSource(std::move(parent_source));
- }
- }
-
- for (const pb::Style_Entry& pb_entry : pb_style.entry()) {
- Style::Entry entry;
- DeserializeItemCommon(pb_entry, &entry.key);
- if (!DeserializeReferenceFromPb(pb_entry.key(), &entry.key)) {
- return {};
- }
-
- entry.value = DeserializeItemFromPb(pb_entry.item(), config, pool);
- if (!entry.value) {
- return {};
- }
-
- DeserializeItemCommon(pb_entry, entry.value.get());
- style->entries.push_back(std::move(entry));
- }
- value = std::move(style);
-
- } else if (pb_compound_value.has_styleable()) {
- const pb::Styleable& pb_styleable = pb_compound_value.styleable();
- std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
- for (const pb::Styleable_Entry& pb_entry : pb_styleable.entry()) {
- Reference attr_ref;
- DeserializeItemCommon(pb_entry, &attr_ref);
- DeserializeReferenceFromPb(pb_entry.attr(), &attr_ref);
- styleable->entries.push_back(std::move(attr_ref));
- }
- value = std::move(styleable);
-
- } else if (pb_compound_value.has_array()) {
- const pb::Array& pb_array = pb_compound_value.array();
- std::unique_ptr<Array> array = util::make_unique<Array>();
- for (const pb::Array_Element& pb_entry : pb_array.element()) {
- std::unique_ptr<Item> item = DeserializeItemFromPb(pb_entry.item(), config, pool);
- if (!item) {
- return {};
- }
-
- DeserializeItemCommon(pb_entry, item.get());
- array->elements.push_back(std::move(item));
- }
- value = std::move(array);
-
- } else if (pb_compound_value.has_plural()) {
- const pb::Plural& pb_plural = pb_compound_value.plural();
- std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- for (const pb::Plural_Entry& pb_entry : pb_plural.entry()) {
- size_t pluralIdx = DeserializePluralEnumFromPb(pb_entry.arity());
- plural->values[pluralIdx] = DeserializeItemFromPb(pb_entry.item(), config, pool);
- if (!plural->values[pluralIdx]) {
- return {};
- }
-
- DeserializeItemCommon(pb_entry, plural->values[pluralIdx].get());
- }
- value = std::move(plural);
-
- } else {
- diag_->Error(DiagMessage(source_) << "unknown compound value");
- return {};
- }
- } else {
- diag_->Error(DiagMessage(source_) << "unknown value");
- return {};
- }
-
- CHECK(value) << "forgot to set value";
-
- value->SetWeak(pb_value.weak());
- DeserializeItemCommon(pb_value, value.get());
- return value;
- }
-
- bool DeserializeReferenceFromPb(const pb::Reference& pb_ref, Reference* out_ref) {
- out_ref->reference_type = DeserializeReferenceTypeFromPb(pb_ref.type());
- out_ref->private_reference = pb_ref.private_();
-
- if (pb_ref.has_id()) {
- out_ref->id = ResourceId(pb_ref.id());
- }
-
- if (pb_ref.has_name()) {
- ResourceNameRef name_ref;
- if (!ResourceUtils::ParseResourceName(pb_ref.name(), &name_ref, nullptr)) {
- diag_->Error(DiagMessage(source_) << "invalid reference name '" << pb_ref.name() << "'");
- return false;
- }
-
- out_ref->name = name_ref.ToResourceName();
- }
- return true;
- }
-
- template <typename T>
- void DeserializeItemCommon(const T& pb_item, Value* out_value) {
- if (pb_item.has_source()) {
- Source source;
- DeserializeSourceFromPb(pb_item.source(), *source_pool_, &source);
- out_value->SetSource(std::move(source));
- }
-
- if (pb_item.has_comment()) {
- out_value->SetComment(pb_item.comment());
- }
- }
-
- private:
- const android::ResStringPool* source_pool_;
- const Source source_;
- IDiagnostics* diag_;
-};
-
-} // namespace
-
-std::unique_ptr<ResourceTable> DeserializeTableFromPb(const pb::ResourceTable& pb_table,
- const Source& source, IDiagnostics* diag) {
- // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
- // causes errors when qualifying it with android::
- using namespace android;
-
- std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
-
- ResStringPool source_pool;
- if (pb_table.has_source_pool()) {
- status_t result = source_pool.setTo(pb_table.source_pool().data().data(),
- pb_table.source_pool().data().size());
- if (result != NO_ERROR) {
- diag->Error(DiagMessage(source) << "invalid source pool");
- return {};
- }
- }
-
- PackagePbDeserializer package_pb_deserializer(&source_pool, source, diag);
- for (const pb::Package& pb_package : pb_table.package()) {
- if (!package_pb_deserializer.DeserializeFromPb(pb_package, table.get())) {
- return {};
- }
- }
- return table;
-}
-
-std::unique_ptr<ResourceFile> DeserializeCompiledFileFromPb(
- const pb::internal::CompiledFile& pb_file, const Source& source, IDiagnostics* diag) {
- std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
-
- ResourceNameRef name_ref;
-
- // Need to create an lvalue here so that nameRef can point to something real.
- if (!ResourceUtils::ParseResourceName(pb_file.resource_name(), &name_ref)) {
- diag->Error(DiagMessage(source)
- << "invalid resource name in compiled file header: "
- << pb_file.resource_name());
- return {};
- }
- file->name = name_ref.ToResourceName();
- file->source.path = pb_file.source_path();
- DeserializeConfigDescriptionFromPb(pb_file.config(), &file->config);
-
- for (const pb::internal::CompiledFile_Symbol& pb_symbol : pb_file.exported_symbol()) {
- // Need to create an lvalue here so that nameRef can point to something real.
- if (!ResourceUtils::ParseResourceName(pb_symbol.resource_name(), &name_ref)) {
- diag->Error(DiagMessage(source)
- << "invalid resource name for exported symbol in "
- "compiled file header: "
- << pb_file.resource_name());
- return {};
- }
- size_t line = 0u;
- if (pb_symbol.has_source()) {
- line = pb_symbol.source().line_number();
- }
- file->exported_symbols.push_back(SourcedResourceName{name_ref.ToResourceName(), line});
- }
- return file;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
deleted file mode 100644
index a08df71..0000000
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#include "Resource.h"
-#include "ResourceTable.h"
-#include "StringPool.h"
-#include "ValueVisitor.h"
-#include "proto/ProtoHelpers.h"
-#include "proto/ProtoSerialize.h"
-#include "util/BigBuffer.h"
-
-#include "android-base/logging.h"
-
-using ::google::protobuf::io::CodedInputStream;
-using ::google::protobuf::io::CodedOutputStream;
-using ::google::protobuf::io::ZeroCopyOutputStream;
-
-namespace aapt {
-
-namespace {
-
-class PbSerializerVisitor : public RawValueVisitor {
- public:
- using RawValueVisitor::Visit;
-
- // Constructor to use when expecting to serialize any value.
- PbSerializerVisitor(StringPool* source_pool, pb::Value* out_pb_value)
- : source_pool_(source_pool), out_pb_value_(out_pb_value), out_pb_item_(nullptr) {
- }
-
- // Constructor to use when expecting to serialize an Item.
- PbSerializerVisitor(StringPool* sourcePool, pb::Item* outPbItem)
- : source_pool_(sourcePool), out_pb_value_(nullptr), out_pb_item_(outPbItem) {
- }
-
- void Visit(Reference* ref) override {
- SerializeReferenceToPb(*ref, pb_item()->mutable_ref());
- }
-
- void Visit(String* str) override {
- pb_item()->mutable_str()->set_value(*str->value);
- }
-
- void Visit(RawString* str) override {
- pb_item()->mutable_raw_str()->set_value(*str->value);
- }
-
- void Visit(StyledString* str) override {
- pb::StyledString* pb_str = pb_item()->mutable_styled_str();
- pb_str->set_value(str->value->value);
-
- for (const StringPool::Span& span : str->value->spans) {
- pb::StyledString::Span* pb_span = pb_str->add_span();
- pb_span->set_tag(*span.name);
- pb_span->set_first_char(span.first_char);
- pb_span->set_last_char(span.last_char);
- }
- }
-
- void Visit(FileReference* file) override {
- pb_item()->mutable_file()->set_path(*file->path);
- }
-
- void Visit(Id* /*id*/) override {
- pb_item()->mutable_id();
- }
-
- void Visit(BinaryPrimitive* prim) override {
- android::Res_value val = {};
- prim->Flatten(&val);
-
- pb::Primitive* pb_prim = pb_item()->mutable_prim();
- pb_prim->set_type(val.dataType);
- pb_prim->set_data(val.data);
- }
-
- void VisitItem(Item* item) override {
- LOG(FATAL) << "unimplemented item";
- }
-
- void Visit(Attribute* attr) override {
- pb::Attribute* pb_attr = pb_compound_value()->mutable_attr();
- pb_attr->set_format_flags(attr->type_mask);
- pb_attr->set_min_int(attr->min_int);
- pb_attr->set_max_int(attr->max_int);
-
- for (auto& symbol : attr->symbols) {
- pb::Attribute_Symbol* pb_symbol = pb_attr->add_symbol();
- SerializeItemCommonToPb(symbol.symbol, pb_symbol);
- SerializeReferenceToPb(symbol.symbol, pb_symbol->mutable_name());
- pb_symbol->set_value(symbol.value);
- }
- }
-
- void Visit(Style* style) override {
- pb::Style* pb_style = pb_compound_value()->mutable_style();
- if (style->parent) {
- SerializeReferenceToPb(style->parent.value(), pb_style->mutable_parent());
- SerializeSourceToPb(style->parent.value().GetSource(), source_pool_,
- pb_style->mutable_parent_source());
- }
-
- for (Style::Entry& entry : style->entries) {
- pb::Style_Entry* pb_entry = pb_style->add_entry();
- SerializeReferenceToPb(entry.key, pb_entry->mutable_key());
-
- pb::Item* pb_item = pb_entry->mutable_item();
- SerializeItemCommonToPb(entry.key, pb_entry);
- PbSerializerVisitor sub_visitor(source_pool_, pb_item);
- entry.value->Accept(&sub_visitor);
- }
- }
-
- void Visit(Styleable* styleable) override {
- pb::Styleable* pb_styleable = pb_compound_value()->mutable_styleable();
- for (Reference& entry : styleable->entries) {
- pb::Styleable_Entry* pb_entry = pb_styleable->add_entry();
- SerializeItemCommonToPb(entry, pb_entry);
- SerializeReferenceToPb(entry, pb_entry->mutable_attr());
- }
- }
-
- void Visit(Array* array) override {
- pb::Array* pb_array = pb_compound_value()->mutable_array();
- for (auto& value : array->elements) {
- pb::Array_Element* pb_element = pb_array->add_element();
- SerializeItemCommonToPb(*value, pb_element);
- PbSerializerVisitor sub_visitor(source_pool_, pb_element->mutable_item());
- value->Accept(&sub_visitor);
- }
- }
-
- void Visit(Plural* plural) override {
- pb::Plural* pb_plural = pb_compound_value()->mutable_plural();
- const size_t count = plural->values.size();
- for (size_t i = 0; i < count; i++) {
- if (!plural->values[i]) {
- // No plural value set here.
- continue;
- }
-
- pb::Plural_Entry* pb_entry = pb_plural->add_entry();
- pb_entry->set_arity(SerializePluralEnumToPb(i));
- pb::Item* pb_element = pb_entry->mutable_item();
- SerializeItemCommonToPb(*plural->values[i], pb_entry);
- PbSerializerVisitor sub_visitor(source_pool_, pb_element);
- plural->values[i]->Accept(&sub_visitor);
- }
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(PbSerializerVisitor);
-
- pb::Item* pb_item() {
- if (out_pb_value_) {
- return out_pb_value_->mutable_item();
- }
- return out_pb_item_;
- }
-
- pb::CompoundValue* pb_compound_value() {
- CHECK(out_pb_value_ != nullptr);
- return out_pb_value_->mutable_compound_value();
- }
-
- template <typename T>
- void SerializeItemCommonToPb(const Item& item, T* pb_item) {
- SerializeSourceToPb(item.GetSource(), source_pool_, pb_item->mutable_source());
- if (!item.GetComment().empty()) {
- pb_item->set_comment(item.GetComment());
- }
- }
-
- void SerializeReferenceToPb(const Reference& ref, pb::Reference* pb_ref) {
- if (ref.id) {
- pb_ref->set_id(ref.id.value().id);
- }
-
- if (ref.name) {
- pb_ref->set_name(ref.name.value().ToString());
- }
-
- pb_ref->set_private_(ref.private_reference);
- pb_ref->set_type(SerializeReferenceTypeToPb(ref.reference_type));
- }
-
- StringPool* source_pool_;
- pb::Value* out_pb_value_;
- pb::Item* out_pb_item_;
-};
-
-} // namespace
-
-std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) {
- // We must do this before writing the resources, since the string pool IDs may change.
- table->string_pool.Prune();
- table->string_pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int {
- int diff = util::compare(a.priority, b.priority);
- if (diff == 0) {
- diff = a.config.compare(b.config);
- }
- return diff;
- });
-
- auto pb_table = util::make_unique<pb::ResourceTable>();
- StringPool source_pool;
-
- for (auto& package : table->packages) {
- pb::Package* pb_package = pb_table->add_package();
- if (package->id) {
- pb_package->set_package_id(package->id.value());
- }
- pb_package->set_package_name(package->name);
-
- for (auto& type : package->types) {
- pb::Type* pb_type = pb_package->add_type();
- if (type->id) {
- pb_type->set_id(type->id.value());
- }
- pb_type->set_name(ToString(type->type).to_string());
-
- for (auto& entry : type->entries) {
- pb::Entry* pb_entry = pb_type->add_entry();
- if (entry->id) {
- pb_entry->set_id(entry->id.value());
- }
- pb_entry->set_name(entry->name);
-
- // Write the SymbolStatus struct.
- pb::SymbolStatus* pb_status = pb_entry->mutable_symbol_status();
- pb_status->set_visibility(SerializeVisibilityToPb(entry->symbol_status.state));
- SerializeSourceToPb(entry->symbol_status.source, &source_pool, pb_status->mutable_source());
- pb_status->set_comment(entry->symbol_status.comment);
- pb_status->set_allow_new(entry->symbol_status.allow_new);
-
- for (auto& config_value : entry->values) {
- pb::ConfigValue* pb_config_value = pb_entry->add_config_value();
- SerializeConfig(config_value->config, pb_config_value->mutable_config());
- if (!config_value->product.empty()) {
- pb_config_value->mutable_config()->set_product(config_value->product);
- }
-
- pb::Value* pb_value = pb_config_value->mutable_value();
- SerializeSourceToPb(config_value->value->GetSource(), &source_pool,
- pb_value->mutable_source());
- if (!config_value->value->GetComment().empty()) {
- pb_value->set_comment(config_value->value->GetComment());
- }
-
- if (config_value->value->IsWeak()) {
- pb_value->set_weak(true);
- }
-
- PbSerializerVisitor visitor(&source_pool, pb_value);
- config_value->value->Accept(&visitor);
- }
- }
- }
- }
-
- SerializeStringPoolToPb(source_pool, pb_table->mutable_source_pool());
- return pb_table;
-}
-
-std::unique_ptr<pb::internal::CompiledFile> SerializeCompiledFileToPb(const ResourceFile& file) {
- auto pb_file = util::make_unique<pb::internal::CompiledFile>();
- pb_file->set_resource_name(file.name.ToString());
- pb_file->set_source_path(file.source.path);
- SerializeConfig(file.config, pb_file->mutable_config());
-
- for (const SourcedResourceName& exported : file.exported_symbols) {
- pb::internal::CompiledFile_Symbol* pb_symbol = pb_file->add_exported_symbol();
- pb_symbol->set_resource_name(exported.name.ToString());
- pb_symbol->mutable_source()->set_line_number(exported.line);
- }
- return pb_file;
-}
-
-CompiledFileOutputStream::CompiledFileOutputStream(ZeroCopyOutputStream* out) : out_(out) {
-}
-
-void CompiledFileOutputStream::EnsureAlignedWrite() {
- const int padding = out_.ByteCount() % 4;
- if (padding > 0) {
- uint32_t zero = 0u;
- out_.WriteRaw(&zero, padding);
- }
-}
-
-void CompiledFileOutputStream::WriteLittleEndian32(uint32_t val) {
- EnsureAlignedWrite();
- out_.WriteLittleEndian32(val);
-}
-
-void CompiledFileOutputStream::WriteCompiledFile(const pb::internal::CompiledFile* compiled_file) {
- EnsureAlignedWrite();
- out_.WriteLittleEndian64(static_cast<uint64_t>(compiled_file->ByteSize()));
- compiled_file->SerializeWithCachedSizes(&out_);
-}
-
-void CompiledFileOutputStream::WriteData(const BigBuffer* buffer) {
- EnsureAlignedWrite();
- out_.WriteLittleEndian64(static_cast<uint64_t>(buffer->size()));
- for (const BigBuffer::Block& block : *buffer) {
- out_.WriteRaw(block.buffer.get(), block.size);
- }
-}
-
-void CompiledFileOutputStream::WriteData(const void* data, size_t len) {
- EnsureAlignedWrite();
- out_.WriteLittleEndian64(static_cast<uint64_t>(len));
- out_.WriteRaw(data, len);
-}
-
-bool CompiledFileOutputStream::HadError() {
- return out_.HadError();
-}
-
-CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size)
- : in_(static_cast<const uint8_t*>(data), size) {}
-
-void CompiledFileInputStream::EnsureAlignedRead() {
- const int padding = in_.CurrentPosition() % 4;
- if (padding > 0) {
- // Reads are always 4 byte aligned.
- in_.Skip(padding);
- }
-}
-
-bool CompiledFileInputStream::ReadLittleEndian32(uint32_t* out_val) {
- EnsureAlignedRead();
- return in_.ReadLittleEndian32(out_val);
-}
-
-bool CompiledFileInputStream::ReadCompiledFile(pb::internal::CompiledFile* out_val) {
- EnsureAlignedRead();
-
- google::protobuf::uint64 pb_size = 0u;
- if (!in_.ReadLittleEndian64(&pb_size)) {
- return false;
- }
-
- CodedInputStream::Limit l = in_.PushLimit(static_cast<int>(pb_size));
-
- // Check that we haven't tried to read past the end.
- if (static_cast<uint64_t>(in_.BytesUntilLimit()) != pb_size) {
- in_.PopLimit(l);
- in_.PushLimit(0);
- return false;
- }
-
- if (!out_val->ParsePartialFromCodedStream(&in_)) {
- in_.PopLimit(l);
- in_.PushLimit(0);
- return false;
- }
-
- in_.PopLimit(l);
- return true;
-}
-
-bool CompiledFileInputStream::ReadDataMetaData(uint64_t* out_offset, uint64_t* out_len) {
- EnsureAlignedRead();
-
- google::protobuf::uint64 pb_size = 0u;
- if (!in_.ReadLittleEndian64(&pb_size)) {
- return false;
- }
-
- // Check that we aren't trying to read past the end.
- if (pb_size > static_cast<uint64_t>(in_.BytesUntilLimit())) {
- in_.PushLimit(0);
- return false;
- }
-
- uint64_t offset = static_cast<uint64_t>(in_.CurrentPosition());
- if (!in_.Skip(pb_size)) {
- return false;
- }
-
- *out_offset = offset;
- *out_len = pb_size;
- return true;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp
deleted file mode 100644
index 80608b3..0000000
--- a/tools/aapt2/proto/TableProtoSerializer_test.cpp
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#include "proto/ProtoSerialize.h"
-
-#include "ResourceTable.h"
-#include "test/Test.h"
-
-using ::google::protobuf::io::StringOutputStream;
-using ::testing::Eq;
-using ::testing::NotNull;
-using ::testing::SizeIs;
-
-namespace aapt {
-
-TEST(TableProtoSerializer, SerializeSinglePackage) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- std::unique_ptr<ResourceTable> table =
- test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
- .AddFileReference("com.app.a:layout/main", ResourceId(0x7f020000), "res/layout/main.xml")
- .AddReference("com.app.a:layout/other", ResourceId(0x7f020001), "com.app.a:layout/main")
- .AddString("com.app.a:string/text", {}, "hi")
- .AddValue("com.app.a:id/foo", {}, util::make_unique<Id>())
- .SetSymbolState("com.app.a:bool/foo", {}, SymbolState::kUndefined, true /*allow_new*/)
- .Build();
-
- Symbol public_symbol;
- public_symbol.state = SymbolState::kPublic;
- ASSERT_TRUE(table->SetSymbolState(test::ParseNameOrDie("com.app.a:layout/main"),
- ResourceId(0x7f020000), public_symbol,
- context->GetDiagnostics()));
-
- Id* id = test::GetValue<Id>(table.get(), "com.app.a:id/foo");
- ASSERT_THAT(id, NotNull());
-
- // Make a plural.
- std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- plural->values[Plural::One] = util::make_unique<String>(table->string_pool.MakeRef("one"));
- ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.app.a:plurals/hey"),
- ConfigDescription{}, {}, std::move(plural),
- context->GetDiagnostics()));
-
- // Make a styled string.
- StyleString style_string;
- style_string.str = "hello";
- style_string.spans.push_back(Span{"b", 0u, 4u});
- ASSERT_TRUE(
- table->AddResource(test::ParseNameOrDie("com.app.a:string/styled"), ConfigDescription{}, {},
- util::make_unique<StyledString>(table->string_pool.MakeRef(style_string)),
- context->GetDiagnostics()));
-
- // Make a resource with different products.
- ASSERT_TRUE(table->AddResource(
- test::ParseNameOrDie("com.app.a:integer/one"),
- test::ParseConfigOrDie("land"), {},
- test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 123u),
- context->GetDiagnostics()));
- ASSERT_TRUE(table->AddResource(
- test::ParseNameOrDie("com.app.a:integer/one"),
- test::ParseConfigOrDie("land"), "tablet",
- test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 321u),
- context->GetDiagnostics()));
-
- // Make a reference with both resource name and resource ID.
- // The reference should point to a resource outside of this table to test that both name and id
- // get serialized.
- Reference expected_ref;
- expected_ref.name = test::ParseNameOrDie("android:layout/main");
- expected_ref.id = ResourceId(0x01020000);
- ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.app.a:layout/abc"),
- ConfigDescription::DefaultConfig(), {},
- util::make_unique<Reference>(expected_ref),
- context->GetDiagnostics()));
-
- std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(table.get());
- ASSERT_THAT(pb_table, NotNull());
-
- std::unique_ptr<ResourceTable> new_table = DeserializeTableFromPb(
- *pb_table, Source{"test"}, context->GetDiagnostics());
- ASSERT_THAT(new_table, NotNull());
-
- Id* new_id = test::GetValue<Id>(new_table.get(), "com.app.a:id/foo");
- ASSERT_THAT(new_id, NotNull());
- EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak()));
-
- Maybe<ResourceTable::SearchResult> result =
- new_table->FindResource(test::ParseNameOrDie("com.app.a:layout/main"));
- ASSERT_TRUE(result);
-
- EXPECT_THAT(result.value().type->symbol_status.state, Eq(SymbolState::kPublic));
- EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic));
-
- result = new_table->FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
- ASSERT_TRUE(result);
- EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kUndefined));
- EXPECT_TRUE(result.value().entry->symbol_status.allow_new);
-
- // Find the product-dependent values
- BinaryPrimitive* prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
- new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "");
- ASSERT_THAT(prim, NotNull());
- EXPECT_THAT(prim->value.data, Eq(123u));
-
- prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
- new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "tablet");
- ASSERT_THAT(prim, NotNull());
- EXPECT_THAT(prim->value.data, Eq(321u));
-
- Reference* actual_ref = test::GetValue<Reference>(new_table.get(), "com.app.a:layout/abc");
- ASSERT_THAT(actual_ref, NotNull());
- ASSERT_TRUE(actual_ref->name);
- ASSERT_TRUE(actual_ref->id);
- EXPECT_THAT(*actual_ref, Eq(expected_ref));
-
- StyledString* actual_styled_str =
- test::GetValue<StyledString>(new_table.get(), "com.app.a:string/styled");
- ASSERT_THAT(actual_styled_str, NotNull());
- EXPECT_THAT(actual_styled_str->value->value, Eq("hello"));
- ASSERT_THAT(actual_styled_str->value->spans, SizeIs(1u));
- EXPECT_THAT(*actual_styled_str->value->spans[0].name, Eq("b"));
- EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u));
- EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u));
-}
-
-TEST(TableProtoSerializer, SerializeFileHeader) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-
- ResourceFile f;
- f.config = test::ParseConfigOrDie("hdpi-v9");
- f.name = test::ParseNameOrDie("com.app.a:layout/main");
- f.source.path = "res/layout-hdpi-v9/main.xml";
- f.exported_symbols.push_back(
- SourcedResourceName{test::ParseNameOrDie("id/unchecked"), 23u});
-
- const std::string expected_data1 = "123";
- const std::string expected_data2 = "1234";
-
- std::string output_str;
- {
- std::unique_ptr<pb::internal::CompiledFile> pb_file1 = SerializeCompiledFileToPb(f);
-
- f.name.entry = "__" + f.name.entry + "$0";
- std::unique_ptr<pb::internal::CompiledFile> pb_file2 = SerializeCompiledFileToPb(f);
-
- StringOutputStream out_stream(&output_str);
- CompiledFileOutputStream out_file_stream(&out_stream);
- out_file_stream.WriteLittleEndian32(2);
- out_file_stream.WriteCompiledFile(pb_file1.get());
- out_file_stream.WriteData(expected_data1.data(), expected_data1.size());
- out_file_stream.WriteCompiledFile(pb_file2.get());
- out_file_stream.WriteData(expected_data2.data(), expected_data2.size());
- ASSERT_FALSE(out_file_stream.HadError());
- }
-
- CompiledFileInputStream in_file_stream(output_str.data(), output_str.size());
- uint32_t num_files = 0;
- ASSERT_TRUE(in_file_stream.ReadLittleEndian32(&num_files));
- ASSERT_EQ(2u, num_files);
-
- // Read the first compiled file.
-
- pb::internal::CompiledFile new_pb_file;
- ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_file));
-
- std::unique_ptr<ResourceFile> file = DeserializeCompiledFileFromPb(
- new_pb_file, Source("test"), context->GetDiagnostics());
- ASSERT_THAT(file, NotNull());
-
- uint64_t offset, len;
- ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len));
-
- std::string actual_data(output_str.data() + offset, len);
- EXPECT_EQ(expected_data1, actual_data);
-
- // Expect the data to be aligned.
- EXPECT_EQ(0u, offset & 0x03);
-
- ASSERT_EQ(1u, file->exported_symbols.size());
- EXPECT_EQ(test::ParseNameOrDie("id/unchecked"), file->exported_symbols[0].name);
-
- // Read the second compiled file.
-
- ASSERT_TRUE(in_file_stream.ReadCompiledFile(&new_pb_file));
-
- file = DeserializeCompiledFileFromPb(new_pb_file, Source("test"), context->GetDiagnostics());
- ASSERT_THAT(file, NotNull());
-
- ASSERT_TRUE(in_file_stream.ReadDataMetaData(&offset, &len));
-
- actual_data = std::string(output_str.data() + offset, len);
- EXPECT_EQ(expected_data2, actual_data);
-
- // Expect the data to be aligned.
- EXPECT_EQ(0u, offset & 0x03);
-}
-
-TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) {
- ResourceFile f;
- std::unique_ptr<pb::internal::CompiledFile> pb_file = SerializeCompiledFileToPb(f);
-
- const std::string expected_data = "1234";
-
- std::string output_str;
- {
- StringOutputStream out_stream(&output_str);
- CompiledFileOutputStream out_file_stream(&out_stream);
- out_file_stream.WriteLittleEndian32(1);
- out_file_stream.WriteCompiledFile(pb_file.get());
- out_file_stream.WriteData(expected_data.data(), expected_data.size());
- ASSERT_FALSE(out_file_stream.HadError());
- }
-
- output_str[4] = 0xff;
-
- CompiledFileInputStream in_file_stream(output_str.data(), output_str.size());
-
- uint32_t num_files = 0;
- EXPECT_TRUE(in_file_stream.ReadLittleEndian32(&num_files));
- EXPECT_EQ(1u, num_files);
-
- pb::internal::CompiledFile new_pb_file;
- EXPECT_FALSE(in_file_stream.ReadCompiledFile(&new_pb_file));
-
- uint64_t offset, len;
- EXPECT_FALSE(in_file_stream.ReadDataMetaData(&offset, &len));
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 32ec7bc..b3e0a92 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -461,6 +461,12 @@
visitor->AfterVisitElement(this);
}
+void Element::Accept(ConstVisitor* visitor) const {
+ visitor->BeforeVisitElement(this);
+ visitor->Visit(this);
+ visitor->AfterVisitElement(this);
+}
+
std::unique_ptr<Node> Text::Clone(const ElementCloneFunc&) const {
auto t = util::make_unique<Text>();
t->comment = comment;
@@ -474,6 +480,10 @@
visitor->Visit(this);
}
+void Text::Accept(ConstVisitor* visitor) const {
+ visitor->Visit(this);
+}
+
void PackageAwareVisitor::BeforeVisitElement(Element* el) {
std::vector<PackageDecl> decls;
for (const NamespaceDecl& decl : el->namespace_decls) {
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index 9a9151d..063d7b9 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -35,6 +35,7 @@
class Element;
class Visitor;
+class ConstVisitor;
// Base class for all XML nodes.
class Node {
@@ -47,6 +48,7 @@
std::string comment;
virtual void Accept(Visitor* visitor) = 0;
+ virtual void Accept(ConstVisitor* visitor) const = 0;
using ElementCloneFunc = std::function<void(const Element&, Element*)>;
@@ -112,6 +114,7 @@
std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) const override;
void Accept(Visitor* visitor) override;
+ void Accept(ConstVisitor* visitor) const override;
};
// A Text (CDATA) XML node. Can not have any children.
@@ -122,6 +125,7 @@
std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) const override;
void Accept(Visitor* visitor) override;
+ void Accept(ConstVisitor* visitor) const override;
};
// An XML resource with a source, name, and XML tree.
@@ -180,6 +184,38 @@
friend class Element;
};
+class ConstVisitor {
+ public:
+ virtual ~ConstVisitor() = default;
+
+ virtual void Visit(const Element* el) {
+ VisitChildren(el);
+ }
+
+ virtual void Visit(const Text* text) {
+ }
+
+ protected:
+ ConstVisitor() = default;
+
+ void VisitChildren(const Element* el) {
+ for (const auto& child : el->children) {
+ child->Accept(this);
+ }
+ }
+
+ virtual void BeforeVisitElement(const Element* el) {
+ }
+
+ virtual void AfterVisitElement(const Element* el) {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ConstVisitor);
+
+ friend class Element;
+};
+
// An XML DOM visitor that will record the package name for a namespace prefix.
class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
public:
@@ -207,19 +243,19 @@
namespace internal {
// Base class that overrides the default behaviour and does not descend into child nodes.
-class NodeCastBase : public Visitor {
+class NodeCastBase : public ConstVisitor {
public:
- void Visit(Element* el) override {
+ void Visit(const Element* el) override {
}
- void Visit(Text* el) override {
+ void Visit(const Text* el) override {
}
protected:
NodeCastBase() = default;
- void BeforeVisitElement(Element* el) override {
+ void BeforeVisitElement(const Element* el) override {
}
- void AfterVisitElement(Element* el) override {
+ void AfterVisitElement(const Element* el) override {
}
private:
@@ -233,9 +269,9 @@
NodeCastImpl() = default;
- T* value = nullptr;
+ const T* value = nullptr;
- void Visit(T* v) override {
+ void Visit(const T* v) override {
value = v;
}
@@ -246,12 +282,17 @@
} // namespace internal
template <typename T>
-T* NodeCast(Node* node) {
+const T* NodeCast(const Node* node) {
internal::NodeCastImpl<T> visitor;
node->Accept(&visitor);
return visitor.value;
}
+template <typename T>
+T* NodeCast(Node* node) {
+ return const_cast<T*>(NodeCast<T>(static_cast<const T*>(node)));
+}
+
} // namespace xml
} // namespace aapt
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index 10a4587..4ba0443 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -18,6 +18,7 @@
#include <string>
+#include "format/binary/XmlFlattener.h"
#include "io/StringInputStream.h"
#include "test/Test.h"
@@ -51,6 +52,36 @@
EXPECT_THAT(el->namespace_decls[0].prefix, StrEq("android"));
}
+TEST(XmlDomTest, BinaryInflate) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<XmlResource> doc = util::make_unique<XmlResource>();
+ doc->root = util::make_unique<Element>();
+ doc->root->name = "Layout";
+ doc->root->line_number = 2u;
+
+ NamespaceDecl decl;
+ decl.uri = kSchemaAndroid;
+ decl.prefix = "android";
+ decl.line_number = 2u;
+ doc->root->namespace_decls.push_back(decl);
+
+ BigBuffer buffer(4096);
+ XmlFlattener flattener(&buffer, {});
+ ASSERT_TRUE(flattener.Consume(context.get(), doc.get()));
+
+ auto block = util::Copy(buffer);
+ std::unique_ptr<XmlResource> new_doc =
+ Inflate(block.get(), buffer.size(), context->GetDiagnostics(), Source("test.xml"));
+ ASSERT_THAT(new_doc, NotNull());
+
+ EXPECT_THAT(new_doc->root->name, StrEq("Layout"));
+ EXPECT_THAT(new_doc->root->line_number, Eq(2u));
+ ASSERT_THAT(new_doc->root->namespace_decls, SizeIs(1u));
+ EXPECT_THAT(new_doc->root->namespace_decls[0].uri, StrEq(kSchemaAndroid));
+ EXPECT_THAT(new_doc->root->namespace_decls[0].prefix, StrEq("android"));
+ EXPECT_THAT(new_doc->root->namespace_decls[0].line_number, Eq(2u));
+}
+
// Escaping is handled after parsing of the values for resource-specific values.
TEST(XmlDomTest, ForwardEscapes) {
std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"(
diff --git a/tools/incident_report/generic_message.h b/tools/incident_report/generic_message.h
index df3f7b2..7c4ad34 100644
--- a/tools/incident_report/generic_message.h
+++ b/tools/incident_report/generic_message.h
@@ -25,7 +25,7 @@
/**
* Class to represent a protobuf Message, where we don't actually
* know what any of the fields are, just their type codes. In other
- * words, this loslessly stores a parsed protobuf object without
+ * words, this losslessly stores a parsed protobuf object without
* having the .proto file that generated it.
*/
class GenericMessage
diff --git a/tools/incident_report/main.cpp b/tools/incident_report/main.cpp
index 250d118..d4ad340 100644
--- a/tools/incident_report/main.cpp
+++ b/tools/incident_report/main.cpp
@@ -138,7 +138,6 @@
static void
print_value(Out* out, FieldDescriptor const* field, GenericMessage::Node const& node)
{
- uint32_t val32;
FieldDescriptor::Type type = field->type();
switch (node.type) {
@@ -160,23 +159,25 @@
break;
case GenericMessage::TYPE_VALUE64:
switch (type) {
- case FieldDescriptor::TYPE_FIXED64:
- case FieldDescriptor::TYPE_SFIXED64:
case FieldDescriptor::TYPE_DOUBLE:
out->printf("%f", *(double*)&node.value64);
break;
+ // Int32s here were added with addInt64 from a WIRETYPE_VARINT,
+ // even if the definition is for a 32 bit int.
case FieldDescriptor::TYPE_SINT32:
case FieldDescriptor::TYPE_INT32:
- val32 = (uint32_t)node.value32;
- out->printf("%d", val32);
+ out->printf("%d", node.value64);
break;
case FieldDescriptor::TYPE_INT64:
- case FieldDescriptor::TYPE_UINT32:
- val32 = (uint32_t)node.value32;
- out->printf("%u", val32);
- break;
- case FieldDescriptor::TYPE_UINT64:
case FieldDescriptor::TYPE_SINT64:
+ case FieldDescriptor::TYPE_SFIXED64:
+ out->printf("%lld", node.value64);
+ break;
+ case FieldDescriptor::TYPE_UINT32:
+ case FieldDescriptor::TYPE_UINT64:
+ case FieldDescriptor::TYPE_FIXED64:
+ out->printf("%u", node.value64);
+ break;
case FieldDescriptor::TYPE_BOOL:
if (node.value64) {
out->printf("true");
@@ -185,11 +186,15 @@
}
break;
case FieldDescriptor::TYPE_ENUM:
- out->printf("%s", field->enum_type()->FindValueByNumber((int)node.value64)
+ if (field->enum_type()->FindValueByNumber((int)node.value64) == NULL) {
+ out->printf("%lld", (int) node.value64);
+ } else {
+ out->printf("%s", field->enum_type()->FindValueByNumber((int)node.value64)
->name().c_str());
+ }
break;
default:
- out->printf("(unexpected value64 %ld (0x%x))", node.value64, node.value64);
+ out->printf("(unexpected value64 %lld (0x%x))", node.value64, node.value64);
break;
}
break;
@@ -224,22 +229,13 @@
out->printf("%s=", field->name().c_str());
if (repeated) {
if (it.first != it.second) {
- out->printf("[");
- if (type == FieldDescriptor::TYPE_MESSAGE
- || type == FieldDescriptor::TYPE_STRING
- || type == FieldDescriptor::TYPE_BYTES) {
- out->printf("\n");
- }
+ out->printf("[\n");
out->indent();
for (GenericMessage::const_iterator_pair it = message->find(fieldId);
it.first != it.second; it.first++) {
print_value(out, field, it.first->second);
- if (type == FieldDescriptor::TYPE_MESSAGE
- || type == FieldDescriptor::TYPE_STRING
- || type == FieldDescriptor::TYPE_BYTES) {
- out->printf("\n");
- }
+ out->printf("\n");
}
out->dedent();
@@ -335,7 +331,7 @@
}
id = field->number();
}
-
+
int pfd[2];
if (pipe(pfd) != 0) {
fprintf(stderr, "pipe failed: %s\n", strerror(errno));
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index a145327..6438631 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -790,6 +790,28 @@
/**
* @hide
+ * Returns true if this WiFi config is for an open network.
+ */
+ public boolean isOpenNetwork() {
+ final int cardinality = allowedKeyManagement.cardinality();
+ final boolean hasNoKeyMgmt = cardinality == 0
+ || (cardinality == 1 && allowedKeyManagement.get(KeyMgmt.NONE));
+
+ boolean hasNoWepKeys = true;
+ if (wepKeys != null) {
+ for (int i = 0; i < wepKeys.length; i++) {
+ if (wepKeys[i] != null) {
+ hasNoWepKeys = false;
+ break;
+ }
+ }
+ }
+
+ return hasNoKeyMgmt && hasNoWepKeys;
+ }
+
+ /**
+ * @hide
* Setting this value will force scan results associated with this configuration to
* be included in the bucket of networks that are externally scored.
* If not set, associated scan results will be treated as legacy saved networks and
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 9c8ea88..b08b4b7 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -967,8 +967,7 @@
* <li>allowedGroupCiphers</li>
* </ul>
* @return a list of network configurations in the form of a list
- * of {@link WifiConfiguration} objects. Upon failure to fetch or
- * when Wi-Fi is turned off, it can be null.
+ * of {@link WifiConfiguration} objects.
*/
public List<WifiConfiguration> getConfiguredNetworks() {
try {
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java
index 357f76e..9f73622 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java
@@ -20,7 +20,6 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.NetworkSpecifier;
-import android.net.wifi.RttManager;
import android.util.Log;
import dalvik.system.CloseGuard;
@@ -224,37 +223,6 @@
}
/**
- * Start a ranging operation with the specified peers. The peer IDs are obtained from an
- * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
- * byte[], java.util.List)} or
- * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
- * byte[])} operation - can
- * only range devices which are part of an ongoing discovery session.
- *
- * @param params RTT parameters - each corresponding to a specific peer ID (the array sizes
- * must be identical). The
- * {@link android.net.wifi.RttManager.RttParams#bssid} member must be set to
- * a peer ID - not to a MAC address.
- * @param listener The listener to receive the results of the ranging session.
- * @hide
- * [TODO: b/28847998 - track RTT API & visilibity]
- */
- public void startRanging(RttManager.RttParams[] params, RttManager.RttListener listener) {
- if (mTerminated) {
- Log.w(TAG, "startRanging: called on terminated session");
- return;
- }
-
- WifiAwareManager mgr = mMgr.get();
- if (mgr == null) {
- Log.w(TAG, "startRanging: called post GC on WifiAwareManager");
- return;
- }
-
- mgr.startRanging(mClientId, mSessionId, params, listener);
- }
-
- /**
* Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
* an unencrypted WiFi Aware connection (link) to the specified peer. The
* {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl
index 30dd64d..b646567 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl
@@ -17,7 +17,6 @@
package android.net.wifi.aware;
import android.net.wifi.aware.ConfigRequest;
-import android.net.wifi.RttManager;
/**
* Callback interface that WifiAwareManager implements
@@ -29,8 +28,4 @@
void onConnectSuccess(int clientId);
void onConnectFail(int reason);
void onIdentityChanged(in byte[] mac);
-
- void onRangingSuccess(int rangingId, in RttManager.ParcelableRttResults results);
- void onRangingFailure(int rangingId, int reason, in String description);
- void onRangingAborted(int rangingId);
}
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
index 0f4910f..f33424b 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
@@ -24,7 +24,6 @@
import android.net.wifi.aware.PublishConfig;
import android.net.wifi.aware.SubscribeConfig;
import android.net.wifi.aware.Characteristics;
-import android.net.wifi.RttManager;
/**
* Interface that WifiAwareService implements
@@ -53,5 +52,4 @@
void sendMessage(int clientId, int discoverySessionId, int peerId, in byte[] message,
int messageId, int retryCount);
void terminateSession(int clientId, int discoverySessionId);
- int startRanging(int clientId, int discoverySessionId, in RttManager.ParcelableRttParams parms);
}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index df0d9d2..ed6804d 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -26,7 +26,6 @@
import android.net.ConnectivityManager;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
-import android.net.wifi.RttManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -35,9 +34,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
import libcore.util.HexEncoding;
@@ -45,7 +41,6 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.nio.BufferOverflowException;
-import java.util.Arrays;
import java.util.List;
/**
@@ -172,9 +167,6 @@
private final Object mLock = new Object(); // lock access to the following vars
- @GuardedBy("mLock")
- private SparseArray<RttManager.RttListener> mRangingListeners = new SparseArray<>();
-
/** @hide */
public WifiAwareManager(Context context, IWifiAwareManager service) {
mContext = context;
@@ -401,27 +393,6 @@
}
/** @hide */
- public void startRanging(int clientId, int sessionId, RttManager.RttParams[] params,
- RttManager.RttListener listener) {
- if (VDBG) {
- Log.v(TAG, "startRanging: clientId=" + clientId + ", sessionId=" + sessionId + ", "
- + "params=" + Arrays.toString(params) + ", listener=" + listener);
- }
-
- int rangingKey = 0;
- try {
- rangingKey = mService.startRanging(clientId, sessionId,
- new RttManager.ParcelableRttParams(params));
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- synchronized (mLock) {
- mRangingListeners.put(rangingKey, listener);
- }
- }
-
- /** @hide */
public NetworkSpecifier createNetworkSpecifier(int clientId, int role, int sessionId,
PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) {
if (VDBG) {
@@ -500,29 +471,12 @@
private static final int CALLBACK_CONNECT_SUCCESS = 0;
private static final int CALLBACK_CONNECT_FAIL = 1;
private static final int CALLBACK_IDENTITY_CHANGED = 2;
- private static final int CALLBACK_RANGING_SUCCESS = 3;
- private static final int CALLBACK_RANGING_FAILURE = 4;
- private static final int CALLBACK_RANGING_ABORTED = 5;
private final Handler mHandler;
private final WeakReference<WifiAwareManager> mAwareManager;
private final Binder mBinder;
private final Looper mLooper;
- RttManager.RttListener getAndRemoveRangingListener(int rangingId) {
- WifiAwareManager mgr = mAwareManager.get();
- if (mgr == null) {
- Log.w(TAG, "getAndRemoveRangingListener: called post GC");
- return null;
- }
-
- synchronized (mgr.mLock) {
- RttManager.RttListener listener = mgr.mRangingListeners.get(rangingId);
- mgr.mRangingListeners.delete(rangingId);
- return listener;
- }
- }
-
/**
* Constructs a {@link AttachCallback} using the specified looper.
* All callbacks will delivered on the thread of the specified looper.
@@ -567,37 +521,6 @@
identityChangedListener.onIdentityChanged((byte[]) msg.obj);
}
break;
- case CALLBACK_RANGING_SUCCESS: {
- RttManager.RttListener listener = getAndRemoveRangingListener(msg.arg1);
- if (listener == null) {
- Log.e(TAG, "CALLBACK_RANGING_SUCCESS rangingId=" + msg.arg1
- + ": no listener registered (anymore)");
- } else {
- listener.onSuccess(
- ((RttManager.ParcelableRttResults) msg.obj).mResults);
- }
- break;
- }
- case CALLBACK_RANGING_FAILURE: {
- RttManager.RttListener listener = getAndRemoveRangingListener(msg.arg1);
- if (listener == null) {
- Log.e(TAG, "CALLBACK_RANGING_SUCCESS rangingId=" + msg.arg1
- + ": no listener registered (anymore)");
- } else {
- listener.onFailure(msg.arg2, (String) msg.obj);
- }
- break;
- }
- case CALLBACK_RANGING_ABORTED: {
- RttManager.RttListener listener = getAndRemoveRangingListener(msg.arg1);
- if (listener == null) {
- Log.e(TAG, "CALLBACK_RANGING_SUCCESS rangingId=" + msg.arg1
- + ": no listener registered (anymore)");
- } else {
- listener.onAborted();
- }
- break;
- }
}
}
};
@@ -629,43 +552,6 @@
msg.obj = mac;
mHandler.sendMessage(msg);
}
-
- @Override
- public void onRangingSuccess(int rangingId, RttManager.ParcelableRttResults results) {
- if (VDBG) {
- Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", results=" + results);
- }
-
- Message msg = mHandler.obtainMessage(CALLBACK_RANGING_SUCCESS);
- msg.arg1 = rangingId;
- msg.obj = results;
- mHandler.sendMessage(msg);
- }
-
- @Override
- public void onRangingFailure(int rangingId, int reason, String description) {
- if (VDBG) {
- Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", reason=" + reason
- + ", description=" + description);
- }
-
- Message msg = mHandler.obtainMessage(CALLBACK_RANGING_FAILURE);
- msg.arg1 = rangingId;
- msg.arg2 = reason;
- msg.obj = description;
- mHandler.sendMessage(msg);
-
- }
-
- @Override
- public void onRangingAborted(int rangingId) {
- if (VDBG) Log.v(TAG, "onRangingAborted: rangingId=" + rangingId);
-
- Message msg = mHandler.obtainMessage(CALLBACK_RANGING_ABORTED);
- msg.arg1 = rangingId;
- mHandler.sendMessage(msg);
-
- }
}
private static class WifiAwareDiscoverySessionCallbackProxy extends
diff --git a/wifi/java/android/net/wifi/rtt/IRttCallback.aidl b/wifi/java/android/net/wifi/rtt/IRttCallback.aidl
new file mode 100644
index 0000000..fb1636f
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/IRttCallback.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.rtt;
+
+import android.net.wifi.rtt.RangingResult;
+
+/**
+ * Interface for RTT result callback.
+ *
+ * @hide
+ */
+oneway interface IRttCallback
+{
+ /**
+ * Service to manager callback providing RTT status and results.
+ */
+ void onRangingResults(int status, in List<RangingResult> results);
+}
diff --git a/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl b/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl
new file mode 100644
index 0000000..ad92e04
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.rtt;
+
+import android.net.wifi.rtt.IRttCallback;
+import android.net.wifi.rtt.RangingRequest;
+
+/**
+ * @hide
+ */
+interface IWifiRttManager
+{
+ void startRanging(in IBinder binder, in String callingPackage, in RangingRequest request,
+ in IRttCallback callback);
+}
diff --git a/wifi/java/android/net/wifi/rtt/RangingRequest.aidl b/wifi/java/android/net/wifi/rtt/RangingRequest.aidl
new file mode 100644
index 0000000..8053c94
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/RangingRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.rtt;
+
+parcelable RangingRequest;
diff --git a/wifi/java/android/net/wifi/rtt/RangingRequest.java b/wifi/java/android/net/wifi/rtt/RangingRequest.java
new file mode 100644
index 0000000..997b680
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/RangingRequest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.rtt;
+
+import android.net.wifi.ScanResult;
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringJoiner;
+
+/**
+ * Defines the ranging request to other devices. The ranging request is built using
+ * {@link RangingRequest.Builder}.
+ * A ranging request is executed using
+ * {@link WifiRttManager#startRanging(RangingRequest, RangingResultCallback, Handler)}.
+ * <p>
+ * The ranging request is a batch request - specifying a set of devices (specified using
+ * {@link RangingRequest.Builder#addAp(ScanResult)} and
+ * {@link RangingRequest.Builder#addAps(List)}).
+ *
+ * @hide RTT_API
+ */
+public final class RangingRequest implements Parcelable {
+ private static final int MAX_PEERS = 10;
+
+ /**
+ * Returns the maximum number of peers to range which can be specified in a single {@code
+ * RangingRequest}. The limit applies no matter how the peers are added to the request, e.g.
+ * through {@link RangingRequest.Builder#addAp(ScanResult)} or
+ * {@link RangingRequest.Builder#addAps(List)}.
+ *
+ * @return Maximum number of peers.
+ */
+ public static int getMaxPeers() {
+ return MAX_PEERS;
+ }
+
+ /** @hide */
+ public final List<RttPeer> mRttPeers;
+
+ /** @hide */
+ private RangingRequest(List<RttPeer> rttPeers) {
+ mRttPeers = rttPeers;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeList(mRttPeers);
+ }
+
+ public static final Creator<RangingRequest> CREATOR = new Creator<RangingRequest>() {
+ @Override
+ public RangingRequest[] newArray(int size) {
+ return new RangingRequest[size];
+ }
+
+ @Override
+ public RangingRequest createFromParcel(Parcel in) {
+ return new RangingRequest(in.readArrayList(null));
+ }
+ };
+
+ /** @hide */
+ @Override
+ public String toString() {
+ StringJoiner sj = new StringJoiner(", ", "RangingRequest: mRttPeers=[", ",");
+ for (RttPeer rp : mRttPeers) {
+ sj.add(rp.toString());
+ }
+ return sj.toString();
+ }
+
+ /** @hide */
+ public void enforceValidity() {
+ if (mRttPeers.size() > MAX_PEERS) {
+ throw new IllegalArgumentException(
+ "Ranging to too many peers requested. Use getMaxPeers() API to get limit.");
+ }
+ }
+
+ /**
+ * Builder class used to construct {@link RangingRequest} objects.
+ */
+ public static final class Builder {
+ private List<RttPeer> mRttPeers = new ArrayList<>();
+
+ /**
+ * Add the device specified by the {@link ScanResult} to the list of devices with
+ * which to measure range. The total number of results added to a request cannot exceed the
+ * limit specified by {@link #getMaxPeers()}.
+ *
+ * @param apInfo Information of an Access Point (AP) obtained in a Scan Result.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder addAp(ScanResult apInfo) {
+ if (apInfo == null) {
+ throw new IllegalArgumentException("Null ScanResult!");
+ }
+ mRttPeers.add(new RttPeerAp(apInfo));
+ return this;
+ }
+
+ /**
+ * Add the devices specified by the {@link ScanResult}s to the list of devices with
+ * which to measure range. The total number of results added to a request cannot exceed the
+ * limit specified by {@link #getMaxPeers()}.
+ *
+ * @param apInfos Information of an Access Points (APs) obtained in a Scan Result.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder addAps(List<ScanResult> apInfos) {
+ if (apInfos == null) {
+ throw new IllegalArgumentException("Null list of ScanResults!");
+ }
+ for (ScanResult scanResult : apInfos) {
+ addAp(scanResult);
+ }
+ return this;
+ }
+
+ /**
+ * Build {@link RangingRequest} given the current configurations made on the
+ * builder.
+ */
+ public RangingRequest build() {
+ return new RangingRequest(mRttPeers);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof RangingRequest)) {
+ return false;
+ }
+
+ RangingRequest lhs = (RangingRequest) o;
+
+ return mRttPeers.size() == lhs.mRttPeers.size() && mRttPeers.containsAll(lhs.mRttPeers);
+ }
+
+ @Override
+ public int hashCode() {
+ return mRttPeers.hashCode();
+ }
+
+ /** @hide */
+ public interface RttPeer {
+ // empty (marker interface)
+ }
+
+ /** @hide */
+ public static class RttPeerAp implements RttPeer, Parcelable {
+ public final ScanResult scanResult;
+
+ public RttPeerAp(ScanResult scanResult) {
+ this.scanResult = scanResult;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ scanResult.writeToParcel(dest, flags);
+ }
+
+ public static final Creator<RttPeerAp> CREATOR = new Creator<RttPeerAp>() {
+ @Override
+ public RttPeerAp[] newArray(int size) {
+ return new RttPeerAp[size];
+ }
+
+ @Override
+ public RttPeerAp createFromParcel(Parcel in) {
+ return new RttPeerAp(ScanResult.CREATOR.createFromParcel(in));
+ }
+ };
+
+ @Override
+ public String toString() {
+ return new StringBuilder("RttPeerAp: scanResult=").append(
+ scanResult.toString()).toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof RttPeerAp)) {
+ return false;
+ }
+
+ RttPeerAp lhs = (RttPeerAp) o;
+
+ // Note: the only thing which matters for the request identity is the BSSID of the AP
+ return TextUtils.equals(scanResult.BSSID, lhs.scanResult.BSSID);
+ }
+
+ @Override
+ public int hashCode() {
+ return scanResult.hashCode();
+ }
+ }
+}
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.aidl b/wifi/java/android/net/wifi/rtt/RangingResult.aidl
new file mode 100644
index 0000000..ae295a6
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.rtt;
+
+parcelable RangingResult;
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.java b/wifi/java/android/net/wifi/rtt/RangingResult.java
new file mode 100644
index 0000000..918803e
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.rtt;
+
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import libcore.util.HexEncoding;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Ranging result for a request started by
+ * {@link WifiRttManager#startRanging(RangingRequest, RangingResultCallback, Handler)}. Results are
+ * returned in {@link RangingResultCallback#onRangingResults(List)}.
+ * <p>
+ * A ranging result is the distance measurement result for a single device specified in the
+ * {@link RangingRequest}.
+ *
+ * @hide RTT_API
+ */
+public final class RangingResult implements Parcelable {
+ private static final String TAG = "RangingResult";
+
+ private final int mStatus;
+ private final byte[] mMac;
+ private final int mDistanceCm;
+ private final int mDistanceStdDevCm;
+ private final int mRssi;
+ private final long mTimestamp;
+
+ /** @hide */
+ public RangingResult(int status, byte[] mac, int distanceCm, int distanceStdDevCm, int rssi,
+ long timestamp) {
+ mStatus = status;
+ mMac = mac;
+ mDistanceCm = distanceCm;
+ mDistanceStdDevCm = distanceStdDevCm;
+ mRssi = rssi;
+ mTimestamp = timestamp;
+ }
+
+ /**
+ * @return The status of ranging measurement: {@link RangingResultCallback#STATUS_SUCCESS} in
+ * case of success, and {@link RangingResultCallback#STATUS_FAIL} in case of failure.
+ */
+ public int getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * @return The MAC address of the device whose range measurement was requested. Will correspond
+ * to the MAC address of the device in the {@link RangingRequest}.
+ * <p>
+ * Always valid (i.e. when {@link #getStatus()} is either SUCCESS or FAIL.
+ */
+ public byte[] getMacAddress() {
+ return mMac;
+ }
+
+ /**
+ * @return The distance (in cm) to the device specified by {@link #getMacAddress()}.
+ * <p>
+ * Only valid if {@link #getStatus()} returns {@link RangingResultCallback#STATUS_SUCCESS}.
+ */
+ public int getDistanceCm() {
+ if (mStatus != RangingResultCallback.STATUS_SUCCESS) {
+ Log.e(TAG, "getDistanceCm(): invalid value retrieved");
+ }
+ return mDistanceCm;
+ }
+
+ /**
+ * @return The standard deviation of the measured distance (in cm) to the device specified by
+ * {@link #getMacAddress()}. The standard deviation is calculated over the measurements
+ * executed in a single RTT burst.
+ * <p>
+ * Only valid if {@link #getStatus()} returns {@link RangingResultCallback#STATUS_SUCCESS}.
+ */
+ public int getDistanceStdDevCm() {
+ if (mStatus != RangingResultCallback.STATUS_SUCCESS) {
+ Log.e(TAG, "getDistanceStdDevCm(): invalid value retrieved");
+ }
+ return mDistanceStdDevCm;
+ }
+
+ /**
+ * @return The average RSSI (in units of -0.5dB) observed during the RTT measurement.
+ * <p>
+ * Only valid if {@link #getStatus()} returns {@link RangingResultCallback#STATUS_SUCCESS}.
+ */
+ public int getRssi() {
+ if (mStatus != RangingResultCallback.STATUS_SUCCESS) {
+ // TODO: should this be an exception?
+ Log.e(TAG, "getRssi(): invalid value retrieved");
+ }
+ return mRssi;
+ }
+
+ /**
+ * @return The timestamp (in us) at which the ranging operation was performed
+ * <p>
+ * Only valid if {@link #getStatus()} returns {@link RangingResultCallback#STATUS_SUCCESS}.
+ */
+ public long getRangingTimestamp() {
+ return mTimestamp;
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mStatus);
+ dest.writeByteArray(mMac);
+ dest.writeInt(mDistanceCm);
+ dest.writeInt(mDistanceStdDevCm);
+ dest.writeInt(mRssi);
+ dest.writeLong(mTimestamp);
+ }
+
+ /** @hide */
+ public static final Creator<RangingResult> CREATOR = new Creator<RangingResult>() {
+ @Override
+ public RangingResult[] newArray(int size) {
+ return new RangingResult[size];
+ }
+
+ @Override
+ public RangingResult createFromParcel(Parcel in) {
+ int status = in.readInt();
+ byte[] mac = in.createByteArray();
+ int distanceCm = in.readInt();
+ int distanceStdDevCm = in.readInt();
+ int rssi = in.readInt();
+ long timestamp = in.readLong();
+ return new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi, timestamp);
+ }
+ };
+
+ /** @hide */
+ @Override
+ public String toString() {
+ return new StringBuilder("RangingResult: [status=").append(mStatus).append(", mac=").append(
+ mMac == null ? "<null>" : HexEncoding.encodeToString(mMac)).append(
+ ", distanceCm=").append(mDistanceCm).append(", distanceStdDevCm=").append(
+ mDistanceStdDevCm).append(", rssi=").append(mRssi).append(", timestamp=").append(
+ mTimestamp).append("]").toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof RangingResult)) {
+ return false;
+ }
+
+ RangingResult lhs = (RangingResult) o;
+
+ return mStatus == lhs.mStatus && Arrays.equals(mMac, lhs.mMac)
+ && mDistanceCm == lhs.mDistanceCm && mDistanceStdDevCm == lhs.mDistanceStdDevCm
+ && mRssi == lhs.mRssi && mTimestamp == lhs.mTimestamp;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mStatus, mMac, mDistanceCm, mDistanceStdDevCm, mRssi, mTimestamp);
+ }
+}
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/rtt/RangingResultCallback.java b/wifi/java/android/net/wifi/rtt/RangingResultCallback.java
new file mode 100644
index 0000000..d7270ad
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/RangingResultCallback.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.rtt;
+
+import android.os.Handler;
+
+import java.util.List;
+
+/**
+ * Base class for ranging result callbacks. Should be extended by applications and set when calling
+ * {@link WifiRttManager#startRanging(RangingRequest, RangingResultCallback, Handler)}. A single
+ * result from a range request will be called in this object.
+ *
+ * @hide RTT_API
+ */
+public abstract class RangingResultCallback {
+ /**
+ * Individual range request status, {@link RangingResult#getStatus()}. Indicates ranging
+ * operation was successful and distance value is valid.
+ */
+ public static final int STATUS_SUCCESS = 0;
+
+ /**
+ * Individual range request status, {@link RangingResult#getStatus()}. Indicates ranging
+ * operation failed and the distance value is invalid.
+ */
+ public static final int STATUS_FAIL = 1;
+
+ /**
+ * Called when a ranging operation failed in whole - i.e. no ranging operation to any of the
+ * devices specified in the request was attempted.
+ */
+ public abstract void onRangingFailure();
+
+ /**
+ * Called when a ranging operation was executed. The list of results corresponds to devices
+ * specified in the ranging request.
+ *
+ * @param results List of range measurements, one per requested device.
+ */
+ public abstract void onRangingResults(List<RangingResult> results);
+}
diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
new file mode 100644
index 0000000..a085de1
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
@@ -0,0 +1,100 @@
+package android.net.wifi.rtt;
+
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_WIFI_STATE;
+import static android.Manifest.permission.CHANGE_WIFI_STATE;
+
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * This class provides the primary API for measuring distance (range) to other devices using the
+ * IEEE 802.11mc Wi-Fi Round Trip Time (RTT) technology.
+ * <p>
+ * The devices which can be ranged include:
+ * <li>Access Points (APs)
+ * <p>
+ * Ranging requests are triggered using
+ * {@link #startRanging(RangingRequest, RangingResultCallback, Handler)}. Results (in case of
+ * successful operation) are returned in the {@link RangingResultCallback#onRangingResults(List)}
+ * callback.
+ *
+ * @hide RTT_API
+ */
+@SystemService(Context.WIFI_RTT2_SERVICE)
+public class WifiRttManager {
+ private static final String TAG = "WifiRttManager";
+ private static final boolean VDBG = true;
+
+ private final Context mContext;
+ private final IWifiRttManager mService;
+
+ /** @hide */
+ public WifiRttManager(Context context, IWifiRttManager service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Initiate a request to range to a set of devices specified in the {@link RangingRequest}.
+ * Results will be returned in the {@link RangingResultCallback} set of callbacks.
+ *
+ * @param request A request specifying a set of devices whose distance measurements are
+ * requested.
+ * @param callback A callback for the result of the ranging request.
+ * @param handler The Handler on whose thread to execute the callbacks of the {@code
+ * callback} object. If a null is provided then the application's main thread
+ * will be used.
+ */
+ @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, CHANGE_WIFI_STATE, ACCESS_WIFI_STATE})
+ public void startRanging(RangingRequest request, RangingResultCallback callback,
+ @Nullable Handler handler) {
+ if (VDBG) {
+ Log.v(TAG, "startRanging: request=" + request + ", callback=" + callback + ", handler="
+ + handler);
+ }
+
+ Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
+ Binder binder = new Binder();
+ try {
+ mService.startRanging(binder, mContext.getOpPackageName(), request,
+ new RttCallbackProxy(looper, callback));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private static class RttCallbackProxy extends IRttCallback.Stub {
+ private final Handler mHandler;
+ private final RangingResultCallback mCallback;
+
+ RttCallbackProxy(Looper looper, RangingResultCallback callback) {
+ mHandler = new Handler(looper);
+ mCallback = callback;
+ }
+
+ @Override
+ public void onRangingResults(int status, List<RangingResult> results) throws RemoteException {
+ if (VDBG) {
+ Log.v(TAG, "RttCallbackProxy: onRanginResults: status=" + status + ", results="
+ + results);
+ }
+ mHandler.post(() -> {
+ if (status == RangingResultCallback.STATUS_SUCCESS) {
+ mCallback.onRangingResults(results);
+ } else {
+ mCallback.onRangingFailure();
+ }
+ });
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/rtt/package.html b/wifi/java/android/net/wifi/rtt/package.html
new file mode 100644
index 0000000..221b94b7
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/package.html
@@ -0,0 +1,39 @@
+<HTML>
+<BODY>
+<p>Provides classes which allow applications to use Wi-Fi RTT (IEEE 802.11mc) to measure distance
+ to supporting Access Points and peer devices.</p>
+<p>The primary entry point to Wi-Fi RTT capabilities is the
+ {@link android.net.wifi.rtt.WifiRttManager} class, which is acquired by calling
+ {@link android.content.Context#getSystemService(String)
+ Context.getSystemService(Context.WIFI_RTT_SERVICE)}</p>
+
+<p>Some APIs may require the following user permissions:</p>
+<ul>
+ <li>{@link android.Manifest.permission#ACCESS_WIFI_STATE}</li>
+ <li>{@link android.Manifest.permission#CHANGE_WIFI_STATE}</li>
+ <li>{@link android.Manifest.permission#ACCESS_COARSE_LOCATION}</li>
+</ul>
+
+<p class="note"><strong>Note:</strong> Not all Android-powered devices support Wi-Fi RTT
+ functionality.
+ If your application only works with Wi-Fi RTT (i.e. it should only be installed on devices which
+ support Wi-Fi RTT), declare so with a <a
+ href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
+ {@code <uses-feature>}</a>
+ element in the manifest file:</p>
+<pre>
+<manifest ...>
+ <uses-feature android:name="android.hardware.wifi.rtt" />
+ ...
+</manifest>
+</pre>
+<p>Alternatively, if your application does not require Wi-Fi RTT but can take advantage of it if
+ available, you can perform
+ the check at run-time in your code using {@link
+ android.content.pm.PackageManager#hasSystemFeature(String)} with {@link
+ android.content.pm.PackageManager#FEATURE_WIFI_RTT}:</p>
+<pre>
+ getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)
+</pre>
+</BODY>
+</HTML>
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 632cfaf..622dce6 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -18,6 +18,8 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
import android.os.Parcel;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
@@ -98,4 +100,73 @@
assertEquals(networkSelectionStatus.isNotRecommended(), copy.isNotRecommended());
}
+
+ @Test
+ public void testIsOpenNetwork_IsOpen_NullWepKeys() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.allowedKeyManagement.clear();
+ config.wepKeys = null;
+
+ assertTrue(config.isOpenNetwork());
+ }
+
+ @Test
+ public void testIsOpenNetwork_IsOpen_ZeroLengthWepKeysArray() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.allowedKeyManagement.clear();
+ config.wepKeys = new String[0];
+
+ assertTrue(config.isOpenNetwork());
+ }
+
+ @Test
+ public void testIsOpenNetwork_IsOpen_NullWepKeysArray() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.allowedKeyManagement.clear();
+ config.wepKeys = new String[1];
+
+ assertTrue(config.isOpenNetwork());
+ }
+
+ @Test
+ public void testIsOpenNetwork_NotOpen_HasWepKeys() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.allowedKeyManagement.clear();
+ config.wepKeys = new String[] {"test"};
+
+ assertFalse(config.isOpenNetwork());
+ }
+
+ @Test
+ public void testIsOpenNetwork_NotOpen_HasNullWepKeyFollowedByNonNullKey() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.allowedKeyManagement.clear();
+ config.wepKeys = new String[] {null, null, "test"};
+
+ assertFalse(config.isOpenNetwork());
+ }
+
+ @Test
+ public void testIsOpenNetwork_NotOpen_HasAuthType() {
+ for (int keyMgmt = 0; keyMgmt < WifiConfiguration.KeyMgmt.strings.length; keyMgmt++) {
+ if (keyMgmt == WifiConfiguration.KeyMgmt.NONE) continue;
+ WifiConfiguration config = new WifiConfiguration();
+ config.allowedKeyManagement.clear();
+ config.allowedKeyManagement.set(keyMgmt);
+ config.wepKeys = null;
+
+ assertFalse("Open network reported when key mgmt was set to "
+ + WifiConfiguration.KeyMgmt.strings[keyMgmt], config.isOpenNetwork());
+ }
+ }
+
+ @Test
+ public void testIsOpenNetwork_NotOpen_HasAuthTypeNoneAndMore() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+ config.wepKeys = null;
+
+ assertFalse(config.isOpenNetwork());
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index d9433c5..1aeeee3 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -845,88 +845,6 @@
}
/*
- * Ranging tests
- */
-
- /**
- * Validate ranging + success flow: (1) connect, (2) create a (publish) session, (3) start
- * ranging, (4) ranging success callback, (5) ranging aborted callback ignored (since
- * listener removed).
- */
- @Test
- public void testRangingCallbacks() throws Exception {
- final int clientId = 4565;
- final int sessionId = 123;
- final int rangingId = 3482;
- final ConfigRequest configRequest = new ConfigRequest.Builder().build();
- final PublishConfig publishConfig = new PublishConfig.Builder().build();
- final RttManager.RttParams rttParams = new RttManager.RttParams();
- rttParams.deviceType = RttManager.RTT_PEER_NAN;
- rttParams.bssid = Integer.toString(1234);
- final RttManager.RttResult rttResults = new RttManager.RttResult();
- rttResults.distance = 10;
-
- when(mockAwareService.startRanging(anyInt(), anyInt(), any())).thenReturn(rangingId);
-
- InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
- mockPublishSession, mockRttListener);
- ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
- WifiAwareSession.class);
- ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
- .forClass(IWifiAwareEventCallback.class);
- ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
- .forClass(IWifiAwareDiscoverySessionCallback.class);
- ArgumentCaptor<PublishDiscoverySession> publishSession = ArgumentCaptor
- .forClass(PublishDiscoverySession.class);
- ArgumentCaptor<RttManager.ParcelableRttParams> rttParamCaptor = ArgumentCaptor
- .forClass(RttManager.ParcelableRttParams.class);
- ArgumentCaptor<RttManager.RttResult[]> rttResultsCaptor = ArgumentCaptor
- .forClass(RttManager.RttResult[].class);
-
- // (1) connect successfully
- mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
- inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
- eq(configRequest), eq(false));
- clientProxyCallback.getValue().onConnectSuccess(clientId);
- mMockLooper.dispatchAll();
- inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
- WifiAwareSession session = sessionCaptor.getValue();
-
- // (2) publish successfully
- session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
- inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
- sessionProxyCallback.capture());
- sessionProxyCallback.getValue().onSessionStarted(sessionId);
- mMockLooper.dispatchAll();
- inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
-
- // (3) start ranging
- publishSession.getValue().startRanging(new RttManager.RttParams[]{rttParams},
- mockRttListener);
- inOrder.verify(mockAwareService).startRanging(eq(clientId), eq(sessionId),
- rttParamCaptor.capture());
- collector.checkThat("RttParams.deviceType", rttParams.deviceType,
- equalTo(rttParamCaptor.getValue().mParams[0].deviceType));
- collector.checkThat("RttParams.bssid", rttParams.bssid,
- equalTo(rttParamCaptor.getValue().mParams[0].bssid));
-
- // (4) ranging success callback
- clientProxyCallback.getValue().onRangingSuccess(rangingId,
- new RttManager.ParcelableRttResults(new RttManager.RttResult[] { rttResults }));
- mMockLooper.dispatchAll();
- inOrder.verify(mockRttListener).onSuccess(rttResultsCaptor.capture());
- collector.checkThat("RttResult.distance", rttResults.distance,
- equalTo(rttResultsCaptor.getValue()[0].distance));
-
- // (5) ranging aborted callback (should be ignored since listener cleared on first callback)
- clientProxyCallback.getValue().onRangingAborted(rangingId);
- mMockLooper.dispatchAll();
-
- verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
- mockPublishSession, mockRttListener);
- }
-
- /*
* Data-path tests
*/
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
new file mode 100644
index 0000000..23c75ce
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.rtt;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.wifi.ScanResult;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import libcore.util.HexEncoding;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit test harness for WifiRttManager class.
+ */
+@SmallTest
+public class WifiRttManagerTest {
+ private WifiRttManager mDut;
+ private TestLooper mMockLooper;
+ private Handler mMockLooperHandler;
+
+ private final String packageName = "some.package.name.for.rtt.app";
+
+ @Mock
+ public Context mockContext;
+
+ @Mock
+ public IWifiRttManager mockRttService;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mDut = new WifiRttManager(mockContext, mockRttService);
+ mMockLooper = new TestLooper();
+ mMockLooperHandler = new Handler(mMockLooper.getLooper());
+
+ when(mockContext.getOpPackageName()).thenReturn(packageName);
+ }
+
+ /**
+ * Validate ranging call flow with succesful results.
+ */
+ @Test
+ public void testRangeSuccess() throws Exception {
+ RangingRequest request = new RangingRequest.Builder().build();
+ List<RangingResult> results = new ArrayList<>();
+ results.add(new RangingResult(RangingResultCallback.STATUS_SUCCESS, null, 15, 5, 10, 666));
+ RangingResultCallback callbackMock = mock(RangingResultCallback.class);
+ ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);
+
+ // verify ranging request passed to service
+ mDut.startRanging(request, callbackMock, mMockLooperHandler);
+ verify(mockRttService).startRanging(any(IBinder.class), eq(packageName), eq(request),
+ callbackCaptor.capture());
+
+ // service calls back with success
+ callbackCaptor.getValue().onRangingResults(RangingResultCallback.STATUS_SUCCESS, results);
+ mMockLooper.dispatchAll();
+ verify(callbackMock).onRangingResults(results);
+
+ verifyNoMoreInteractions(mockRttService, callbackMock);
+ }
+
+ /**
+ * Validate ranging call flow which failed.
+ */
+ @Test
+ public void testRangeFail() throws Exception {
+ RangingRequest request = new RangingRequest.Builder().build();
+ RangingResultCallback callbackMock = mock(RangingResultCallback.class);
+ ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);
+
+ // verify ranging request passed to service
+ mDut.startRanging(request, callbackMock, mMockLooperHandler);
+ verify(mockRttService).startRanging(any(IBinder.class), eq(packageName), eq(request),
+ callbackCaptor.capture());
+
+ // service calls back with failure code
+ callbackCaptor.getValue().onRangingResults(RangingResultCallback.STATUS_FAIL, null);
+ mMockLooper.dispatchAll();
+ verify(callbackMock).onRangingFailure();
+
+ verifyNoMoreInteractions(mockRttService, callbackMock);
+ }
+
+ /**
+ * Validate that RangingRequest parcel works (produces same object on write/read).
+ */
+ @Test
+ public void testRangingRequestParcel() {
+ // Note: not validating parcel code of ScanResult (assumed to work)
+ ScanResult scanResult1 = new ScanResult();
+ scanResult1.BSSID = "00:01:02:03:04:05";
+ ScanResult scanResult2 = new ScanResult();
+ scanResult2.BSSID = "06:07:08:09:0A:0B";
+ ScanResult scanResult3 = new ScanResult();
+ scanResult3.BSSID = "AA:BB:CC:DD:EE:FF";
+ List<ScanResult> scanResults2and3 = new ArrayList<>(2);
+ scanResults2and3.add(scanResult2);
+ scanResults2and3.add(scanResult3);
+
+ RangingRequest.Builder builder = new RangingRequest.Builder();
+ builder.addAp(scanResult1);
+ builder.addAps(scanResults2and3);
+ RangingRequest request = builder.build();
+
+ Parcel parcelW = Parcel.obtain();
+ request.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ RangingRequest rereadRequest = RangingRequest.CREATOR.createFromParcel(parcelR);
+
+ assertEquals(request, rereadRequest);
+ }
+
+ /**
+ * Validate that can request as many range operation as the upper limit on number of requests.
+ */
+ @Test
+ public void testRangingRequestAtLimit() {
+ ScanResult scanResult = new ScanResult();
+ List<ScanResult> scanResultList = new ArrayList<>();
+ for (int i = 0; i < RangingRequest.getMaxPeers() - 2; ++i) {
+ scanResultList.add(scanResult);
+ }
+
+ // create request
+ RangingRequest.Builder builder = new RangingRequest.Builder();
+ builder.addAp(scanResult);
+ builder.addAps(scanResultList);
+ builder.addAp(scanResult);
+ RangingRequest request = builder.build();
+
+ // verify request
+ request.enforceValidity();
+ }
+
+ /**
+ * Validate that limit on number of requests is applied.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testRangingRequestPastLimit() {
+ ScanResult scanResult = new ScanResult();
+ List<ScanResult> scanResultList = new ArrayList<>();
+ for (int i = 0; i < RangingRequest.getMaxPeers() - 1; ++i) {
+ scanResultList.add(scanResult);
+ }
+
+ // create request
+ RangingRequest.Builder builder = new RangingRequest.Builder();
+ builder.addAp(scanResult);
+ builder.addAps(scanResultList);
+ builder.addAp(scanResult);
+ RangingRequest request = builder.build();
+
+ // verify request
+ request.enforceValidity();
+ }
+
+ /**
+ * Validate that RangingResults parcel works (produces same object on write/read).
+ */
+ @Test
+ public void testRangingResultsParcel() {
+ // Note: not validating parcel code of ScanResult (assumed to work)
+ int status = RangingResultCallback.STATUS_SUCCESS;
+ final byte[] mac = HexEncoding.decode("000102030405".toCharArray(), false);
+ int distanceCm = 105;
+ int distanceStdDevCm = 10;
+ int rssi = 5;
+ long timestamp = System.currentTimeMillis();
+
+ RangingResult result = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
+ timestamp);
+
+ Parcel parcelW = Parcel.obtain();
+ result.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ RangingResult rereadResult = RangingResult.CREATOR.createFromParcel(parcelR);
+
+ assertEquals(result, rereadResult);
+ }
+}